user32/tests: Add more A and W test combinations for dde.
[wine] / dlls / shell32 / dialogs.c
1 /*
2  *      common shell dialogs
3  *
4  * Copyright 2000 Juergen Schmied
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <string.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include "winerror.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "commdlg.h"
34 #include "wine/debug.h"
35
36 #include "shellapi.h"
37 #include "shlobj.h"
38 #include "shell32_main.h"
39 #include "shresdef.h"
40 #include "undocshell.h"
41
42 typedef struct
43     {
44         HWND hwndOwner ;
45         HICON hIcon ;
46         LPCWSTR lpstrDirectory ;
47         LPCWSTR lpstrTitle ;
48         LPCWSTR lpstrDescription ;
49         UINT uFlags ;
50     } RUNFILEDLGPARAMS ;
51
52 typedef BOOL (*WINAPI LPFNOFN) (OPENFILENAMEW *) ;
53
54 WINE_DEFAULT_DEBUG_CHANNEL(shell);
55 static INT_PTR CALLBACK RunDlgProc (HWND, UINT, WPARAM, LPARAM) ;
56 static void FillList (HWND, char *, BOOL) ;
57
58
59 /*************************************************************************
60  * PickIconDlg                                  [SHELL32.62]
61  *
62  */
63 BOOL WINAPI PickIconDlg(
64         HWND hwndOwner,
65         LPSTR lpstrFile,
66         DWORD nMaxFile,
67         LPDWORD lpdwIconIndex)
68 {
69         FIXME("(%p,%s,%08x,%p):stub.\n",
70           hwndOwner, lpstrFile, nMaxFile,lpdwIconIndex);
71         return 0xffffffff;
72 }
73
74 /*************************************************************************
75  * RunFileDlgW                                  [internal]
76  *
77  * The Unicode function that is available as ordinal 61 on Windows NT/2000/XP/...
78  *
79  * SEE ALSO
80  *   RunFileDlgAW
81  */
82 void WINAPI RunFileDlgW(
83         HWND hwndOwner,
84         HICON hIcon,
85         LPCWSTR lpstrDirectory,
86         LPCWSTR lpstrTitle,
87         LPCWSTR lpstrDescription,
88         UINT uFlags)
89 {
90     static const WCHAR resnameW[] = {'S','H','E','L','L','_','R','U','N','_','D','L','G',0};
91     RUNFILEDLGPARAMS rfdp;
92     HRSRC hRes;
93     LPVOID template;
94     TRACE("\n");
95
96     rfdp.hwndOwner        = hwndOwner;
97     rfdp.hIcon            = hIcon;
98     rfdp.lpstrDirectory   = lpstrDirectory;
99     rfdp.lpstrTitle       = lpstrTitle;
100     rfdp.lpstrDescription = lpstrDescription;
101     rfdp.uFlags           = uFlags;
102
103     if (!(hRes = FindResourceW(shell32_hInstance, resnameW, (LPWSTR)RT_DIALOG)) ||
104         !(template = LoadResource(shell32_hInstance, hRes)))
105     {
106         ERR("Couldn't load SHELL_RUN_DLG resource\n");
107         ShellMessageBoxW(shell32_hInstance, hwndOwner, MAKEINTRESOURCEW(IDS_RUNDLG_ERROR), NULL, MB_OK | MB_ICONERROR);
108         return;
109     }
110
111     DialogBoxIndirectParamW(shell32_hInstance,
112                             template, hwndOwner, RunDlgProc, (LPARAM)&rfdp);
113
114 }
115
116 /* Dialog procedure for RunFileDlg */
117 static INT_PTR CALLBACK RunDlgProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
118     {
119     RUNFILEDLGPARAMS *prfdp = (RUNFILEDLGPARAMS *)GetWindowLongPtrW(hwnd, DWLP_USER);
120
121     switch (message)
122         {
123         case WM_INITDIALOG :
124             prfdp = (RUNFILEDLGPARAMS *)lParam ;
125             SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)prfdp);
126
127             if (prfdp->lpstrTitle)
128                 SetWindowTextW(hwnd, prfdp->lpstrTitle);
129             if (prfdp->lpstrDescription)
130                 SetWindowTextW(GetDlgItem(hwnd, IDC_RUNDLG_DESCRIPTION), prfdp->lpstrDescription);
131             if (prfdp->uFlags & RFF_NOBROWSE)
132             {
133                 HWND browse = GetDlgItem(hwnd, IDC_RUNDLG_BROWSE);
134                 ShowWindow(browse, SW_HIDE);
135                 EnableWindow(browse, FALSE);
136             }
137             if (prfdp->uFlags & RFF_NOLABEL)
138                 ShowWindow(GetDlgItem(hwnd, IDC_RUNDLG_LABEL), SW_HIDE);
139             if (prfdp->uFlags & RFF_CALCDIRECTORY)
140                 FIXME("RFF_CALCDIRECTORY not supported\n");
141
142             if (prfdp->hIcon == NULL)
143                 prfdp->hIcon = LoadIconW(NULL, (LPCWSTR)IDI_WINLOGO);
144             SendMessageW(hwnd, WM_SETICON, ICON_BIG, (LPARAM)prfdp->hIcon);
145             SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)prfdp->hIcon);
146             SendMessageW(GetDlgItem(hwnd, IDC_RUNDLG_ICON), STM_SETICON, (WPARAM)prfdp->hIcon, 0);
147
148             FillList (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), NULL, (prfdp->uFlags & RFF_NODEFAULT) == 0) ;
149             SetFocus (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH)) ;
150             return TRUE ;
151
152         case WM_COMMAND :
153             switch (LOWORD (wParam))
154                 {
155                 case IDOK :
156                     {
157                     int ic ;
158                     HWND htxt = GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH);
159                     if ((ic = GetWindowTextLengthW (htxt)))
160                         {
161                         WCHAR *psz ;
162                         psz = HeapAlloc( GetProcessHeap(), 0, (ic + 1)*sizeof(WCHAR) );
163                         GetWindowTextW (htxt, psz, ic + 1) ;
164
165                         /* according to http://www.codeproject.com/KB/shell/runfiledlg.aspx we should send a
166                          * WM_NOTIFY before execution */
167
168                         if (ShellExecuteW(hwnd, NULL, psz, NULL, prfdp->lpstrDirectory, SW_SHOWNORMAL) < (HINSTANCE)33)
169                             {
170                             char *pszSysMsg = NULL ;
171                             char szMsg[256];
172                             FormatMessageA (
173                                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
174                                 FORMAT_MESSAGE_FROM_SYSTEM |
175                                 FORMAT_MESSAGE_IGNORE_INSERTS,
176                                 NULL, GetLastError (),
177                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
178                                 (LPSTR)&pszSysMsg, 0, NULL
179                                 ) ;
180                             sprintf (szMsg, "Error: %s", pszSysMsg) ;
181                             LocalFree ((HLOCAL)pszSysMsg) ;
182                             MessageBoxA (hwnd, szMsg, "Nix", MB_OK | MB_ICONEXCLAMATION) ;
183
184                             HeapFree(GetProcessHeap(), 0, psz);
185                             SendMessageA (htxt, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
186                             return TRUE ;
187                             }
188
189                         /* FillList is still ANSI */
190                         GetWindowTextA (htxt, (LPSTR)psz, ic + 1) ;
191                         FillList (htxt, (LPSTR)psz, FALSE) ;
192
193                         HeapFree(GetProcessHeap(), 0, psz);
194                         EndDialog (hwnd, 0) ;
195                         }
196                     }
197
198                 case IDCANCEL :
199                     EndDialog (hwnd, 0) ;
200                     return TRUE ;
201
202                 case IDC_RUNDLG_BROWSE :
203                     {
204                     HMODULE hComdlg = NULL ;
205                     LPFNOFN ofnProc = NULL ;
206                     static const WCHAR comdlg32W[] = {'c','o','m','d','l','g','3','2',0};
207                     WCHAR szFName[1024] = {0};
208                     WCHAR *pszFilter, szCaption[MAX_PATH];
209                     OPENFILENAMEW ofn;
210
211                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_FILTER, (LPWSTR)&pszFilter, 0);
212                     LoadStringW(shell32_hInstance, IDS_RUNDLG_BROWSE_CAPTION, szCaption, MAX_PATH);
213
214                     ZeroMemory(&ofn, sizeof(ofn));
215                     ofn.lStructSize = sizeof(OPENFILENAMEW);
216                     ofn.hwndOwner = hwnd;
217                     ofn.lpstrFilter = pszFilter;
218                     ofn.lpstrFile = szFName;
219                     ofn.nMaxFile = 1023;
220                     ofn.lpstrTitle = szCaption;
221                     ofn.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
222                     ofn.lpstrInitialDir = prfdp->lpstrDirectory;
223
224                     if (NULL == (hComdlg = LoadLibraryExW (comdlg32W, NULL, 0)) ||
225                         NULL == (ofnProc = (LPFNOFN)GetProcAddress (hComdlg, "GetOpenFileNameW")))
226                     {
227                         ERR("Couldn't get GetOpenFileName function entry (lib=%p, proc=%p)\n", hComdlg, ofnProc);
228                         ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_RUNDLG_BROWSE_ERROR), NULL, MB_OK | MB_ICONERROR);
229                         return TRUE ;
230                     }
231
232                     if (ofnProc(&ofn))
233                     {
234                         SetFocus (GetDlgItem (hwnd, IDOK)) ;
235                         SetWindowTextW (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), szFName) ;
236                         SendMessageW (GetDlgItem (hwnd, IDC_RUNDLG_EDITPATH), CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
237                         SetFocus (GetDlgItem (hwnd, IDOK)) ;
238                     }
239
240                     FreeLibrary (hComdlg) ;
241
242                     return TRUE ;
243                     }
244                 }
245             return TRUE ;
246         }
247     return FALSE ;
248     }
249
250 /* This grabs the MRU list from the registry and fills the combo for the "Run" dialog above */
251 /* fShowDefault ignored if pszLatest != NULL */
252 static void FillList (HWND hCb, char *pszLatest, BOOL fShowDefault)
253     {
254     HKEY hkey ;
255 /*    char szDbgMsg[256] = "" ; */
256     char *pszList = NULL, *pszCmd = NULL, cMatch = 0, cMax = 0x60, szIndex[2] = "-" ;
257     DWORD icList = 0, icCmd = 0 ;
258     UINT Nix ;
259
260     SendMessageA (hCb, CB_RESETCONTENT, 0, 0) ;
261
262     if (ERROR_SUCCESS != RegCreateKeyExA (
263         HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU",
264         0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
265         MessageBoxA (hCb, "Unable to open registry key !", "Nix", MB_OK) ;
266
267     RegQueryValueExA (hkey, "MRUList", NULL, NULL, NULL, &icList) ;
268
269     if (icList > 0)
270         {
271         pszList = HeapAlloc( GetProcessHeap(), 0, icList) ;
272         if (ERROR_SUCCESS != RegQueryValueExA (hkey, "MRUList", NULL, NULL, (LPBYTE)pszList, &icList))
273             MessageBoxA (hCb, "Unable to grab MRUList !", "Nix", MB_OK) ;
274         }
275     else
276         {
277         icList = 1 ;
278         pszList = HeapAlloc( GetProcessHeap(), 0, icList) ;
279         pszList[0] = 0 ;
280         }
281
282     for (Nix = 0 ; Nix < icList - 1 ; Nix++)
283         {
284         if (pszList[Nix] > cMax)
285             cMax = pszList[Nix] ;
286
287         szIndex[0] = pszList[Nix] ;
288
289         if (ERROR_SUCCESS != RegQueryValueExA (hkey, szIndex, NULL, NULL, NULL, &icCmd))
290             MessageBoxA (hCb, "Unable to grab size of index", "Nix", MB_OK) ;
291         if( pszCmd )
292             pszCmd = HeapReAlloc(GetProcessHeap(), 0, pszCmd, icCmd) ;
293         else
294             pszCmd = HeapAlloc(GetProcessHeap(), 0, icCmd) ;
295         if (ERROR_SUCCESS != RegQueryValueExA (hkey, szIndex, NULL, NULL, (LPBYTE)pszCmd, &icCmd))
296             MessageBoxA (hCb, "Unable to grab index", "Nix", MB_OK) ;
297
298         if (NULL != pszLatest)
299             {
300             if (!lstrcmpiA(pszCmd, pszLatest))
301                 {
302                 /*
303                 sprintf (szDbgMsg, "Found existing (%d).\n", Nix) ;
304                 MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
305                 */
306                 SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszCmd) ;
307                 SetWindowTextA (hCb, pszCmd) ;
308                 SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
309
310                 cMatch = pszList[Nix] ;
311                 memmove (&pszList[1], pszList, Nix) ;
312                 pszList[0] = cMatch ;
313                 continue ;
314                 }
315             }
316
317         if (26 != icList - 1 || icList - 2 != Nix || cMatch || NULL == pszLatest)
318             {
319             /*
320             sprintf (szDbgMsg, "Happily appending (%d).\n", Nix) ;
321             MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
322             */
323             SendMessageA (hCb, CB_ADDSTRING, 0, (LPARAM)pszCmd) ;
324             if (!Nix && fShowDefault)
325                 {
326                 SetWindowTextA (hCb, pszCmd) ;
327                 SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
328                 }
329
330             }
331         else
332             {
333             /*
334             sprintf (szDbgMsg, "Doing loop thing.\n") ;
335             MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
336             */
337             SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest) ;
338             SetWindowTextA (hCb, pszLatest) ;
339             SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
340
341             cMatch = pszList[Nix] ;
342             memmove (&pszList[1], pszList, Nix) ;
343             pszList[0] = cMatch ;
344             szIndex[0] = cMatch ;
345             RegSetValueExA (hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, strlen (pszLatest) + 1) ;
346             }
347         }
348
349     if (!cMatch && NULL != pszLatest)
350         {
351         /*
352         sprintf (szDbgMsg, "Simply inserting (increasing list).\n") ;
353         MessageBoxA (hCb, szDbgMsg, "Nix", MB_OK) ;
354         */
355         SendMessageA (hCb, CB_INSERTSTRING, 0, (LPARAM)pszLatest) ;
356         SetWindowTextA (hCb, pszLatest) ;
357         SendMessageA (hCb, CB_SETEDITSEL, 0, MAKELPARAM (0, -1)) ;
358
359         cMatch = ++cMax ;
360         if( pszList )
361             pszList = HeapReAlloc(GetProcessHeap(), 0, pszList, ++icList) ;
362         else
363             pszList = HeapAlloc(GetProcessHeap(), 0, ++icList) ;
364         memmove (&pszList[1], pszList, icList - 1) ;
365         pszList[0] = cMatch ;
366         szIndex[0] = cMatch ;
367         RegSetValueExA (hkey, szIndex, 0, REG_SZ, (LPBYTE)pszLatest, strlen (pszLatest) + 1) ;
368         }
369
370     RegSetValueExA (hkey, "MRUList", 0, REG_SZ, (LPBYTE)pszList, strlen (pszList) + 1) ;
371
372     HeapFree( GetProcessHeap(), 0, pszCmd) ;
373     HeapFree( GetProcessHeap(), 0, pszList) ;
374     }
375
376 /*************************************************************************
377  * RunFileDlgA                                  [internal]
378  *
379  * The ANSI function that is available as ordinal 61 on Windows 9x/Me
380  *
381  * SEE ALSO
382  *   RunFileDlgAW
383  */
384 void WINAPI RunFileDlgA(
385         HWND hwndOwner,
386         HICON hIcon,
387         LPCSTR lpstrDirectory,
388         LPCSTR lpstrTitle,
389         LPCSTR lpstrDescription,
390         UINT uFlags)
391 {
392     WCHAR title[MAX_PATH];       /* longer string wouldn't be visible in the dialog anyway */
393     WCHAR description[MAX_PATH];
394     WCHAR directory[MAX_PATH];
395
396     MultiByteToWideChar(CP_ACP, 0, lpstrTitle, -1, title, MAX_PATH);
397     title[MAX_PATH - 1] = 0;
398     MultiByteToWideChar(CP_ACP, 0, lpstrDescription, -1, description, MAX_PATH);
399     description[MAX_PATH - 1] = 0;
400     if (!MultiByteToWideChar(CP_ACP, 0, lpstrDirectory, -1, directory, MAX_PATH))
401         directory[0] = 0;
402
403     RunFileDlgW(hwndOwner, hIcon,
404         lpstrDirectory ? directory : NULL,
405         lpstrTitle ? title : NULL,
406         lpstrDescription ? description : NULL,
407         uFlags);
408 }
409
410 /*************************************************************************
411  * RunFileDlgAW                                 [SHELL32.61]
412  *
413  * An undocumented way to open the Run File dialog. A documented way is to use
414  * CLSID_Shell, IID_IShellDispatch (as of Wine 1.0, not implemented under Wine)
415  *
416  * Exported by ordinal. ANSI on Windows 9x and Unicode on Windows NT/2000/XP/etc
417  *
418  */
419 void WINAPI RunFileDlgAW(
420         HWND hwndOwner,
421         HICON hIcon,
422         LPCVOID lpstrDirectory,
423         LPCVOID lpstrTitle,
424         LPCVOID lpstrDescription,
425         UINT uFlags)
426 {
427     if (SHELL_OsIsUnicode())
428         RunFileDlgW(hwndOwner, hIcon, lpstrDirectory, lpstrTitle, lpstrDescription, uFlags);
429     else
430         RunFileDlgA(hwndOwner, hIcon, lpstrDirectory, lpstrTitle, lpstrDescription, uFlags);
431 }
432
433
434 /*************************************************************************
435  * ConfirmDialog                                [internal]
436  *
437  * Put up a confirm box, return TRUE if the user confirmed
438  */
439 static BOOL ConfirmDialog(HWND hWndOwner, UINT PromptId, UINT TitleId)
440 {
441   WCHAR Prompt[256];
442   WCHAR Title[256];
443
444   LoadStringW(shell32_hInstance, PromptId, Prompt, sizeof(Prompt) / sizeof(WCHAR));
445   LoadStringW(shell32_hInstance, TitleId, Title, sizeof(Title) / sizeof(WCHAR));
446   return MessageBoxW(hWndOwner, Prompt, Title, MB_YESNO|MB_ICONQUESTION) == IDYES;
447 }
448
449
450 /*************************************************************************
451  * RestartDialogEx                              [SHELL32.730]
452  */
453
454 int WINAPI RestartDialogEx(HWND hWndOwner, LPCWSTR lpwstrReason, DWORD uFlags, DWORD uReason)
455 {
456     TRACE("(%p)\n", hWndOwner);
457
458     /* FIXME: use lpwstrReason */
459     if (ConfirmDialog(hWndOwner, IDS_RESTART_PROMPT, IDS_RESTART_TITLE))
460     {
461         HANDLE hToken;
462         TOKEN_PRIVILEGES npr;
463
464         /* enable the shutdown privilege for the current process */
465         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
466         {
467             LookupPrivilegeValueA(0, "SeShutdownPrivilege", &npr.Privileges[0].Luid);
468             npr.PrivilegeCount = 1;
469             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
470             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
471             CloseHandle(hToken);
472         }
473         ExitWindowsEx(EWX_REBOOT, uReason);
474     }
475
476     return 0;
477 }
478
479
480 /*************************************************************************
481  * RestartDialog                                [SHELL32.59]
482  */
483
484 int WINAPI RestartDialog(HWND hWndOwner, LPCWSTR lpstrReason, DWORD uFlags)
485 {
486     return RestartDialogEx(hWndOwner, lpstrReason, uFlags, 0);
487 }
488
489
490 /*************************************************************************
491  * ExitWindowsDialog                            [SHELL32.60]
492  *
493  * NOTES
494  *     exported by ordinal
495  */
496 void WINAPI ExitWindowsDialog (HWND hWndOwner)
497 {
498     TRACE("(%p)\n", hWndOwner);
499
500     if (ConfirmDialog(hWndOwner, IDS_SHUTDOWN_PROMPT, IDS_SHUTDOWN_TITLE))
501     {
502         HANDLE hToken;
503         TOKEN_PRIVILEGES npr;
504
505         /* enable shutdown privilege for current process */
506         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
507         {
508             LookupPrivilegeValueA(0, "SeShutdownPrivilege", &npr.Privileges[0].Luid);
509             npr.PrivilegeCount = 1;
510             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
511             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
512             CloseHandle(hToken);
513         }
514         ExitWindowsEx(EWX_SHUTDOWN, 0);
515     }
516 }