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