[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / she_ocmenu.cpp
1 /*
2 * Open With Context Menu extension
3 *
4 * Copyright 2007 Johannes Anderwald <janderwald@reactos.org>
5 * Copyright 2009 Andrew Hill
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include <precomp.h>
23
24 WINE_DEFAULT_DEBUG_CHANNEL (shell);
25
26 ///
27 /// [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system]
28 /// "NoInternetOpenWith"=dword:00000001
29 ///
30
31 // TODO
32 // implement duplicate checks in list box
33 // implement duplicate checks for MRU!
34 // implement owner drawn menu
35
36 typedef struct
37 {
38 BOOL bMenu;
39 HMENU hMenu;
40 HWND hDlgCtrl;
41 UINT Count;
42 BOOL NoOpen;
43 UINT idCmdFirst;
44 }OPEN_WITH_CONTEXT, *POPEN_WITH_CONTEXT;
45
46 #define MANUFACTURER_NAME_SIZE 100
47
48 typedef struct
49 {
50 HICON hIcon;
51 WCHAR szAppName[MAX_PATH];
52 WCHAR szManufacturer[MANUFACTURER_NAME_SIZE];
53 }OPEN_ITEM_CONTEXT, *POPEN_ITEM_CONTEXT;
54
55
56 typedef struct _LANGANDCODEPAGE_
57 {
58 WORD lang;
59 WORD code;
60 } LANGANDCODEPAGE, *LPLANGANDCODEPAGE;
61
62 HANDLE OpenMRUList(HKEY hKey);
63
64 void LoadItemFromHKCU(POPEN_WITH_CONTEXT pContext, const WCHAR * szExt);
65 void LoadItemFromHKCR(POPEN_WITH_CONTEXT pContext, const WCHAR * szExt);
66 void InsertOpenWithItem(POPEN_WITH_CONTEXT pContext, WCHAR * szAppName);
67
68 COpenWithMenu::COpenWithMenu()
69 {
70 count = 0;
71 wId = 0;
72 }
73
74 COpenWithMenu::~COpenWithMenu()
75 {
76 TRACE(" destroying IContextMenu(%p)\n", this);
77 }
78
79 VOID
80 AddItem(HMENU hMenu, UINT idCmdFirst)
81 {
82 MENUITEMINFOW mii;
83 WCHAR szBuffer[MAX_PATH];
84 static const WCHAR szChoose[] = { 'C','h','o','o','s','e',' ','P','r','o','g','r','a','m','.','.','.',0 };
85
86 ZeroMemory(&mii, sizeof(mii));
87 mii.cbSize = sizeof(mii);
88 mii.fMask = MIIM_TYPE | MIIM_ID;
89 mii.fType = MFT_SEPARATOR;
90 mii.wID = -1;
91 InsertMenuItemW(hMenu, -1, TRUE, &mii);
92
93 if (!LoadStringW(shell32_hInstance, IDS_OPEN_WITH_CHOOSE, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
94 wcscpy(szBuffer, szChoose);
95
96 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
97
98 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
99 mii.fType = MFT_STRING;
100 mii.fState = MFS_ENABLED;
101 mii.wID = idCmdFirst;
102 mii.dwTypeData = (LPWSTR)szBuffer;
103 mii.cch = wcslen(szBuffer);
104
105 InsertMenuItemW(hMenu, -1, TRUE, &mii);
106 }
107
108 static
109 void
110 LoadOWItems(POPEN_WITH_CONTEXT pContext, LPCWSTR szName)
111 {
112 const WCHAR * szExt;
113 WCHAR szPath[100];
114 DWORD dwPath;
115
116 szExt = wcsrchr(szName, '.');
117 if (!szExt)
118 {
119 /* FIXME
120 * show default list of available programs
121 */
122 return;
123 }
124
125 /* load programs directly associated from HKCU */
126 LoadItemFromHKCU(pContext, szExt);
127
128 /* load programs associated from HKCR\Extension */
129 LoadItemFromHKCR(pContext, szExt);
130
131 /* load programs referenced from HKCR\ProgId */
132 dwPath = sizeof(szPath);
133 szPath[0] = 0;
134 if (RegGetValueW(HKEY_CLASSES_ROOT, szExt, NULL, RRF_RT_REG_SZ, NULL, szPath, &dwPath) == ERROR_SUCCESS)
135 {
136 szPath[(sizeof(szPath)/sizeof(WCHAR))-1] = L'\0';
137 LoadItemFromHKCR(pContext, szPath);
138 }
139 }
140
141
142
143 HRESULT WINAPI COpenWithMenu::QueryContextMenu(
144 HMENU hmenu,
145 UINT indexMenu,
146 UINT idCmdFirst,
147 UINT idCmdLast,
148 UINT uFlags)
149 {
150 MENUITEMINFOW mii;
151 WCHAR szBuffer[100] = {0};
152 INT pos;
153 HMENU hSubMenu = NULL;
154 OPEN_WITH_CONTEXT Context;
155
156 if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH, szBuffer, sizeof(szBuffer)/sizeof(WCHAR)) < 0)
157 {
158 TRACE("failed to load string\n");
159 return E_FAIL;
160 }
161 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
162
163 hSubMenu = CreatePopupMenu();
164
165 /* set up context */
166 ZeroMemory(&Context, sizeof(OPEN_WITH_CONTEXT));
167 Context.bMenu = TRUE;
168 Context.Count = 0;
169 Context.hMenu = hSubMenu;
170 Context.idCmdFirst = idCmdFirst;
171 /* load items */
172 LoadOWItems(&Context, szPath);
173 if (!Context.Count)
174 {
175 DestroyMenu(hSubMenu);
176 hSubMenu = NULL;
177 wId = 0;
178 count = 0;
179 }
180 else
181 {
182 AddItem(hSubMenu, Context.idCmdFirst++);
183 count = Context.idCmdFirst - idCmdFirst;
184 /* verb start at index zero */
185 wId = count -1;
186 hSubMenu = hSubMenu;
187 }
188
189 pos = GetMenuDefaultItem(hmenu, TRUE, 0) + 1;
190
191 ZeroMemory(&mii, sizeof(mii));
192 mii.cbSize = sizeof(mii);
193 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
194 if (hSubMenu)
195 {
196 mii.fMask |= MIIM_SUBMENU;
197 mii.hSubMenu = hSubMenu;
198 }
199 mii.dwTypeData = (LPWSTR) szBuffer;
200 mii.fState = MFS_ENABLED;
201 if (!pos)
202 {
203 mii.fState |= MFS_DEFAULT;
204 }
205
206 mii.wID = Context.idCmdFirst;
207 mii.fType = MFT_STRING;
208 if (InsertMenuItemW( hmenu, pos, TRUE, &mii))
209 Context.Count++;
210
211 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Context.Count);
212 }
213
214 void
215 FreeListItems(HWND hwndDlg)
216 {
217 HWND hList;
218 LRESULT iIndex, iCount;
219 POPEN_ITEM_CONTEXT pContext;
220
221 hList = GetDlgItem(hwndDlg, 14002);
222 iCount = SendMessageW(hList, LB_GETCOUNT, 0, 0);
223 if (iCount == LB_ERR)
224 return;
225
226 for (iIndex = 0; iIndex < iCount; iIndex++)
227 {
228 pContext = (POPEN_ITEM_CONTEXT)SendMessageW(hList, LB_GETITEMDATA, iIndex, 0);
229 if (pContext)
230 {
231 DestroyIcon(pContext->hIcon);
232 SendMessageW(hList, LB_SETITEMDATA, iIndex, (LPARAM)0);
233 HeapFree(GetProcessHeap(), 0, pContext);
234 }
235 }
236 }
237
238 BOOL HideApplicationFromList(WCHAR * pFileName)
239 {
240 WCHAR szBuffer[100] = {'A','p','p','l','i','c','a','t','i','o','n','s','\\',0};
241 DWORD dwSize = 0;
242 LONG result;
243
244 if (wcslen(pFileName) > (sizeof(szBuffer)/sizeof(WCHAR)) - 14)
245 {
246 ERR("insufficient buffer\n");
247 return FALSE;
248 }
249 wcscpy(&szBuffer[13], pFileName);
250
251 result = RegGetValueW(HKEY_CLASSES_ROOT, szBuffer, L"NoOpenWith", RRF_RT_REG_SZ, NULL, NULL, &dwSize);
252
253 TRACE("result %d szBuffer %s\n", result, debugstr_w(szBuffer));
254
255 if (result == ERROR_SUCCESS)
256 return TRUE;
257 else
258 return FALSE;
259 }
260
261 VOID
262 WriteStaticShellExtensionKey(HKEY hRootKey, const WCHAR * pVerb, WCHAR *pFullPath)
263 {
264 HKEY hShell;
265 LONG result;
266 WCHAR szBuffer[MAX_PATH+10] = {'s','h','e','l','l','\\', 0 };
267
268 if (wcslen(pVerb) > (sizeof(szBuffer)/sizeof(WCHAR)) - 15 ||
269 wcslen(pFullPath) > (sizeof(szBuffer)/sizeof(WCHAR)) - 4)
270 {
271 ERR("insufficient buffer\n");
272 return;
273 }
274
275 /* construct verb reg path */
276 wcscpy(&szBuffer[6], pVerb);
277 wcscat(szBuffer, L"\\command");
278
279 /* create verb reg key */
280 if (RegCreateKeyExW(hRootKey, szBuffer, 0, NULL, 0, KEY_WRITE, NULL, &hShell, NULL) != ERROR_SUCCESS)
281 return;
282
283 /* build command buffer */
284 wcscpy(szBuffer, pFullPath);
285 wcscat(szBuffer, L" %1");
286
287 result = RegSetValueExW(hShell, NULL, 0, REG_SZ, (const BYTE*)szBuffer, (wcslen(szBuffer)+1)* sizeof(WCHAR));
288 RegCloseKey(hShell);
289 }
290
291 VOID
292 StoreNewSettings(LPCWSTR szFileName, WCHAR *szAppName)
293 {
294 WCHAR szBuffer[100] = { L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"};
295 const WCHAR * pFileExt;
296 HKEY hKey;
297 LONG result;
298 HANDLE hList;
299
300 /* get file extension */
301 pFileExt = wcsrchr(szFileName, L'.');
302 if (wcslen(pFileExt) > (sizeof(szBuffer)/sizeof(WCHAR)) - 60)
303 {
304 ERR("insufficient buffer\n");
305 return;
306 }
307 wcscpy(&szBuffer[60], pFileExt);
308 /* open base key for this file extension */
309 if (RegCreateKeyExW(HKEY_CURRENT_USER, szBuffer, 0, NULL, 0, KEY_WRITE | KEY_READ, NULL, &hKey, NULL) != ERROR_SUCCESS)
310 return;
311
312 /* open mru list */
313 hList = OpenMRUList(hKey);
314
315 if (!hList)
316 {
317 RegCloseKey(hKey);
318 return;
319 }
320
321 /* insert the entry */
322 result = AddMRUStringW(hList, szAppName);
323
324 /* close mru list */
325 FreeMRUList(hList);
326 /* create mru list key */
327 RegCloseKey(hKey);
328 }
329
330 VOID
331 SetProgrammAsDefaultHandler(LPCWSTR szFileName, WCHAR * szAppName)
332 {
333 HKEY hKey;
334 HKEY hAppKey;
335 DWORD dwDisposition;
336 WCHAR szBuffer[100];
337 DWORD dwSize;
338 BOOL result;
339 const WCHAR * pFileExt;
340 WCHAR * pFileName;
341
342 /* extract file extension */
343 pFileExt = wcsrchr(szFileName, L'.');
344 if (!pFileExt)
345 return;
346
347 /* create file extension key */
348 if (RegCreateKeyExW(HKEY_CLASSES_ROOT, pFileExt, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
349 return;
350
351 if (dwDisposition & REG_CREATED_NEW_KEY)
352 {
353 /* a new entry was created create the prog key id */
354 wcscpy(szBuffer, &pFileExt[1]);
355 wcscat(szBuffer, L"_auto_file");
356 if (RegSetValueExW(hKey, NULL, 0, REG_SZ, (const BYTE*)szBuffer, (wcslen(szBuffer)+1) * sizeof(WCHAR)) != ERROR_SUCCESS)
357 {
358 RegCloseKey(hKey);
359 return;
360 }
361 }
362 else
363 {
364 /* entry already exists fetch prog key id */
365 dwSize = sizeof(szBuffer);
366 if (RegGetValueW(hKey, NULL, NULL, RRF_RT_REG_SZ, NULL, szBuffer, &dwSize) != ERROR_SUCCESS)
367 {
368 RegCloseKey(hKey);
369 return;
370 }
371 }
372 /* close file extension key */
373 RegCloseKey(hKey);
374
375 /* create prog id key */
376 if (RegCreateKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
377 return;
378
379
380 /* check if there already verbs existing for that app */
381 pFileName = wcsrchr(szAppName, L'\\');
382 wcscpy(szBuffer, L"Classes\\Applications\\");
383 wcscat(szBuffer, pFileName);
384 wcscat(szBuffer, L"\\shell");
385 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ, &hAppKey) == ERROR_SUCCESS)
386 {
387 /* copy static verbs from Classes\Applications key */
388 HKEY hTemp;
389 if (RegCreateKeyExW(hKey, L"shell", 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hTemp, &dwDisposition) == ERROR_SUCCESS)
390 {
391 result = RegCopyTreeW(hAppKey, NULL, hTemp);
392 RegCloseKey(hTemp);
393 if (result == ERROR_SUCCESS)
394 {
395 /* copied all subkeys, we are done */
396 RegCloseKey(hKey);
397 RegCloseKey(hAppKey);
398 return;
399 }
400 }
401 RegCloseKey(hAppKey);
402 }
403 /* write standard static shell extension */
404 WriteStaticShellExtensionKey(hKey, L"open", szAppName);
405 RegCloseKey(hKey);
406 }
407
408 void
409 BrowseForApplication(HWND hwndDlg)
410 {
411 WCHAR szBuffer[64] = {0};
412 WCHAR szFilter[256] = {0};
413 WCHAR szPath[MAX_PATH];
414 OPENFILENAMEW ofn;
415 OPEN_WITH_CONTEXT Context;
416 INT count;
417
418 /* load resource open with */
419 if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
420 {
421 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
422 ofn.lpstrTitle = szBuffer;
423 ofn.nMaxFileTitle = wcslen(szBuffer);
424 }
425
426 ZeroMemory(&ofn, sizeof(OPENFILENAMEW));
427 ofn.lStructSize = sizeof(OPENFILENAMEW);
428 ofn.hInstance = shell32_hInstance;
429 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
430 ofn.nMaxFile = (sizeof(szPath) / sizeof(WCHAR));
431 ofn.lpstrFile = szPath;
432
433 /* load the filter resource string */
434 if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH_FILTER, szFilter, sizeof(szFilter) / sizeof(WCHAR)))
435 {
436 szFilter[(sizeof(szFilter)/sizeof(WCHAR))-1] = 0;
437 ofn.lpstrFilter = szFilter;
438 }
439 ZeroMemory(szPath, sizeof(szPath));
440
441 /* call openfilename */
442 if (!GetOpenFileNameW(&ofn))
443 return;
444
445 /* setup context for insert proc */
446 ZeroMemory(&Context, sizeof(OPEN_WITH_CONTEXT));
447 Context.hDlgCtrl = GetDlgItem(hwndDlg, 14002);
448 count = SendMessage(Context.hDlgCtrl, LB_GETCOUNT, 0, 0);
449 InsertOpenWithItem(&Context, szPath);
450 /* select new item */
451 SendMessage(Context.hDlgCtrl, LB_SETCURSEL, count, 0);
452 }
453
454 POPEN_ITEM_CONTEXT
455 GetCurrentOpenItemContext(HWND hwndDlg)
456 {
457 LRESULT result;
458
459 /* get current item */
460 result = SendDlgItemMessage(hwndDlg, 14002, LB_GETCURSEL, 0, 0);
461 if(result == LB_ERR)
462 return NULL;
463
464 /* get item context */
465 result = SendDlgItemMessage(hwndDlg, 14002, LB_GETITEMDATA, result, 0);
466 if (result == LB_ERR)
467 return NULL;
468
469 return (POPEN_ITEM_CONTEXT)result;
470 }
471
472 void
473 ExecuteOpenItem(POPEN_ITEM_CONTEXT pItemContext, LPCWSTR FileName)
474 {
475 STARTUPINFOW si;
476 PROCESS_INFORMATION pi;
477 WCHAR szPath[(MAX_PATH * 2)];
478
479 /* setup path with argument */
480 ZeroMemory(&si, sizeof(STARTUPINFOW));
481 si.cb = sizeof(STARTUPINFOW);
482 wcscpy(szPath, pItemContext->szAppName);
483 wcscat(szPath, L" ");
484 wcscat(szPath, FileName);
485
486 ERR("path %s\n", debugstr_w(szPath));
487
488 if (CreateProcessW(NULL, szPath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
489 {
490 CloseHandle(pi.hThread);
491 CloseHandle(pi.hProcess);
492 SHAddToRecentDocs(SHARD_PATHW, FileName);
493 }
494 }
495
496
497 static INT_PTR CALLBACK OpenWithProgrammDlg(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
498 {
499 LPMEASUREITEMSTRUCT lpmis;
500 LPDRAWITEMSTRUCT lpdis;
501 INT index;
502 WCHAR szBuffer[MAX_PATH + 30] = { 0 };
503 OPENASINFO *poainfo;
504 TEXTMETRIC mt;
505 COLORREF preColor, preBkColor;
506 POPEN_ITEM_CONTEXT pItemContext;
507 LONG YOffset;
508 OPEN_WITH_CONTEXT Context;
509
510 poainfo = (OPENASINFO*) GetWindowLongPtr(hwndDlg, DWLP_USER);
511
512 switch(uMsg)
513 {
514 case WM_INITDIALOG:
515 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG)lParam);
516 poainfo = (OPENASINFO*)lParam;
517 if (!(poainfo->oaifInFlags & OAIF_ALLOW_REGISTRATION))
518 EnableWindow(GetDlgItem(hwndDlg, 14003), FALSE);
519 if (poainfo->oaifInFlags & OAIF_FORCE_REGISTRATION)
520 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_CHECKED, 0);
521 if (poainfo->oaifInFlags & OAIF_HIDE_REGISTRATION)
522 ShowWindow(GetDlgItem(hwndDlg, 14003), SW_HIDE);
523 if (poainfo->pcszFile)
524 {
525 szBuffer[0] = L'\0';
526 SendDlgItemMessageW(hwndDlg, 14001, WM_GETTEXT, sizeof(szBuffer), (LPARAM)szBuffer);
527 index = wcslen(szBuffer);
528 if (index + wcslen(poainfo->pcszFile) + 1 < sizeof(szBuffer)/sizeof(szBuffer[0]))
529 wcscat(szBuffer, poainfo->pcszFile);
530 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
531 SendDlgItemMessageW(hwndDlg, 14001, WM_SETTEXT, 0, (LPARAM)szBuffer);
532 ZeroMemory(&Context, sizeof(OPEN_WITH_CONTEXT));
533 Context.hDlgCtrl = GetDlgItem(hwndDlg, 14002);
534 LoadOWItems(&Context, poainfo->pcszFile);
535 SendMessage(Context.hDlgCtrl, LB_SETCURSEL, 0, 0);
536 }
537 return TRUE;
538 case WM_MEASUREITEM:
539 lpmis = (LPMEASUREITEMSTRUCT) lParam;
540 lpmis->itemHeight = 64;
541 return TRUE;
542 case WM_COMMAND:
543 switch(LOWORD(wParam))
544 {
545 case 14004: /* browse */
546 BrowseForApplication(hwndDlg);
547 return TRUE;
548 case 14002:
549 if (HIWORD(wParam) == LBN_SELCHANGE)
550 InvalidateRect((HWND)lParam, NULL, TRUE); // FIXME USE UPDATE RECT
551 break;
552 case 14005: /* ok */
553 pItemContext = GetCurrentOpenItemContext(hwndDlg);
554 if (pItemContext)
555 {
556 /* store settings in HKCU path */
557 StoreNewSettings(poainfo->pcszFile, pItemContext->szAppName);
558
559 if (SendDlgItemMessage(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
560 {
561 /* set programm as default handler */
562 SetProgrammAsDefaultHandler(poainfo->pcszFile, pItemContext->szAppName);
563 }
564
565 if (poainfo->oaifInFlags & OAIF_EXEC)
566 ExecuteOpenItem(pItemContext, poainfo->pcszFile);
567 }
568 FreeListItems(hwndDlg);
569 EndDialog(hwndDlg, 1);
570 return TRUE;
571 case 14006: /* cancel */
572 FreeListItems(hwndDlg);
573 EndDialog(hwndDlg, 0);
574 return TRUE;
575 default:
576 break;
577 }
578 break;
579 case WM_DRAWITEM:
580 lpdis = (LPDRAWITEMSTRUCT) lParam;
581 if ((int)lpdis->itemID == -1)
582 break;
583
584 switch (lpdis->itemAction)
585 {
586 case ODA_SELECT:
587 case ODA_DRAWENTIRE:
588 index = SendMessageW(lpdis->hwndItem, LB_GETCURSEL, 0, 0);
589 pItemContext =(POPEN_ITEM_CONTEXT)SendMessage(lpdis->hwndItem, LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0);
590
591 if ((int)lpdis->itemID == index)
592 {
593 /* paint focused item with standard background colour */
594 HBRUSH hBrush;
595 hBrush = CreateSolidBrush(RGB(46, 104, 160));
596 FillRect(lpdis->hDC, &lpdis->rcItem, hBrush);
597 DeleteObject(hBrush);
598 preBkColor = SetBkColor(lpdis->hDC, RGB(46, 104, 160));
599 }
600 else
601 {
602 /* paint non focused item with white background */
603 HBRUSH hBrush;
604 hBrush = CreateSolidBrush(RGB(255, 255, 255));
605 FillRect(lpdis->hDC, &lpdis->rcItem, hBrush);
606 DeleteObject(hBrush);
607 preBkColor = SetBkColor(lpdis->hDC, RGB(255, 255, 255));
608 }
609
610 SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID, (LPARAM) szBuffer);
611 /* paint the icon */
612 DrawIconEx(lpdis->hDC, lpdis->rcItem.left,lpdis->rcItem.top, pItemContext->hIcon, 0, 0, 0, NULL, DI_NORMAL);
613 /* get text size */
614 GetTextMetrics(lpdis->hDC, &mt);
615 /* paint app name */
616 YOffset = lpdis->rcItem.top + mt.tmHeight/2;
617 TextOutW(lpdis->hDC, 45, YOffset, szBuffer, wcslen(szBuffer));
618 /* paint manufacturer description */
619 YOffset += mt.tmHeight + 2;
620 preColor = SetTextColor(lpdis->hDC, RGB(192, 192, 192));
621 if (pItemContext->szManufacturer[0])
622 TextOutW(lpdis->hDC, 45, YOffset, pItemContext->szManufacturer, wcslen(pItemContext->szManufacturer));
623 else
624 TextOutW(lpdis->hDC, 45, YOffset, pItemContext->szAppName, wcslen(pItemContext->szAppName));
625 SetTextColor(lpdis->hDC, preColor);
626 SetBkColor(lpdis->hDC, preBkColor);
627 break;
628 }
629 break;
630 case WM_CLOSE:
631 FreeListItems(hwndDlg);
632 EndDialog(hwndDlg, 0);
633 return TRUE;
634 default:
635 break;
636 }
637 return FALSE;
638 }
639
640 void
641 FreeMenuItemContext(HMENU hMenu)
642 {
643 INT Count;
644 INT Index;
645 MENUITEMINFOW mii;
646
647 /* get item count */
648 Count = GetMenuItemCount(hMenu);
649 if (Count == -1)
650 return;
651
652 /* setup menuitem info */
653 ZeroMemory(&mii, sizeof(mii));
654 mii.cbSize = sizeof(mii);
655 mii.fMask = MIIM_DATA | MIIM_FTYPE;
656
657 for(Index = 0; Index < Count; Index++)
658 {
659 if (GetMenuItemInfoW(hMenu, Index, TRUE, &mii))
660 {
661 if ((mii.fType & MFT_SEPARATOR) || mii.dwItemData == 0)
662 continue;
663 HeapFree(GetProcessHeap(), 0, (LPVOID)mii.dwItemData);
664 }
665 }
666 }
667
668
669 HRESULT WINAPI
670 COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici )
671 {
672 MENUITEMINFOW mii;
673
674 ERR("This %p wId %x count %u verb %x\n", this, wId, count, LOWORD(lpici->lpVerb));
675
676 if (wId < LOWORD(lpici->lpVerb))
677 return E_FAIL;
678
679 if (wId == LOWORD(lpici->lpVerb))
680 {
681 OPENASINFO info;
682
683 info.pcszFile = szPath;
684 info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC;
685 info.pcszClass = NULL;
686 FreeMenuItemContext(hSubMenu);
687 return SHOpenWithDialog(lpici->hwnd, &info);
688 }
689
690 /* retrieve menu item info */
691 ZeroMemory(&mii, sizeof(mii));
692 mii.cbSize = sizeof(mii);
693 mii.fMask = MIIM_DATA | MIIM_FTYPE;
694
695 if (GetMenuItemInfoW(hSubMenu, LOWORD(lpici->lpVerb), TRUE, &mii))
696 {
697 POPEN_ITEM_CONTEXT pItemContext = (POPEN_ITEM_CONTEXT)mii.dwItemData;
698 if (pItemContext)
699 {
700 /* launch item with specified app */
701 ExecuteOpenItem(pItemContext, szPath);
702 }
703 }
704 /* free menu item context */
705 FreeMenuItemContext(hSubMenu);
706 return S_OK;
707 }
708
709 HRESULT WINAPI
710 COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType,
711 UINT* pwReserved, LPSTR pszName, UINT cchMax )
712 {
713 FIXME("%p %lu %u %p %p %u\n", this,
714 idCmd, uType, pwReserved, pszName, cchMax );
715
716 return E_NOTIMPL;
717 }
718
719 HRESULT WINAPI COpenWithMenu::HandleMenuMsg(
720 UINT uMsg,
721 WPARAM wParam,
722 LPARAM lParam)
723 {
724 TRACE("This %p uMsg %x\n",this, uMsg);
725
726 return E_NOTIMPL;
727 }
728
729 VOID
730 GetManufacturer(WCHAR * szAppName, POPEN_ITEM_CONTEXT pContext)
731 {
732 UINT VerSize;
733 DWORD DummyHandle;
734 LPVOID pBuf;
735 WORD lang = 0;
736 WORD code = 0;
737 LPLANGANDCODEPAGE lplangcode;
738 WCHAR szBuffer[100];
739 WCHAR * pResult;
740 BOOL bResult;
741
742 static const WCHAR wFormat[] = L"\\StringFileInfo\\%04x%04x\\CompanyName";
743 static const WCHAR wTranslation[] = L"VarFileInfo\\Translation";
744
745 /* query version info size */
746 VerSize = GetFileVersionInfoSizeW(szAppName, &DummyHandle);
747 if (!VerSize)
748 {
749 pContext->szManufacturer[0] = 0;
750 return;
751 }
752
753 /* allocate buffer */
754 pBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, VerSize);
755 if (!pBuf)
756 {
757 pContext->szManufacturer[0] = 0;
758 return;
759 }
760
761 /* query version info */
762 if(!GetFileVersionInfoW(szAppName, 0, VerSize, pBuf))
763 {
764 pContext->szManufacturer[0] = 0;
765 HeapFree(GetProcessHeap(), 0, pBuf);
766 return;
767 }
768
769 /* query lang code */
770 if(VerQueryValueW(pBuf, const_cast<LPWSTR>(wTranslation), (LPVOID *)&lplangcode, &VerSize))
771 {
772 /* FIXME find language from current locale / if not available,
773 * default to english
774 * for now default to first available language
775 */
776 lang = lplangcode->lang;
777 code = lplangcode->code;
778 }
779 /* set up format */
780 swprintf(szBuffer, wFormat, lang, code);
781 /* query manufacturer */
782 pResult = NULL;
783 bResult = VerQueryValueW(pBuf, szBuffer, (LPVOID *)&pResult, &VerSize);
784
785 if (VerSize && bResult && pResult)
786 wcscpy(pContext->szManufacturer, pResult);
787 else
788 pContext->szManufacturer[0] = 0;
789 HeapFree(GetProcessHeap(), 0, pBuf);
790 }
791
792
793
794
795 void
796 InsertOpenWithItem(POPEN_WITH_CONTEXT pContext, WCHAR * szAppName)
797 {
798 MENUITEMINFOW mii;
799 POPEN_ITEM_CONTEXT pItemContext;
800 LRESULT index;
801 WCHAR * Offset;
802 WCHAR Buffer[_MAX_FNAME];
803
804 pItemContext = (OPEN_ITEM_CONTEXT *)HeapAlloc(GetProcessHeap(), 0, sizeof(OPEN_ITEM_CONTEXT));
805 if (!pItemContext)
806 return;
807
808 /* store app path */
809 wcscpy(pItemContext->szAppName, szAppName);
810 /* null terminate it */
811 pItemContext->szAppName[MAX_PATH-1] = 0;
812 /* extract path name */
813 _wsplitpath(szAppName, NULL, NULL, Buffer, NULL);
814 Offset = wcsrchr(Buffer, '.');
815 if (Offset)
816 Offset[0] = L'\0';
817 Buffer[0] = towupper(Buffer[0]);
818
819 if (pContext->bMenu)
820 {
821 ZeroMemory(&mii, sizeof(mii));
822 mii.cbSize = sizeof(mii);
823 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
824 mii.fType = MFT_STRING; //MFT_OWNERDRAW;
825 mii.fState = MFS_ENABLED;
826 mii.wID = pContext->idCmdFirst;
827 mii.dwTypeData = Buffer;
828 mii.cch = wcslen(Buffer);
829 mii.dwItemData = (ULONG_PTR)pItemContext;
830 wcscpy(pItemContext->szManufacturer, Buffer);
831 if (InsertMenuItemW(pContext->hMenu, -1, TRUE, &mii))
832 {
833 pContext->idCmdFirst++;
834 pContext->Count++;
835 }
836 }
837 else
838 {
839 /* get default icon */
840 pItemContext->hIcon = ExtractIconW(shell32_hInstance, szAppName, 0);
841 /* get manufacturer */
842 GetManufacturer(pItemContext->szAppName, pItemContext);
843 index = SendMessageW(pContext->hDlgCtrl, LB_ADDSTRING, 0, (LPARAM)Buffer);
844 if (index != LB_ERR)
845 SendMessageW(pContext->hDlgCtrl, LB_SETITEMDATA, index, (LPARAM)pItemContext);
846 }
847 }
848
849 void
850 AddItemFromProgIDList(POPEN_WITH_CONTEXT pContext, HKEY hKey)
851 {
852 FIXME("implement me :)))\n");
853 }
854
855 HANDLE
856 OpenMRUList(HKEY hKey)
857 {
858 CREATEMRULISTW info;
859
860 /* initialize mru list info */
861 info.cbSize = sizeof(info);
862 info.nMaxItems = 32;
863 info.dwFlags = MRU_STRING;
864 info.hKey = hKey;
865 info.lpszSubKey = L"OpenWithList";
866 info.lpfnCompare = NULL;
867
868 /* load list */
869 return CreateMRUListW(&info);
870 }
871
872 void
873 AddItemFromMRUList(POPEN_WITH_CONTEXT pContext, HKEY hKey)
874 {
875 HANDLE hList;
876 int nItem, nCount, nResult;
877 WCHAR szBuffer[MAX_PATH];
878
879 /* open mru list */
880 hList = OpenMRUList(hKey);
881 if (!hList)
882 return;
883
884 /* get list count */
885 nCount = EnumMRUListW(hList, -1, NULL, 0);
886
887 for(nItem = 0; nItem < nCount; nItem++)
888 {
889 nResult = EnumMRUListW(hList, nItem, szBuffer, MAX_PATH);
890 if (nResult <= 0)
891 continue;
892 /* make sure its zero terminated */
893 szBuffer[min(MAX_PATH-1, nResult)] = '\0';
894 /* insert item */
895 if (!HideApplicationFromList(szBuffer))
896 InsertOpenWithItem(pContext, szBuffer);
897 }
898
899 /* free the mru list */
900 FreeMRUList(hList);
901 }
902
903
904
905 void
906 LoadItemFromHKCR(POPEN_WITH_CONTEXT pContext, const WCHAR * szExt)
907 {
908 HKEY hKey;
909 HKEY hSubKey;
910 WCHAR szBuffer[MAX_PATH+10];
911 WCHAR szResult[100];
912 DWORD dwSize;
913
914 static const WCHAR szOpenWithList[] = L"OpenWithList";
915 static const WCHAR szOpenWithProgIds[] = L"OpenWithProgIDs";
916 static const WCHAR szPerceivedType[] = L"PerceivedType";
917 static const WCHAR szSysFileAssoc[] = L"SystemFileAssociations\\%s";
918
919 /* check if extension exists */
920 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, szExt, 0, KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS)
921 return;
922
923 if (RegGetValueW(hKey, NULL, L"NoOpen", RRF_RT_REG_SZ, NULL, NULL, &dwSize) == ERROR_SUCCESS)
924 {
925 /* display warning dialog */
926 pContext->NoOpen = TRUE;
927 }
928
929 /* check if there is a directly available execute key */
930 if (RegOpenKeyExW(hKey, L"shell\\open\\command", 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
931 {
932 DWORD dwBuffer = sizeof(szBuffer);
933
934 if (RegGetValueW(hSubKey, NULL, NULL, RRF_RT_REG_SZ, NULL, (PVOID)szBuffer, &dwBuffer) == ERROR_SUCCESS)
935 {
936 WCHAR * Ext = wcsrchr(szBuffer, ' ');
937 if (Ext)
938 {
939 /* erase %1 or extra arguments */
940 Ext[0] = 0;
941 }
942 if(!HideApplicationFromList(szBuffer))
943 InsertOpenWithItem(pContext, szBuffer);
944 }
945 RegCloseKey(hSubKey);
946 }
947
948 /* load items from HKCR\Ext\OpenWithList */
949 if (RegOpenKeyExW(hKey, szOpenWithList, 0, KEY_READ | KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS)
950 {
951 AddItemFromMRUList(pContext, hKey);
952 RegCloseKey(hSubKey);
953 }
954
955 /* load items from HKCR\Ext\OpenWithProgIDs */
956 if (RegOpenKeyExW(hKey, szOpenWithProgIds, 0, KEY_READ | KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS)
957 {
958 AddItemFromProgIDList(pContext, hSubKey);
959 RegCloseKey(hSubKey);
960 }
961
962 /* load items from SystemFileAssociations\Ext key */
963 swprintf(szResult, szSysFileAssoc, szExt);
964 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, szResult, 0, KEY_READ | KEY_WRITE, &hSubKey) == ERROR_SUCCESS)
965 {
966 AddItemFromMRUList(pContext, hSubKey);
967 RegCloseKey(hSubKey);
968 }
969
970 /* load additional items from referenced PerceivedType*/
971 dwSize = sizeof(szBuffer);
972 if (RegGetValueW(hKey, NULL, szPerceivedType, RRF_RT_REG_SZ, NULL, szBuffer, &dwSize) != ERROR_SUCCESS)
973 {
974 RegCloseKey(hKey);
975 return;
976 }
977 RegCloseKey(hKey);
978
979 /* terminate it explictely */
980 szBuffer[29] = 0;
981 swprintf(szResult, szSysFileAssoc, szBuffer);
982 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, szResult, 0, KEY_READ | KEY_WRITE, &hSubKey) == ERROR_SUCCESS)
983 {
984 AddItemFromMRUList(pContext, hSubKey);
985 RegCloseKey(hSubKey);
986 }
987 }
988
989 void
990 LoadItemFromHKCU(POPEN_WITH_CONTEXT pContext, const WCHAR * szExt)
991 {
992 WCHAR szBuffer[MAX_PATH];
993 HKEY hKey;
994
995 static const WCHAR szOpenWithProgIDs[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s\\OpenWithProgIDs";
996 static const WCHAR szOpenWithList[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s";
997
998 /* handle first progid lists */
999 swprintf(szBuffer, szOpenWithProgIDs, szExt);
1000 if (RegOpenKeyExW(HKEY_CURRENT_USER, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
1001 {
1002 AddItemFromProgIDList(pContext, hKey);
1003 RegCloseKey(hKey);
1004 }
1005
1006 /* now handle mru lists */
1007 swprintf(szBuffer, szOpenWithList, szExt);
1008 if (RegOpenKeyExW(HKEY_CURRENT_USER, szBuffer, 0, KEY_READ | KEY_WRITE, &hKey) == ERROR_SUCCESS)
1009 {
1010 AddItemFromMRUList(pContext, hKey);
1011 RegCloseKey(hKey);
1012 }
1013 }
1014
1015 HRESULT
1016 COpenWithMenu::SHEOW_LoadOpenWithItems(IDataObject *pdtobj)
1017 {
1018 STGMEDIUM medium;
1019 FORMATETC fmt;
1020 HRESULT hr;
1021 LPIDA pida;
1022 LPCITEMIDLIST pidl_folder;
1023 LPCITEMIDLIST pidl_child;
1024 LPCITEMIDLIST pidl;
1025 DWORD dwPath;
1026 LPWSTR szPtr;
1027 static const WCHAR szShortCut[] = { '.','l','n','k', 0 };
1028
1029 fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
1030 fmt.ptd = NULL;
1031 fmt.dwAspect = DVASPECT_CONTENT;
1032 fmt.lindex = -1;
1033 fmt.tymed = TYMED_HGLOBAL;
1034
1035 hr = pdtobj->GetData(&fmt, &medium);
1036
1037 if (FAILED(hr))
1038 {
1039 ERR("IDataObject_GetData failed with 0x%x\n", hr);
1040 return hr;
1041 }
1042
1043 /*assert(pida->cidl==1);*/
1044 pida = (LPIDA)GlobalLock(medium.hGlobal);
1045
1046 pidl_folder = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]);
1047 pidl_child = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[1]);
1048
1049 pidl = ILCombine(pidl_folder, pidl_child);
1050
1051 GlobalUnlock(medium.hGlobal);
1052 GlobalFree(medium.hGlobal);
1053
1054 if (!pidl)
1055 {
1056 ERR("no mem\n");
1057 return E_OUTOFMEMORY;
1058 }
1059 if (_ILIsDesktop(pidl) || _ILIsMyDocuments(pidl) || _ILIsControlPanel(pidl) || _ILIsNetHood(pidl) ||
1060 _ILIsBitBucket(pidl) || _ILIsDrive(pidl) || _ILIsCPanelStruct(pidl) || _ILIsFolder(pidl) || _ILIsControlPanel(pidl))
1061 {
1062 TRACE("pidl is a folder\n");
1063 SHFree((void*)pidl);
1064 return E_FAIL;
1065 }
1066
1067 if (!SHGetPathFromIDListW(pidl, szPath))
1068 {
1069 SHFree((void*)pidl);
1070 ERR("SHGetPathFromIDListW failed\n");
1071 return E_FAIL;
1072 }
1073
1074 SHFree((void*)pidl);
1075 TRACE("szPath %s\n", debugstr_w(szPath));
1076
1077 if (GetBinaryTypeW(szPath, &dwPath))
1078 {
1079 TRACE("path is a executable %x\n", dwPath);
1080 return E_FAIL;
1081 }
1082
1083 szPtr = wcsrchr(szPath, '.');
1084 if (szPtr)
1085 {
1086 if (!_wcsicmp(szPtr, szShortCut))
1087 {
1088 FIXME("pidl is a shortcut\n");
1089 return E_FAIL;
1090 }
1091 }
1092 return S_OK;
1093 }
1094
1095 HRESULT WINAPI
1096 COpenWithMenu::Initialize(LPCITEMIDLIST pidlFolder,
1097 IDataObject *pdtobj, HKEY hkeyProgID )
1098 {
1099 TRACE("This %p\n", this);
1100
1101 if (pdtobj == NULL)
1102 return E_INVALIDARG;
1103 return SHEOW_LoadOpenWithItems(pdtobj);
1104 }
1105
1106 HRESULT WINAPI SHOpenWithDialog(
1107 HWND hwndParent,
1108 const OPENASINFO *poainfo
1109 )
1110 {
1111 MSG msg;
1112 BOOL bRet;
1113 HWND hwnd;
1114
1115 if (poainfo->pcszClass == NULL && poainfo->pcszFile == NULL)
1116 return E_FAIL;
1117
1118
1119 hwnd = CreateDialogParam(shell32_hInstance, MAKEINTRESOURCE(OPEN_WITH_PROGRAMM_DLG), hwndParent, OpenWithProgrammDlg, (LPARAM)poainfo);
1120 if (hwnd == NULL)
1121 {
1122 ERR("Failed to create dialog\n");
1123 return E_FAIL;
1124 }
1125 ShowWindow(hwnd, SW_SHOWNORMAL);
1126
1127 while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
1128 {
1129 if (!IsWindow(hwnd) || !IsDialogMessage(hwnd, &msg))
1130 {
1131 TranslateMessage(&msg);
1132 DispatchMessage(&msg);
1133 }
1134 }
1135 return S_OK;
1136 }