1 /*
2 * w32oo: collection of Win32-specific functions that require a C++ (OO)
3 * compilation.
4 *
5 * Caveats
6 * =======
7 * - Due to a conflict between estruct.h and MS names, the Windows macros
8 * "FAILED" may not be used to test an OLE return code. Use SUCCEEDED
9 * instead.
10 *
11 * $Id: w32oo.cpp,v 1.21 2018/11/05 01:29:09 tom Exp $
12 */
13
14 #include "w32vile.h"
15
16 #include <ole2.h>
17 #include <shlobj.h>
18 #include <stdio.h>
19
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23
24 #include "estruct.h"
25 #include "edef.h"
26 #include "dirstuff.h"
27
28 #if CC_TURBO
29 #include <dir.h> /* for 'chdir()' */
30 #endif
31
32 #ifdef UNICODE
33 static int
vl_GetPathFromIDList(LPITEMIDLIST lp,char * bufferp)34 vl_GetPathFromIDList(LPITEMIDLIST lp, char *bufferp)
35 {
36 int rc = 0;
37 W32_CHAR buffer[FILENAME_MAX];
38 char *result;
39
40 rc = SHGetPathFromIDList(lp, reinterpret_cast<LPTSTR>(buffer));
41 if (rc) {
42 if ((result = asc_charstring(buffer)) != 0) {
43 strcpy(bufferp, result);
44 free (result);
45 }
46 }
47 return rc;
48 }
49 #else
50 #define vl_GetPathFromIDList(listp, bufferp) SHGetPathFromIDList(listp, bufferp)
51 #endif
52
53 /* ---------------------------- Favorites --------------------------------- */
54
55 /*
56 * Return path to favorites folder. don't know of any other way to do this
57 * than via COM ... a bit over the top, doncha' think?
58 */
59 const char *
get_favorites(void)60 get_favorites(void)
61 {
62 char dir[FILENAME_MAX];
63 static char *path;
64 LPITEMIDLIST pidl;
65 HRESULT hr;
66
67 if (! path)
68 {
69 #ifndef VILE_OLE
70 hr = OleInitialize(NULL);
71 if (! SUCCEEDED(hr))
72 {
73 disp_win32_error(hr, NULL);
74 return (NULL);
75 }
76 #endif
77 pidl = NULL;
78 hr = SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
79 if (! SUCCEEDED(hr))
80 {
81 disp_win32_error(hr, NULL);
82 CoTaskMemFree(pidl); // API accepts NULL ptr
83 #ifndef VILE_OLE
84 OleUninitialize();
85 #endif
86 return (NULL);
87 }
88
89 if (! vl_GetPathFromIDList(pidl, dir)) {
90 disp_win32_error(W32_SYS_ERROR, NULL);
91 } else {
92 bsl_to_sl_inplace(dir); // Convert to canonical form
93 path = strmalloc(dir);
94 if (! path)
95 no_memory("get_favorites()");
96 }
97 CoTaskMemFree(pidl);
98 #ifndef VILE_OLE
99 OleUninitialize();
100 #endif
101 }
102 return (path);
103 }
104
105 /* --------------------------- Graphical CD ------------------------------- */
106
107 static const char *initial_browse_dir;
108
109 /*
110 * Setting the initial browse folder for SHBrowseForFolder() is, *cough*,
111 * insane. Oh by the way, if a pidl isn't used to set the initial browse
112 * folder, then initial paths like "\\some_unc_path" don't work. I kid
113 * you not.
114 *
115 * Much of this callback's code is cribbed from the 'Net.
116 */
117 static int CALLBACK
BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp,LPARAM pData)118 BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
119 {
120 HRESULT hr;
121 ULONG len;
122 LPITEMIDLIST pidl;
123 LPSHELLFOLDER pShellFolder;
124 W32_CHAR szDir[MAX_PATH];
125 LPWSTR wide_path;
126
127 switch(uMsg)
128 {
129 case BFFM_INITIALIZED:
130 // Set/initial the browse folder path. This code is a hoot.
131 len = MultiByteToWideChar(CP_ACP,
132 MB_USEGLYPHCHARS|MB_PRECOMPOSED,
133 initial_browse_dir,
134 -1,
135 0,
136 0);
137 if (len == 0)
138 {
139 disp_win32_error(GetLastError(), hwnd);
140 break;
141 }
142 wide_path = new WCHAR[len];
143 MultiByteToWideChar(CP_ACP,
144 MB_USEGLYPHCHARS|MB_PRECOMPOSED,
145 initial_browse_dir,
146 -1,
147 wide_path,
148 len);
149 pShellFolder = 0;
150 hr = SHGetDesktopFolder(&pShellFolder);
151 if (! SUCCEEDED(hr))
152 {
153 delete [] wide_path;
154 disp_win32_error(hr, hwnd);
155 break;
156 }
157 hr = pShellFolder->ParseDisplayName(0,
158 0,
159 wide_path,
160 NULL,
161 &pidl,
162 0);
163 if (! SUCCEEDED(hr)) {
164 disp_win32_error(hr, hwnd);
165 } else {
166 SendMessage(hwnd, BFFM_SETSELECTION, FALSE, (LPARAM) pidl);
167 CoTaskMemFree(pidl);
168 }
169 delete [] wide_path;
170 pShellFolder->Release();
171 break;
172 case BFFM_SELCHANGED:
173 // Set the status window text to the currently selected path.
174 if (SHGetPathFromIDList((LPITEMIDLIST) lp, reinterpret_cast<LPTSTR>(szDir)))
175 SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM) szDir);
176 break;
177 default:
178 break;
179 }
180 return (0);
181 }
182
183 static int
graphical_cd(const char * dir)184 graphical_cd(const char *dir)
185 {
186 BROWSEINFO bi;
187 HWND hwnd;
188 LPITEMIDLIST pidl;
189 int rc = FALSE;
190 char bslbuf[FILENAME_MAX + 1], selected_folder[FILENAME_MAX + 1];
191
192 TRACE((T_CALLED "graphical_cd(%s)\n", dir));
193
194 // win32 functions don't like '/' as path delimiter.
195 if (! w32_glob_and_validate_dir(dir, bslbuf))
196 returnCode(rc);
197
198 // pShellFolder->ParseDisplayName() will not accept local system folder
199 // names that don't include a drive letter. Yes, this is fatal brain
200 // damage, but it's not like there are other supported, non-deprecated
201 // alternatives. Fortunately, lengthen_path() rides to the rescue.
202 // Unfortunately, lengthen_path() reverts to '/' as a path delimiter.
203 // Oh, what tangled webs we weave.
204 initial_browse_dir = sl_to_bsl(lengthen_path(bslbuf));
205 TRACE(("initial_browse_dir=%s\n", initial_browse_dir));
206
207 #if DISP_NTWIN
208 hwnd = (HWND) winvile_hwnd();
209 #else
210 hwnd = GetVileWindow();
211 #endif
212
213 #ifndef VILE_OLE
214 HRESULT hr = OleInitialize(NULL);
215 if (! SUCCEEDED(hr))
216 {
217 disp_win32_error(hr, hwnd);
218 returnCode(rc);
219 }
220 #endif
221 memset(&bi, 0, sizeof(bi));
222 bi.hwndOwner = hwnd;
223 bi.ulFlags = BIF_STATUSTEXT;
224 bi.lpfn = BrowseCallbackProc;
225 if ((pidl = SHBrowseForFolder(&bi)) != NULL)
226 {
227 if (! vl_GetPathFromIDList(pidl, selected_folder))
228 disp_win32_error(NOERROR, hwnd);
229 else
230 rc = set_directory(selected_folder);
231 CoTaskMemFree(pidl);
232 }
233 // else -- user cancel'd out of dialog box
234
235 #ifndef VILE_OLE
236 OleUninitialize();
237 #endif
238 returnCode(rc);
239 }
240
241 int
wincd(int f,int n)242 wincd(int f, int n)
243 {
244 char buf[NFILEN];
245 static TBUFF *last;
246 int rc;
247
248 (void) f;
249 (void) n;
250
251 TRACE((T_CALLED "wincd(%d,%d)\n", f, n));
252 buf[0] = '\0';
253 rc = mlreply_dir("Initial Directory: ", &last, buf);
254 if (rc == TRUE)
255 {
256 mktrimmed(buf);
257 rc = graphical_cd(buf);
258 }
259 else if (rc == FALSE)
260 {
261 /* empty response */
262
263 if (getcwd(buf, sizeof(buf)) == NULL) {
264 mlforce("[getcwd: %s]", strerror(errno));
265 return FALSE;
266 }
267 rc = graphical_cd(buf);
268 }
269 /* else rc == ABORT or SORTOFTRUE */
270 returnCode(rc);
271 }
272
273 /* explicitly specify initial directory */
274 int
wincd_dir(const char * dir)275 wincd_dir(const char *dir)
276 {
277 char buf[NFILEN];
278
279 TRACE((T_CALLED "wincd_dir(%s)\n", dir));
280 if (dir == NULL)
281 {
282 if ((dir = getcwd(buf, sizeof(buf))) == NULL) {
283 mlforce("[getcwd: %s]", strerror(errno));
284 return FALSE;
285 }
286 }
287 returnCode(graphical_cd(dir));
288 }
289
290 #if CC_MINGW
291 #define USE_EXCEPTION_FILTERING 0
292 #elif defined(EXCEPTION_INVALID_HANDLE)
293 #define USE_EXCEPTION_FILTERING 1
294 #elif defined(STATUS_INVALID_HANDLE)
295 #define EXCEPTION_INVALID_HANDLE STATUS_INVALID_HANDLE
296 #define USE_EXCEPTION_FILTERING 1
297 #else
298 #define USE_EXCEPTION_FILTERING 0
299 #endif
300
301 #if USE_EXCEPTION_FILTERING
302 static int
filterExceptions(unsigned int code,struct _EXCEPTION_POINTERS * ep)303 filterExceptions(unsigned int code, struct _EXCEPTION_POINTERS *ep)
304 {
305 if (code == EXCEPTION_INVALID_HANDLE)
306 {
307 return EXCEPTION_EXECUTE_HANDLER;
308 }
309 else
310 {
311 return EXCEPTION_CONTINUE_SEARCH;
312 }
313 }
314 #endif
315
316 // filter out STATUS_INVALID_HANDLE aka EXCEPTION_INVALID_HANDLE
317 void
w32_close_handle(HANDLE handle)318 w32_close_handle(HANDLE handle)
319 {
320 #if USE_EXCEPTION_FILTERING
321 __try
322 {
323 (void) CloseHandle(handle);
324 }
325 __except(filterExceptions(GetExceptionCode(), GetExceptionInformation()))
326 {
327 TRACE(("error closing handle %#x\n", handle));
328 }
329 #else
330 (void) CloseHandle(handle);
331 #endif
332 }
333
334 #ifdef __cplusplus
335 } /* Extern "C" */
336 #endif
337