2
2
using System . IO ;
3
3
using System . Runtime . InteropServices ;
4
4
using System . Runtime . InteropServices . ComTypes ;
5
+ using Shell32 ;
5
6
6
7
namespace PasteIntoFile
7
8
{
@@ -50,37 +51,82 @@ private static SHDocVw.InternetExplorer GetActiveExplorer()
50
51
}
51
52
}
52
53
return null ;
53
- }
54
+ }
54
55
56
+ public static event EventHandler FilenameEditComplete ;
55
57
58
+ /// <summary>
59
+ /// Searches the file with given path in the given shell window and selects it if found
60
+ /// </summary>
61
+ /// <param name="window">The shell window</param>
62
+ /// <param name="path">The path of the file to select</param>
63
+ /// <param name="edit">Select in edit mode if true, otherwise just select</param>
64
+ private static void SelectFileInWindow ( SHDocVw . InternetExplorer window , string path , bool edit = true ) {
65
+ if ( ! ( window ? . Document is IShellFolderViewDual view ) ) return ;
66
+ window . DocumentComplete += ( object disp , ref object url ) => {
67
+ foreach ( FolderItem folderItem in view . Folder . Items ( ) ) {
68
+ if ( folderItem . Path == path ) {
69
+ SetForegroundWindow ( ( IntPtr ) window . HWND ) ;
70
+ // https://docs.microsoft.com/en-us/windows/win32/shell/shellfolderview-selectitem
71
+ view . SelectItem ( folderItem , 16 /* focus it, */ + 8 /* ensure it's visible, */
72
+ + 4 /* deselect all other and */
73
+ + ( edit ? 3 : 1 ) /* select or edit */ ) ;
74
+ break ;
75
+ }
76
+ }
77
+ FilenameEditComplete ? . Invoke ( null , EventArgs . Empty ) ;
78
+ } ;
79
+ window . Refresh ( ) ;
80
+ }
56
81
57
82
/// <summary>
58
83
/// Request file name edit by user in active explorer path
59
84
/// </summary>
60
85
/// <param name="filePath">Path of file to select/edit</param>
61
86
/// <param name="edit">can be set to false to select only (without entering edit mode)</param>
62
- public static void RequestFilenameEdit ( string filePath , bool edit = true )
63
- {
87
+ public static void AsyncRequestFilenameEdit ( string filePath , bool edit = true ) {
64
88
filePath = Path . GetFullPath ( filePath ) ;
65
89
var dirPath = Path . GetDirectoryName ( filePath ) ;
66
-
67
- // code below adopted from https://stackoverflow.com/a/8682999/13324744
68
- IntPtr folder = PathToPidl ( dirPath ) ;
69
- IntPtr file = PathToPidl ( filePath ) ;
70
- try
71
- {
72
- SHOpenFolderAndSelectItems ( folder , 1 , new [ ] { file } , edit ? 1 : 0 ) ;
90
+
91
+ // check current shell window first
92
+ var focussedWindow = GetActiveExplorer ( ) ;
93
+ if ( GetExplorerPath ( focussedWindow ) == dirPath ) {
94
+ SelectFileInWindow ( focussedWindow , filePath , edit ) ;
95
+ return ;
73
96
}
74
- finally
75
- {
76
- ILFree ( folder ) ;
97
+
98
+ // then check other open shell windows
99
+ var shellWindows = new SHDocVw . ShellWindows ( ) ;
100
+ foreach ( SHDocVw . InternetExplorer window in shellWindows ) {
101
+ if ( GetExplorerPath ( window ) == dirPath ) {
102
+ SelectFileInWindow ( window , filePath , edit ) ;
103
+ return ;
104
+ }
105
+ }
106
+
107
+ // or open a new shell window
108
+ IntPtr file ;
109
+ SHParseDisplayName ( filePath , IntPtr . Zero , out file , 0 , out _ ) ;
110
+ try {
111
+ SHOpenFolderAndSelectItems ( file , 0 , null , edit ? 1 : 0 ) ;
112
+ FilenameEditComplete ? . Invoke ( null , EventArgs . Empty ) ;
113
+ } finally {
77
114
ILFree ( file ) ;
78
115
}
79
116
}
117
+
80
118
81
- private static IntPtr PathToPidl ( string path )
119
+ private static IntPtr PathToPidl ( string path , bool special = false )
82
120
{
83
- // documentation: https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellfolder-parsedisplayname
121
+ if ( special ) {
122
+ // Will use special paths, e.g. "::{374DE290-123F-4565-9164-39C4925E467B}" for Downloads
123
+ IntPtr fileSpecial ;
124
+ SHParseDisplayName ( path , IntPtr . Zero , out fileSpecial , 0 , out _ ) ;
125
+ return fileSpecial ;
126
+ }
127
+
128
+ // Will use full paths, e.g. "C:\Users\User\Downloads" for Downloads
129
+ // https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellfolder-parsedisplayname
84
130
SHGetDesktopFolder ( out IShellFolder desktopFolder ) ;
85
131
desktopFolder . ParseDisplayName ( IntPtr . Zero , null , path , out var pchEaten , out var ppidl , 0 ) ;
86
132
return ppidl ;
@@ -90,6 +136,13 @@ private static IntPtr PathToPidl(string path)
90
136
[ DllImport ( "user32.dll" ) ]
91
137
private static extern IntPtr GetForegroundWindow ( ) ;
92
138
139
+ [ DllImport ( "user32.dll" ) ]
140
+ static extern bool SetForegroundWindow ( IntPtr hWnd ) ;
141
+
142
+ [ DllImport ( "shell32.dll" , SetLastError = true ) ]
143
+ public static extern void SHParseDisplayName ( [ MarshalAs ( UnmanagedType . LPWStr ) ] string name , IntPtr bindingContext ,
144
+ [ Out ] out IntPtr pidl , uint sfgaoIn , [ Out ] out uint psfgaoOut ) ;
145
+
93
146
[ DllImport ( "shell32.dll" ) ]
94
147
private static extern int SHOpenFolderAndSelectItems ( IntPtr pidlFolder , uint cidl , IntPtr [ ] apidl , int dwFlags ) ;
95
148
0 commit comments