* Sync up to trunk head (r64894).
[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 * UPDATE HISTORY:
9 * 06-17-2004 Created
10 */
11
12 #include "appwiz.h"
13
14 #include <tchar.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 = wcsrchr(pContext->szTarget, '.');
83
84 if (IsExtensionAShortcut(lpExtension))
85 {
86 hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_ALL, &IID_IShellLinkW, (void**)&pSourceShellLink);
87
88 if (hr != S_OK)
89 return FALSE;
90
91 hr = pSourceShellLink->lpVtbl->QueryInterface(pSourceShellLink, &IID_IPersistFile, (void**)&pPersistFile);
92 if (hr != S_OK)
93 {
94 pSourceShellLink->lpVtbl->Release(pSourceShellLink);
95 return FALSE;
96 }
97
98 hr = pPersistFile->lpVtbl->Load(pPersistFile, (LPCOLESTR)pContext->szTarget, STGM_READ);
99 pPersistFile->lpVtbl->Release(pPersistFile);
100
101 if (hr != S_OK)
102 {
103 pSourceShellLink->lpVtbl->Release(pSourceShellLink);
104 return FALSE;
105 }
106
107 hr = pSourceShellLink->lpVtbl->GetPath(pSourceShellLink, Path, sizeof(Path) / sizeof(WCHAR), NULL, 0);
108 pSourceShellLink->lpVtbl->Release(pSourceShellLink);
109
110 if (hr != S_OK)
111 {
112 return FALSE;
113 }
114 }
115 else
116 {
117 wcscpy(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
127 pShellLink->lpVtbl->SetPath(pShellLink, Path);
128 pShellLink->lpVtbl->SetDescription(pShellLink, pContext->szDescription);
129 pShellLink->lpVtbl->SetWorkingDirectory(pShellLink, pContext->szWorkingDirectory);
130
131 hr = pShellLink->lpVtbl->QueryInterface(pShellLink, &IID_IPersistFile, (void**)&pPersistFile);
132 if (hr != S_OK)
133 {
134 pShellLink->lpVtbl->Release(pShellLink);
135 return FALSE;
136 }
137
138 hr = pPersistFile->lpVtbl->Save(pPersistFile, pContext->szLinkName, TRUE);
139 pPersistFile->lpVtbl->Release(pPersistFile);
140 pShellLink->lpVtbl->Release(pShellLink);
141 return (hr == S_OK);
142 }
143
144
145
146
147 INT_PTR
148 CALLBACK
149 WelcomeDlgProc(HWND hwndDlg,
150 UINT uMsg,
151 WPARAM wParam,
152 LPARAM lParam)
153 {
154 LPPROPSHEETPAGEW ppsp;
155 PCREATE_LINK_CONTEXT pContext;
156 LPPSHNOTIFY lppsn;
157 WCHAR szPath[MAX_PATH];
158 WCHAR szDesc[100];
159 BROWSEINFOW brws;
160 LPITEMIDLIST pidllist;
161 IMalloc* malloc;
162
163 switch(uMsg)
164 {
165 case WM_INITDIALOG:
166 ppsp = (LPPROPSHEETPAGEW)lParam;
167 pContext = (PCREATE_LINK_CONTEXT) ppsp->lParam;
168 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pContext);
169 PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
170 break;
171 case WM_COMMAND:
172 switch(HIWORD(wParam))
173 {
174 case EN_CHANGE:
175 if (SendDlgItemMessage(hwndDlg, IDC_SHORTCUT_LOCATION, WM_GETTEXTLENGTH, 0, 0))
176 {
177 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
178 }
179 else
180 {
181 PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
182 }
183 break;
184 }
185 switch(LOWORD(wParam))
186 {
187 case IDC_SHORTCUT_BROWSE:
188 ZeroMemory(&brws, sizeof(brws));
189 brws.hwndOwner = hwndDlg;
190 brws.pidlRoot = NULL;
191 brws.pszDisplayName = szPath;
192 brws.ulFlags = BIF_BROWSEINCLUDEFILES;
193 brws.lpfn = NULL;
194 pidllist = SHBrowseForFolderW(&brws);
195 if (!pidllist)
196 break;
197
198 if (SHGetPathFromIDListW(pidllist, szPath))
199 SendDlgItemMessage(hwndDlg, IDC_SHORTCUT_LOCATION, WM_SETTEXT, 0, (LPARAM)szPath);
200
201 /* Free memory, if possible */
202 if (SUCCEEDED(SHGetMalloc(&malloc)))
203 {
204 IMalloc_Free(malloc, pidllist);
205 IMalloc_Release(malloc);
206 }
207
208 break;
209 }
210 break;
211 case WM_NOTIFY:
212 lppsn = (LPPSHNOTIFY) lParam;
213 if (lppsn->hdr.code == PSN_WIZNEXT)
214 {
215 pContext = (PCREATE_LINK_CONTEXT) GetWindowLongPtr(hwndDlg, DWLP_USER);
216 SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_LOCATION, WM_GETTEXT, MAX_PATH, (LPARAM)pContext->szTarget);
217 ///
218 /// FIXME
219 /// it should also be possible to create a link to folders, shellobjects etc....
220 ///
221 if (GetFileAttributesW(pContext->szTarget) == INVALID_FILE_ATTRIBUTES)
222 {
223 szDesc[0] = L'\0';
224 szPath[0] = L'\0';
225 if (LoadStringW(hApplet, IDS_CREATE_SHORTCUT, szDesc, 100) < 100 &&
226 LoadStringW(hApplet, IDS_ERROR_NOT_FOUND, szPath, MAX_PATH) < MAX_PATH)
227 {
228 WCHAR szError[MAX_PATH + 100];
229 swprintf(szError, szPath, pContext->szTarget);
230 MessageBoxW(hwndDlg, szError, szDesc, MB_ICONERROR);
231 }
232 SendDlgItemMessage(hwndDlg, IDC_SHORTCUT_LOCATION, EM_SETSEL, 0, -1);
233 SetFocus(GetDlgItem(hwndDlg, IDC_SHORTCUT_LOCATION));
234 SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
235 return -1;
236 }
237 else
238 {
239 WCHAR * first, *last;
240 wcscpy(pContext->szWorkingDirectory, pContext->szTarget);
241 first = wcschr(pContext->szWorkingDirectory, L'\\');
242 last = wcsrchr(pContext->szWorkingDirectory, L'\\');
243 wcscpy(pContext->szDescription, &last[1]);
244
245 if (first != last)
246 last[0] = L'\0';
247 else
248 first[1] = L'\0';
249
250 first = wcsrchr(pContext->szDescription, L'.');
251
252 if(first)
253 first[0] = L'\0';
254 }
255
256 }
257 break;
258 }
259 return FALSE;
260 }
261
262 INT_PTR
263 CALLBACK
264 FinishDlgProc(HWND hwndDlg,
265 UINT uMsg,
266 WPARAM wParam,
267 LPARAM lParam)
268 {
269 LPPROPSHEETPAGEW ppsp;
270 PCREATE_LINK_CONTEXT pContext;
271 LPPSHNOTIFY lppsn;
272
273 switch(uMsg)
274 {
275 case WM_INITDIALOG:
276 ppsp = (LPPROPSHEETPAGEW)lParam;
277 pContext = (PCREATE_LINK_CONTEXT) ppsp->lParam;
278 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pContext);
279 SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_NAME, WM_SETTEXT, 0, (LPARAM)pContext->szDescription);
280 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH);
281 break;
282 case WM_COMMAND:
283 switch(HIWORD(wParam))
284 {
285 case EN_CHANGE:
286 if (SendDlgItemMessage(hwndDlg, IDC_SHORTCUT_NAME, WM_GETTEXTLENGTH, 0, 0))
287 {
288 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH);
289 }
290 else
291 {
292 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
293 }
294 break;
295 }
296 break;
297 case WM_NOTIFY:
298 lppsn = (LPPSHNOTIFY) lParam;
299 pContext = (PCREATE_LINK_CONTEXT) GetWindowLongPtr(hwndDlg, DWLP_USER);
300 if (lppsn->hdr.code == PSN_WIZFINISH)
301 {
302 SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_NAME, WM_GETTEXT, MAX_PATH, (LPARAM)pContext->szDescription);
303 wcscat(pContext->szLinkName, pContext->szDescription);
304 wcscat(pContext->szLinkName, L".lnk");
305 if (!CreateShortcut(pContext))
306 {
307 MessageBox(hwndDlg, _T("Failed to create shortcut"), _T("Error"), MB_ICONERROR);
308 }
309 }
310
311
312 }
313 return FALSE;
314 }
315
316 LONG CALLBACK
317 ShowCreateShortcutWizard(HWND hwndCPl, LPWSTR szPath)
318 {
319 PROPSHEETHEADERW psh;
320 HPROPSHEETPAGE ahpsp[2];
321 PROPSHEETPAGE psp;
322 UINT nPages = 0;
323 UINT nLength;
324 DWORD attrs;
325
326 PCREATE_LINK_CONTEXT pContext = (PCREATE_LINK_CONTEXT) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CREATE_LINK_CONTEXT));
327 if (!pContext)
328 {
329 /* no memory */
330 return FALSE;
331 }
332 nLength = wcslen(szPath);
333 if (!nLength)
334 {
335 HeapFree(GetProcessHeap(), 0, pContext);
336
337 /* no directory given */
338 return FALSE;
339 }
340
341 attrs = GetFileAttributesW(szPath);
342 if (attrs == INVALID_FILE_ATTRIBUTES)
343 {
344 HeapFree(GetProcessHeap(), 0, pContext);
345
346 /* invalid path */
347 return FALSE;
348 }
349
350 wcscpy(pContext->szLinkName, szPath);
351 if (pContext->szLinkName[nLength-1] != L'\\')
352 {
353 pContext->szLinkName[nLength] = L'\\';
354 pContext->szLinkName[nLength+1] = L'\0';
355 }
356
357
358 /* Create the Welcome page */
359 psp.dwSize = sizeof(PROPSHEETPAGE);
360 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
361 psp.hInstance = hApplet;
362 psp.pfnDlgProc = WelcomeDlgProc;
363 psp.pszTemplate = MAKEINTRESOURCEW(IDD_SHORTCUT_LOCATION);
364 psp.lParam = (LPARAM)pContext;
365 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
366
367 /* Create the Finish page */
368 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
369 psp.pfnDlgProc = FinishDlgProc;
370 psp.pszTemplate = MAKEINTRESOURCEW(IDD_SHORTCUT_FINISH);
371 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
372
373
374 /* Create the property sheet */
375 psh.dwSize = sizeof(PROPSHEETHEADER);
376 psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK;
377 psh.hInstance = hApplet;
378 psh.hwndParent = NULL;
379 psh.nPages = nPages;
380 psh.nStartPage = 0;
381 psh.phpage = ahpsp;
382 psh.pszbmWatermark = MAKEINTRESOURCEW(IDB_WATERMARK);
383
384 /* Display the wizard */
385 PropertySheet(&psh);
386 HeapFree(GetProcessHeap(), 0, pContext);
387 return TRUE;
388 }
389
390 LONG
391 CALLBACK
392 NewLinkHereW(HWND hwndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
393 {
394 return ShowCreateShortcutWizard(hwndCPl, (LPWSTR) lParam1);
395 }
396
397 LONG
398 CALLBACK
399 NewLinkHereA(HWND hwndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
400 {
401 WCHAR szFile[MAX_PATH];
402
403 if (MultiByteToWideChar(CP_ACP, 0, (LPSTR) lParam1, -1, szFile, MAX_PATH))
404 {
405 return ShowCreateShortcutWizard(hwndCPl, szFile);
406 }
407 return -1;
408 }