[CPL] Properly set display icons of control panel applets (#1129)
[reactos.git] / dll / cpl / appwiz / createlink.c
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
411 PCREATE_LINK_CONTEXT pContext = (PCREATE_LINK_CONTEXT) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CREATE_LINK_CONTEXT));
412 if (!pContext)
413 {
414 /* no memory */
415 return FALSE;
416 }
417
418 nLength = wcslen(szPath);
419 if (!nLength)
420 {
421 HeapFree(GetProcessHeap(), 0, pContext);
422
423 /* no directory given */
424 return FALSE;
425 }
426
427 attrs = GetFileAttributesW(szPath);
428 if (attrs == INVALID_FILE_ATTRIBUTES)
429 {
430 HeapFree(GetProcessHeap(), 0, pContext);
431
432 /* invalid path */
433 return FALSE;
434 }
435
436 /* build the pContext->szOrigin and pContext->szOldFile */
437 StringCchCopyW(pContext->szOrigin, _countof(pContext->szOrigin), szPath);
438 pContext->szOldFile[0] = 0;
439 if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
440 {
441 LPWSTR pch;
442 StringCchCopyW(pContext->szOldFile, _countof(pContext->szOldFile), szPath);
443 pch = PathFindFileNameW(pContext->szOrigin);
444 if (pch && *pch)
445 *pch = 0;
446 }
447 PathAddBackslashW(pContext->szOrigin);
448
449 /* Create the Welcome page */
450 psp.dwSize = sizeof(PROPSHEETPAGE);
451 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
452 psp.hInstance = hApplet;
453 psp.pfnDlgProc = WelcomeDlgProc;
454 psp.pszTemplate = MAKEINTRESOURCEW(IDD_SHORTCUT_LOCATION);
455 psp.lParam = (LPARAM)pContext;
456 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
457
458 /* Create the Finish page */
459 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
460 psp.pfnDlgProc = FinishDlgProc;
461 psp.pszTemplate = MAKEINTRESOURCEW(IDD_SHORTCUT_FINISH);
462 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
463
464
465 /* Create the property sheet */
466 psh.dwSize = sizeof(PROPSHEETHEADER);
467 psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_USEICONID | PSH_USECALLBACK;
468 psh.hInstance = hApplet;
469 psh.pszIcon = MAKEINTRESOURCEW(IDI_APPINETICO);
470 psh.hwndParent = NULL;
471 psh.nPages = nPages;
472 psh.nStartPage = 0;
473 psh.phpage = ahpsp;
474 psh.pszbmWatermark = MAKEINTRESOURCEW(IDB_SHORTCUT);
475 psh.pfnCallback = PropSheetProc;
476
477 /* Display the wizard */
478 PropertySheet(&psh);
479 HeapFree(GetProcessHeap(), 0, pContext);
480 return TRUE;
481 }
482
483 LONG
484 CALLBACK
485 NewLinkHereW(HWND hwndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
486 {
487 return ShowCreateShortcutWizard(hwndCPl, (LPWSTR) lParam1);
488 }
489
490 LONG
491 CALLBACK
492 NewLinkHereA(HWND hwndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
493 {
494 WCHAR szFile[MAX_PATH];
495
496 if (MultiByteToWideChar(CP_ACP, 0, (LPSTR) lParam1, -1, szFile, MAX_PATH))
497 {
498 return ShowCreateShortcutWizard(hwndCPl, szFile);
499 }
500 return -1;
501 }