xref: /reactos/dll/cpl/appwiz/createlink.c (revision 4ba56962)
1 /*
2  * PROJECT:         ReactOS Software Control Panel
3  * FILE:            dll/cpl/appwiz/createlink.c
4  * PURPOSE:         ReactOS Software Control Panel
5  * PROGRAMMER:      Gero Kuehn (reactos.filter@gkware.com)
6  *                  Dmitry Chapyshev (lentind@yandex.ru)
7  *                  Johannes Anderwald
8  *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
9  * UPDATE HISTORY:
10  *      06-17-2004  Created
11  */
12 
13 #include "appwiz.h"
14 #include <commctrl.h>
15 #include <shellapi.h>
16 #include <strsafe.h>
17 #include <shlwapi_undoc.h> // for PathFindOnPathExW
18 #include "../../win32/shell32/shresdef.h" // for IDS_NEWITEMFORMAT and IDS_LNK_FILE
19 
20 BOOL
21 IsShortcut(HKEY hKey)
22 {
23     WCHAR Value[10];
24     DWORD Size;
25     DWORD Type;
26 
27     Size = sizeof(Value);
28     if (RegQueryValueExW(hKey, L"IsShortcut", NULL, &Type, (LPBYTE)Value, &Size) != ERROR_SUCCESS)
29         return FALSE;
30 
31     if (Type != REG_SZ)
32         return FALSE;
33 
34     return (wcsicmp(Value, L"yes") == 0);
35 }
36 
37 BOOL
38 IsExtensionAShortcut(LPWSTR lpExtension)
39 {
40     HKEY hKey;
41     WCHAR Buffer[100];
42     DWORD Size;
43     DWORD Type;
44 
45     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, lpExtension, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
46         return FALSE;
47 
48     if (IsShortcut(hKey))
49     {
50         RegCloseKey(hKey);
51         return TRUE;
52     }
53 
54     Size = sizeof(Buffer);
55     if (RegQueryValueEx(hKey, NULL, NULL, &Type, (LPBYTE)Buffer, &Size) != ERROR_SUCCESS || Type != REG_SZ)
56     {
57         RegCloseKey(hKey);
58         return FALSE;
59     }
60 
61     RegCloseKey(hKey);
62 
63     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, Buffer, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
64         return FALSE;
65 
66     if (IsShortcut(hKey))
67     {
68         RegCloseKey(hKey);
69         return TRUE;
70     }
71 
72     RegCloseKey(hKey);
73     return FALSE;
74 }
75 
76 BOOL
77 CreateShortcut(PCREATE_LINK_CONTEXT pContext)
78 {
79     IShellLinkW *pShellLink;
80     IPersistFile *pPersistFile;
81     HRESULT hr;
82 
83     hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_ALL, &IID_IShellLinkW, (void**)&pShellLink);
84     if (FAILED(hr))
85         return FALSE;
86 
87     hr = IUnknown_QueryInterface(pShellLink, &IID_IPersistFile, (void**)&pPersistFile);
88     if (FAILED(hr))
89     {
90         IUnknown_Release(pShellLink);
91         return FALSE;
92     }
93 
94     if (IsExtensionAShortcut(PathFindExtensionW(pContext->szTarget)))
95     {
96         hr = pPersistFile->lpVtbl->Load(pPersistFile, (LPCOLESTR)pContext->szTarget, STGM_READ);
97     }
98     else
99     {
100         if (pContext->pidlTarget)
101             pShellLink->lpVtbl->SetIDList(pShellLink, pContext->pidlTarget);
102         else
103             pShellLink->lpVtbl->SetPath(pShellLink, pContext->szTarget);
104 
105         if (pContext->szArguments[0])
106             pShellLink->lpVtbl->SetArguments(pShellLink, pContext->szArguments);
107 
108         if (pContext->szDescription[0])
109             pShellLink->lpVtbl->SetDescription(pShellLink, pContext->szDescription);
110     }
111 
112     if (SUCCEEDED(hr))
113         hr = pPersistFile->lpVtbl->Save(pPersistFile, pContext->szLinkName, TRUE);
114 
115     IUnknown_Release(pPersistFile);
116     IUnknown_Release(pShellLink);
117     return SUCCEEDED(hr);
118 }
119 
120 BOOL
121 CreateInternetShortcut(PCREATE_LINK_CONTEXT pContext)
122 {
123     IUniformResourceLocatorW *pURL = NULL;
124     IPersistFile *pPersistFile = NULL;
125     HRESULT hr;
126     WCHAR szPath[MAX_PATH];
127     GetFullPathNameW(pContext->szLinkName, _countof(szPath), szPath, NULL);
128 
129     hr = CoCreateInstance(&CLSID_InternetShortcut, NULL, CLSCTX_ALL,
130                           &IID_IUniformResourceLocatorW, (void **)&pURL);
131     if (FAILED(hr))
132         return FALSE;
133 
134     hr = IUnknown_QueryInterface(pURL, &IID_IPersistFile, (void **)&pPersistFile);
135     if (FAILED(hr))
136     {
137         IUnknown_Release(pURL);
138         return FALSE;
139     }
140 
141     pURL->lpVtbl->SetURL(pURL, pContext->szTarget, 0);
142 
143     hr = pPersistFile->lpVtbl->Save(pPersistFile, szPath, TRUE);
144 
145     IUnknown_Release(pPersistFile);
146     IUnknown_Release(pURL);
147 
148     return SUCCEEDED(hr);
149 }
150 
151 BOOL IsInternetLocation(LPCWSTR pszLocation)
152 {
153     return (PathIsURLW(pszLocation) || wcsstr(pszLocation, L"www.") == pszLocation);
154 }
155 
156 /* Remove all invalid characters from the name */
157 void
158 DoConvertNameForFileSystem(LPWSTR szName)
159 {
160     LPWSTR pch1, pch2;
161     for (pch1 = pch2 = szName; *pch1; ++pch1)
162     {
163         if (wcschr(L"\\/:*?\"<>|", *pch1) != NULL)
164         {
165             /* *pch1 is an invalid character */
166             continue;
167         }
168         *pch2 = *pch1;
169         ++pch2;
170     }
171     *pch2 = 0;
172 }
173 
174 BOOL
175 DoValidateShortcutName(PCREATE_LINK_CONTEXT pContext)
176 {
177     SIZE_T cch;
178     LPCWSTR pch, pszName = pContext->szDescription;
179 
180     if (!pszName || !pszName[0])
181         return FALSE;
182 
183     cch = wcslen(pContext->szOrigin) + wcslen(pszName) + 1;
184     if (cch >= MAX_PATH)
185         return FALSE;
186 
187     pch = pszName;
188     for (pch = pszName; *pch; ++pch)
189     {
190         if (wcschr(L"\\/:*?\"<>|", *pch) != NULL)
191         {
192             /* *pch is an invalid character */
193             return FALSE;
194         }
195     }
196 
197     return TRUE;
198 }
199 
200 INT_PTR
201 CALLBACK
202 WelcomeDlgProc(HWND hwndDlg,
203                UINT uMsg,
204                WPARAM wParam,
205                LPARAM lParam)
206 {
207     LPPROPSHEETPAGEW ppsp;
208     PCREATE_LINK_CONTEXT pContext = (PCREATE_LINK_CONTEXT)GetWindowLongPtr(hwndDlg, DWLP_USER);
209     LPPSHNOTIFY lppsn;
210     WCHAR szPath[MAX_PATH * 2], szDisplayName[MAX_PATH];
211     WCHAR szDesc[100];
212     WCHAR szTitle[100];
213     BROWSEINFOW brws;
214     LPITEMIDLIST pidllist;
215     SHFILEINFOW FileInfo;
216 
217     switch(uMsg)
218     {
219         case WM_INITDIALOG:
220             ppsp = (LPPROPSHEETPAGEW)lParam;
221             pContext = (PCREATE_LINK_CONTEXT) ppsp->lParam;
222             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pContext);
223             PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
224             SHAutoComplete(GetDlgItem(hwndDlg, IDC_LINK_LOCATION), SHACF_DEFAULT);
225             break;
226         case WM_COMMAND:
227         {
228             switch (LOWORD(wParam))
229             {
230                 case IDC_LINK_LOCATION:
231                 {
232                     if (HIWORD(wParam) == EN_CHANGE)
233                     {
234                         /* The text was changed by user. Invalidate pidlTarget. */
235                         if (pContext->pidlTarget)
236                         {
237                             CoTaskMemFree(pContext->pidlTarget);
238                             pContext->pidlTarget = NULL;
239                         }
240 
241                         if (SendDlgItemMessage(hwndDlg, IDC_LINK_LOCATION, WM_GETTEXTLENGTH, 0, 0))
242                             PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
243                         else
244                             PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
245                     }
246                     break;
247                 }
248                 case IDC_SHORTCUT_BROWSE:
249                 {
250                     LoadStringW(hApplet, IDS_BROWSE_FOR_TARGET, szTitle, _countof(szTitle));
251                     ZeroMemory(&brws, sizeof(brws));
252                     brws.hwndOwner = hwndDlg;
253                     brws.pidlRoot = NULL;
254                     brws.pszDisplayName = szDisplayName;
255                     brws.lpszTitle = szTitle;
256                     brws.ulFlags = BIF_BROWSEINCLUDEFILES | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE;
257                     brws.lpfn = NULL;
258                     pidllist = SHBrowseForFolderW(&brws);
259                     if (!pidllist)
260                         break;
261 
262                     StringCchCopyW(pContext->szDescription, _countof(pContext->szDescription),
263                                    szDisplayName);
264 
265                     SHGetPathFromIDListW(pidllist, szPath);
266 
267                     if (PathFileExistsW(szPath) && !PathIsRelativeW(szPath))
268                         SetDlgItemTextW(hwndDlg, IDC_LINK_LOCATION, szPath);
269                     else
270                         SetDlgItemTextW(hwndDlg, IDC_LINK_LOCATION, szDisplayName);
271 
272                     SendDlgItemMessageW(hwndDlg, IDC_LINK_LOCATION, EM_SETSEL, 0, -1);
273 
274                     if (pContext->pidlTarget)
275                         CoTaskMemFree(pContext->pidlTarget);
276                     pContext->pidlTarget = pidllist;
277                     break;
278                 }
279             }
280             break;
281         case WM_NOTIFY:
282             lppsn  = (LPPSHNOTIFY) lParam;
283             if (lppsn->hdr.code == PSN_SETACTIVE)
284             {
285                 SetDlgItemTextW(hwndDlg, IDC_LINK_LOCATION, pContext->szTarget);
286             }
287             else if (lppsn->hdr.code == PSN_WIZNEXT)
288             {
289                 GetDlgItemTextW(hwndDlg, IDC_LINK_LOCATION, pContext->szTarget, _countof(pContext->szTarget));
290                 StrTrimW(pContext->szTarget, L" \t");
291                 ExpandEnvironmentStringsW(pContext->szTarget, szPath, _countof(szPath));
292 
293                 if (IsInternetLocation(szPath)) /* The internet location */
294                 {
295                     WCHAR szName[128];
296                     LoadStringW(hApplet, IDS_NEW_INTERNET_SHORTCUT, szName, _countof(szName));
297                     StringCchCopyW(pContext->szDescription, _countof(pContext->szDescription), szName);
298                     pContext->szArguments[0] = 0;
299                     return FALSE;
300                 }
301 
302                 if (pContext->pidlTarget) /* The result of SHBrowseForFolderW */
303                 {
304                     SHGetPathFromIDListW(pContext->pidlTarget, pContext->szTarget);
305                     pContext->szArguments[0] = 0;
306                     return FALSE;
307                 }
308 
309                 /* Otherwise, the target is a command line or pathname */
310 
311                 /* Split and build args */
312                 LPWSTR pszArgs = PathGetArgsW(szPath);
313                 if (pszArgs && pszArgs > szPath)
314                 {
315                     PathRemoveArgsW(szPath);
316                     StringCchCopyW(pContext->szArguments, _countof(pContext->szArguments), pszArgs);
317                 }
318                 else
319                 {
320                     pContext->szArguments[0] = 0;
321                 }
322 
323                 /* Find the file */
324                 if (!PathFindOnPathExW(szPath, NULL, WHICH_DEFAULT) &&
325                     !PathFileExistsW(szPath))
326                 {
327                     /* Not found */
328                     SendDlgItemMessageW(hwndDlg, IDC_LINK_LOCATION, EM_SETSEL, 0, -1);
329 
330                     LoadStringW(hApplet, IDS_CREATE_SHORTCUT, szDesc, _countof(szDesc));
331                     LoadStringW(hApplet, IDS_ERROR_NOT_FOUND, szPath, _countof(szPath));
332 
333                     WCHAR szError[MAX_PATH + 100];
334                     StringCchPrintfW(szError, _countof(szError), szPath, pContext->szTarget);
335                     MessageBoxW(hwndDlg, szError, szDesc, MB_ICONERROR);
336 
337                     /* Prevent the wizard to go next */
338                     SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
339                     return TRUE;
340                 }
341 
342                 /* Rebuild target */
343                 if (PathIsRelativeW(szPath))
344                     GetFullPathNameW(szPath, _countof(pContext->szTarget), pContext->szTarget, NULL);
345                 else
346                     StringCchCopyW(pContext->szTarget, _countof(pContext->szTarget), szPath);
347 
348                 /* Get display name */
349                 FileInfo.szDisplayName[0] = UNICODE_NULL;
350                 if (SHGetFileInfoW(pContext->szTarget, 0, &FileInfo, sizeof(FileInfo), SHGFI_DISPLAYNAME))
351                     StringCchCopyW(pContext->szDescription, _countof(pContext->szDescription), FileInfo.szDisplayName);
352 
353                 break;
354             }
355             else if (lppsn->hdr.code == PSN_RESET && !lppsn->lParam)
356             {
357                 /* The user has clicked [Cancel] */
358                 DeleteFileW(pContext->szOldFile);
359                 SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, pContext->szOldFile, NULL);
360             }
361             break;
362         }
363     }
364     return FALSE;
365 }
366 
367 INT_PTR
368 CALLBACK
369 FinishDlgProc(HWND hwndDlg,
370                UINT uMsg,
371                WPARAM wParam,
372                LPARAM lParam)
373 {
374     LPPROPSHEETPAGEW ppsp;
375     PCREATE_LINK_CONTEXT pContext;
376     LPPSHNOTIFY lppsn;
377     WCHAR szText[MAX_PATH], szPath[MAX_PATH];
378     WCHAR szMessage[128];
379 
380     switch(uMsg)
381     {
382         case WM_INITDIALOG:
383             ppsp = (LPPROPSHEETPAGEW)lParam;
384             pContext = (PCREATE_LINK_CONTEXT) ppsp->lParam;
385             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pContext);
386             PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH);
387             break;
388         case WM_COMMAND:
389             switch(HIWORD(wParam))
390             {
391                 case EN_CHANGE:
392                     if (SendDlgItemMessage(hwndDlg, IDC_SHORTCUT_NAME, WM_GETTEXTLENGTH, 0, 0))
393                     {
394                         GetDlgItemTextW(hwndDlg, IDC_SHORTCUT_NAME, szText, _countof(szText));
395                         StrTrimW(szText, L" \t");
396                         if (szText[0])
397                             PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH);
398                         else
399                             PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
400                     }
401                     else
402                     {
403                         PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
404                     }
405                     break;
406             }
407             break;
408         case WM_NOTIFY:
409             lppsn  = (LPPSHNOTIFY) lParam;
410             pContext = (PCREATE_LINK_CONTEXT) GetWindowLongPtr(hwndDlg, DWLP_USER);
411             if (lppsn->hdr.code == PSN_SETACTIVE)
412             {
413                 /* Remove invalid characters */
414                 DoConvertNameForFileSystem(pContext->szDescription);
415                 PathCleanupSpec(NULL, pContext->szDescription);
416 
417                 /* Is it empty? (rare case) */
418                 if (!pContext->szDescription[0])
419                 {
420                     HMODULE hShell32 = GetModuleHandleW(L"shell32.dll");
421                     LoadStringW(hShell32, IDS_NEWITEMFORMAT, szText, _countof(szText));
422                     LoadStringW(hShell32, IDS_LNK_FILE, szMessage, _countof(szMessage));
423                     StringCchPrintfW(pContext->szDescription, _countof(pContext->szDescription),
424                                      szText, szMessage);
425                 }
426 
427                 /* Build a path from szOldFile */
428                 StringCchCopyW(szText, _countof(szText), pContext->szOldFile);
429                 PathRemoveFileSpecW(szText);
430 
431                 /* Rename duplicate if necessary */
432                 PathAddExtensionW(pContext->szDescription,
433                                   (IsInternetLocation(pContext->szTarget) ? L".url" : L".lnk"));
434                 PathYetAnotherMakeUniqueName(szPath, szText, NULL, pContext->szDescription);
435                 StringCchCopyW(pContext->szDescription, _countof(pContext->szDescription),
436                                PathFindFileNameW(szPath));
437                 PathRemoveExtensionW(pContext->szDescription);
438 
439                 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_NAME, pContext->szDescription);
440                 SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_NAME, EM_SETSEL, 0, -1);
441                 SetFocus(GetDlgItem(hwndDlg, IDC_SHORTCUT_NAME));
442             }
443             else if (lppsn->hdr.code == PSN_WIZFINISH)
444             {
445                 GetDlgItemTextW(hwndDlg, IDC_SHORTCUT_NAME, pContext->szDescription, _countof(pContext->szDescription));
446                 StrTrimW(pContext->szDescription, L" \t");
447 
448                 if (!DoValidateShortcutName(pContext))
449                 {
450                     SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_NAME, EM_SETSEL, 0, -1);
451 
452                     LoadStringW(hApplet, IDS_INVALID_NAME, szMessage, _countof(szMessage));
453                     MessageBoxW(hwndDlg, szMessage, NULL, MB_ICONERROR);
454 
455                     /* prevent the wizard to go next */
456                     SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
457                     return TRUE;
458                 }
459 
460                 /* if old shortcut file exists, then delete it now */
461                 DeleteFileW(pContext->szOldFile);
462                 SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, pContext->szOldFile, NULL);
463 
464                 if (IsInternetLocation(pContext->szTarget))
465                 {
466                     /* internet */
467                     StringCchCopyW(pContext->szLinkName, _countof(pContext->szLinkName),
468                                    pContext->szOrigin);
469                     PathAppendW(pContext->szLinkName, pContext->szDescription);
470 
471                     /* change extension if any */
472                     PathRemoveExtensionW(pContext->szLinkName);
473                     PathAddExtensionW(pContext->szLinkName, L".url");
474 
475                     if (!CreateInternetShortcut(pContext))
476                     {
477                         LoadStringW(hApplet, IDS_CANTMAKEINETSHORTCUT, szMessage, _countof(szMessage));
478                         MessageBoxW(hwndDlg, szMessage, NULL, MB_ICONERROR);
479                     }
480                 }
481                 else
482                 {
483                     /* file */
484                     StringCchCopyW(pContext->szLinkName, _countof(pContext->szLinkName),
485                                    pContext->szOrigin);
486                     PathAppendW(pContext->szLinkName, pContext->szDescription);
487 
488                     /* change extension if any */
489                     PathRemoveExtensionW(pContext->szLinkName);
490                     PathAddExtensionW(pContext->szLinkName, L".lnk");
491 
492                     if (!CreateShortcut(pContext))
493                     {
494                         WCHAR szMessage[128];
495                         LoadStringW(hApplet, IDS_CANTMAKESHORTCUT, szMessage, _countof(szMessage));
496                         MessageBoxW(hwndDlg, szMessage, NULL, MB_ICONERROR);
497                     }
498                 }
499             }
500             else if (lppsn->hdr.code == PSN_RESET && !lppsn->lParam)
501             {
502                 /* The user has clicked [Cancel] */
503                 DeleteFileW(pContext->szOldFile);
504                 SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, pContext->szOldFile, NULL);
505             }
506             break;
507     }
508     return FALSE;
509 }
510 
511 static int CALLBACK
512 PropSheetProc(HWND hwndDlg, UINT uMsg, LPARAM lParam)
513 {
514     // NOTE: This callback is needed to set large icon correctly.
515     HICON hIcon;
516     switch (uMsg)
517     {
518         case PSCB_INITIALIZED:
519         {
520             hIcon = LoadIconW(hApplet, MAKEINTRESOURCEW(IDI_APPINETICO));
521             SendMessageW(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
522             break;
523         }
524     }
525     return 0;
526 }
527 
528 LONG CALLBACK
529 ShowCreateShortcutWizard(HWND hwndCPl, LPCWSTR szPath)
530 {
531     PROPSHEETHEADERW psh;
532     HPROPSHEETPAGE ahpsp[2];
533     PROPSHEETPAGE psp;
534     UINT nPages = 0;
535     UINT nLength;
536     PCREATE_LINK_CONTEXT pContext;
537     WCHAR szMessage[128];
538     LPWSTR pch;
539 
540     pContext = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pContext));
541     if (!pContext)
542     {
543         /* no memory */
544         LoadStringW(hApplet, IDS_NO_MEMORY, szMessage, _countof(szMessage));
545         MessageBoxW(hwndCPl, szMessage, NULL, MB_ICONERROR);
546         return FALSE;
547     }
548 
549     nLength = wcslen(szPath);
550     if (!nLength)
551     {
552         HeapFree(GetProcessHeap(), 0, pContext);
553 
554         /* no directory given */
555         LoadStringW(hApplet, IDS_NO_DIRECTORY, szMessage, _countof(szMessage));
556         MessageBoxW(hwndCPl, szMessage, NULL, MB_ICONERROR);
557         return FALSE;
558     }
559 
560     if (!PathFileExistsW(szPath))
561     {
562         HeapFree(GetProcessHeap(), 0, pContext);
563 
564         /* invalid path */
565         LoadStringW(hApplet, IDS_INVALID_PATH, szMessage, _countof(szMessage));
566         MessageBoxW(hwndCPl, szMessage, NULL, MB_ICONERROR);
567         return FALSE;
568     }
569 
570     /* build the pContext->szOrigin and pContext->szOldFile */
571     if (PathIsDirectoryW(szPath))
572     {
573         StringCchCopyW(pContext->szOrigin, _countof(pContext->szOrigin), szPath);
574         pContext->szOldFile[0] = 0;
575     }
576     else
577     {
578         StringCchCopyW(pContext->szOrigin, _countof(pContext->szOrigin), szPath);
579         pch = PathFindFileNameW(pContext->szOrigin);
580         if (pch && *pch)
581             *pch = 0;
582 
583         StringCchCopyW(pContext->szOldFile, _countof(pContext->szOldFile), szPath);
584 
585         pch = PathFindFileNameW(szPath);
586         if (pch && *pch)
587         {
588             /* build szDescription */
589             StringCchCopyW(pContext->szDescription, _countof(pContext->szDescription), pch);
590             *pch = 0;
591 
592             pch = PathFindExtensionW(pContext->szDescription);
593             *pch = 0;
594         }
595     }
596     PathAddBackslashW(pContext->szOrigin);
597 
598     /* Create the Welcome page */
599     psp.dwSize = sizeof(PROPSHEETPAGE);
600     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
601     psp.hInstance = hApplet;
602     psp.pfnDlgProc = WelcomeDlgProc;
603     psp.pszTemplate = MAKEINTRESOURCEW(IDD_SHORTCUT_LOCATION);
604     psp.lParam = (LPARAM)pContext;
605     ahpsp[nPages++] = CreatePropertySheetPage(&psp);
606 
607     /* Create the Finish page */
608     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
609     psp.pfnDlgProc = FinishDlgProc;
610     psp.pszTemplate = MAKEINTRESOURCEW(IDD_SHORTCUT_FINISH);
611     ahpsp[nPages++] = CreatePropertySheetPage(&psp);
612 
613     /* Create the property sheet */
614     psh.dwSize = sizeof(PROPSHEETHEADER);
615     psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_USEICONID | PSH_USECALLBACK;
616     psh.hInstance = hApplet;
617     psh.pszIcon = MAKEINTRESOURCEW(IDI_APPINETICO);
618     psh.hwndParent = NULL;
619     psh.nPages = nPages;
620     psh.nStartPage = 0;
621     psh.phpage = ahpsp;
622     psh.pszbmWatermark = MAKEINTRESOURCEW(IDB_SHORTCUT);
623     psh.pfnCallback = PropSheetProc;
624 
625     /* Display the wizard */
626     PropertySheet(&psh);
627 
628     CoTaskMemFree(pContext->pidlTarget);
629     HeapFree(GetProcessHeap(), 0, pContext);
630     return TRUE;
631 }
632 
633 LONG
634 CALLBACK
635 NewLinkHereW(HWND hwndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
636 {
637     InitCommonControls();
638     return ShowCreateShortcutWizard(hwndCPl, (LPCWSTR)lParam1);
639 }
640 
641 LONG
642 CALLBACK
643 NewLinkHereA(HWND hwndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
644 {
645     WCHAR szFile[MAX_PATH];
646 
647     if (MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lParam1, -1, szFile, _countof(szFile)))
648         return NewLinkHereW(hwndCPl, uMsg, (LPARAM)szFile, lParam2);
649     return -1;
650 }
651