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