1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         Test for shell menu objects
5  * PROGRAMMERS:     Giannis Adamopoulos
6  */
7 
8 #include "shelltest.h"
9 
10 #include <shlwapi.h>
11 #include <unknownbase.h>
12 #include <shlguid_undoc.h>
13 
14 #define test_S_OK(hres, message) ok(hres == S_OK, "%s (0x%lx instead of S_OK)\n",message, hResult);
15 #define test_HRES(hres, hresExpected, message) ok(hres == hresExpected, "%s (0x%lx instead of 0x%lx)\n",message, hResult,hresExpected);
16 
17 BOOL CheckWindowClass(HWND hwnd, PCWSTR className)
18 {
19     ULONG size = (lstrlenW(className) + 1)* sizeof(WCHAR);
20     PWCHAR buffer = (PWCHAR)malloc(size);
21     if (GetClassNameW(hwnd, buffer, size ) == 0)
22     {
23         free(buffer);
24         return FALSE;
25     }
26     int res = wcscmp(buffer, className);
27     free(buffer);
28     return res == 0;
29 }
30 
31 class CDummyWindow : public CUnknownBase<IOleWindow>
32 {
33 protected:
34     HWND m_hwnd;
35 
36     const QITAB* GetQITab()
37     {
38         static const QITAB tab[] = {{ &IID_IOleWindow, OFFSETOFCLASS(IOleWindow, CDummyWindow) }, {0}};
39         return tab;
40     }
41 
42 public:
43     CDummyWindow(HWND hwnd)
44         :CUnknownBase( true, 0 )
45     {
46         m_hwnd = hwnd;
47     }
48 
49    HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd)
50    {
51        *phwnd = m_hwnd;
52        return S_OK;
53    }
54 
55    HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode)
56    {
57        return S_OK;
58    }
59 };
60 
61 BOOL CreateCShellMenu(IShellMenu** shellMenu, IDockingWindow** dockingMenu, IObjectWithSite **menuWithSite)
62 {
63     HRESULT hResult;
64     hResult = CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_IShellMenu, reinterpret_cast<void **>(shellMenu));
65     test_S_OK(hResult, "Failed to instantiate CLSID_MenuBand");
66     if (!shellMenu) return FALSE;
67 
68     hResult = (*shellMenu)->QueryInterface(IID_IDockingWindow, reinterpret_cast<void **>(dockingMenu));
69     test_S_OK(hResult, "Failed to query IID_IDockingWindow");
70     hResult = (*shellMenu)->QueryInterface(IID_IObjectWithSite, reinterpret_cast<void **>(menuWithSite));
71     test_S_OK(hResult, "Failed to query IID_IObjectWithSite");
72     if (!dockingMenu || !menuWithSite) return FALSE;
73     return TRUE;
74 }
75 
76 
77 void test_CShellMenu_params()
78 {
79     HRESULT hResult;
80     IShellMenu* shellMenu;
81     IDockingWindow* dockingMenu;
82     IObjectWithSite* menuWithSite;
83 
84     IShellMenuCallback *psmc;
85     UINT uId;
86     UINT uIdAncestor;
87     DWORD dwFlags;
88     HWND hwndToolbar;
89     HMENU hmenu;
90     HWND hwndOwner;
91     DWORD menuFlagss;
92     IShellFolder *shellFolder;
93 
94     if (!CreateCShellMenu(&shellMenu, &dockingMenu, &menuWithSite))
95     {
96         skip("failed to create CShellMenuObject\n");
97         return;
98     }
99 
100     hResult = shellMenu->Initialize(NULL, 11, 22, 0xdeadbeef);
101     test_S_OK(hResult, "Initialize failed");
102 
103     hResult = shellMenu->GetMenuInfo(&psmc, &uId, &uIdAncestor, &dwFlags);
104     test_S_OK(hResult, "GetMenuInfo failed");
105     ok (psmc == NULL, "wrong psmc\n");
106     ok (uId == 11, "wrong uid\n");
107     ok (uIdAncestor == 22, "wrong uIdAncestor\n");
108     ok (dwFlags == 0xdeadbeef, "wrong dwFlags\n");
109 
110     hResult = shellMenu->Initialize(NULL, 0, ANCESTORDEFAULT, SMINIT_TOPLEVEL|SMINIT_VERTICAL);
111     test_S_OK(hResult, "Initialize failed");
112 
113     hResult = dockingMenu->GetWindow(&hwndToolbar);
114     test_HRES(hResult, E_FAIL, "GetWindow should fail");
115 
116     hResult = shellMenu->GetMenu(&hmenu, &hwndOwner, &menuFlagss);
117     test_HRES(hResult, E_FAIL, "GetMenu should fail");
118 
119     hmenu = CreatePopupMenu();
120     hResult = shellMenu->SetMenu(hmenu, NULL, 0);
121     test_S_OK(hResult, "SetMenu failed");
122 
123     hwndToolbar = (HWND)UlongToPtr(0xdeadbeef);
124     hResult = dockingMenu->GetWindow(&hwndToolbar);
125     test_S_OK(hResult, "GetWindow failed");
126     ok (hwndToolbar == NULL, "Expected NULL window\n");
127 
128     hResult = shellMenu->SetMenu(NULL, NULL, 0);
129     test_S_OK(hResult, "SetMenu failed");
130 
131     hResult = shellMenu->GetMenu(&hmenu, &hwndOwner, &menuFlagss);
132     test_S_OK(hResult, "GetMenu failed");
133     ok (hmenu == NULL, "Got a menu\n");
134 
135     hResult = dockingMenu->GetWindow(&hwndToolbar);
136     test_S_OK(hResult, "GetWindow failed");
137 
138     hResult = SHGetDesktopFolder(&shellFolder);
139     test_S_OK(hResult, "SHGetDesktopFolder failed");
140 
141     hResult = shellMenu->SetShellFolder(shellFolder, NULL, 0, 0);
142     test_S_OK(hResult, "SetShellFolder failed");
143 
144     hResult = shellMenu->SetShellFolder(NULL, NULL, 0, 0);
145     test_HRES(hResult, E_INVALIDARG, "SetShellFolder should fail");
146 
147     hwndToolbar = (HWND)UlongToHandle(0xdeadbeef);
148     hResult = dockingMenu->GetWindow(&hwndToolbar);
149     test_S_OK(hResult, "GetWindow failed");
150     ok (hwndToolbar == NULL, "Expected NULL window\n");
151 
152     hResult = dockingMenu->ShowDW(TRUE);
153     test_HRES(hResult, S_FALSE, "ShowDW should fail");
154 
155     menuWithSite->Release();
156     dockingMenu->Release();
157     shellMenu->Release();
158 }
159 
160 void test_CShellMenu()
161 {
162     HRESULT hResult;
163     IShellMenu* shellMenu;
164     IDockingWindow* dockingMenu;
165     IShellFolder *shellFolder;
166     IObjectWithSite *menuWithSite;
167     HWND hwndToolbar;
168 
169     HWND hWndParent = CreateWindowExW(0, L"EDIT", L"miau", 0, CW_USEDEFAULT, CW_USEDEFAULT,
170         CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL);
171     CDummyWindow* dummyWindow = new CDummyWindow(hWndParent);
172 
173     if (!CreateCShellMenu(&shellMenu, &dockingMenu, &menuWithSite))
174     {
175         skip("failed to create CShellMenuObject\n");
176         delete dummyWindow;
177         return;
178     }
179 
180     hResult = SHGetDesktopFolder(&shellFolder);
181     test_S_OK(hResult, "SHGetDesktopFolder failed");
182 
183     hResult = shellMenu->Initialize(NULL, 0, ANCESTORDEFAULT, SMINIT_TOPLEVEL|SMINIT_VERTICAL);
184     test_S_OK(hResult, "Initialize failed");
185 
186     hResult = shellMenu->SetShellFolder(shellFolder, NULL, NULL, 0);
187     test_S_OK(hResult, "SetShellFolder failed");
188 
189     hResult = menuWithSite->SetSite(dummyWindow);
190     test_S_OK(hResult, "SetSite failed");
191 
192     hResult = dockingMenu->GetWindow(&hwndToolbar);
193     test_S_OK(hResult, "GetWindow failed");
194     ok(hwndToolbar != NULL, "GetWindow should return a window\n");
195 
196     HWND hwndRealParent = GetParent(hwndToolbar);
197     ok(GetParent(hwndRealParent) == hWndParent, "Wrong parent\n");
198     ok(CheckWindowClass(hwndToolbar, L"ToolbarWindow32"), "Wrong class\n");
199     ok(CheckWindowClass(hwndRealParent, L"SysPager"), "Wrong class\n");
200 
201     menuWithSite->Release();
202     dockingMenu->Release();
203     shellMenu->Release();
204     ok(!IsWindow(hwndToolbar), "The toolbar window should not exist\n");
205 
206     DestroyWindow(hWndParent);
207 }
208 
209 /* The folowing struct holds info about the order callbacks are called */
210 /* By passing different arrays of results to CMenuCallback, we can test different sequenses of callbacks */
211    struct _test_info{
212        int iTest;
213        UINT uMsg;};
214 
215 class CMenuCallback : public CUnknownBase<IShellMenuCallback>
216 {
217 protected:
218     int m_iTest;
219     int m_iCallback;
220     struct _test_info *m_results;
221     int m_testsCount;
222 
223     const QITAB* GetQITab()
224     {
225         static const QITAB tab[] = {{ &IID_IShellMenuCallback, OFFSETOFCLASS(IShellMenuCallback, CMenuCallback) }, {0}};
226         return tab;
227     }
228 
229 public:
230     CMenuCallback(struct _test_info *testResults, int testsCount)
231         :CUnknownBase( true, 0 )
232     {
233         m_iTest = 0;
234         m_iCallback = 0;
235         m_results = testResults;
236         m_testsCount = testsCount;
237     }
238 
239    void SetTest(int i)
240    {
241        m_iTest = i;
242    }
243 
244    HRESULT STDMETHODCALLTYPE CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
245    {
246        /*trace ("callback type %d\n", uMsg);*/
247 
248        /*
249         * it seems callback 0x10000000 is called for every item added so
250         * we will ignore consecutive callbacks of this type
251         * Note: this callback is invoked by shell32.dll!CMenuSFToolbar::_FilterPidl
252         */
253        if (uMsg == 0x10000000 && m_results[m_iCallback-1].uMsg == 0x10000000)
254        {
255            return S_OK;
256        }
257 
258        m_iCallback++;
259        if (m_iCallback > m_testsCount)
260        {
261            ok(0, "Got more callbacks than expected! (%d not %d). uMsg: %d\n", m_iCallback, m_testsCount, uMsg);
262            return S_OK;
263        }
264 
265        struct _test_info *result = &m_results[m_iCallback-1];
266 
267        ok(psmd != NULL, "Got NULL psmd\n");
268        ok(m_iTest == result->iTest, "Wrong test number (%d not %d)\n", m_iTest, result->iTest);
269        ok(result->uMsg == uMsg, "%d: Got wrong uMsg (%d instead of %d)\n", m_iCallback, uMsg, result->uMsg);
270 
271        if(uMsg == SMC_CREATE)
272        {
273            ok(psmd->dwFlags == 0, "wrong dwFlags\n");
274            ok(psmd->dwMask == 0, "wrong dwMask\n");
275            ok(psmd->hmenu == 0, "wrong hmenu\n");
276            ok(psmd->hwnd == 0, "wrong hwnd\n");
277            ok(psmd->punk != NULL, "punk is null\n");
278        }
279 
280        if (uMsg == SMC_GETSFOBJECT)
281        {
282            ok(psmd->psf != 0, "wrong dwFlags\n");
283        }
284 
285        return S_FALSE;
286    }
287 };
288 
289 void test_CShellMenu_callbacks(IShellFolder *shellFolder, HMENU hmenu)
290 {
291     HRESULT hResult;
292     IShellMenu* shellMenu;
293     IDockingWindow* dockingMenu;
294     IObjectWithSite *menuWithSite;
295     CMenuCallback *callback;
296 
297     HWND hWndParent = CreateWindowExW(0, L"EDIT", L"miau", 0, CW_USEDEFAULT, CW_USEDEFAULT,
298         CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL);
299     CDummyWindow* dummyWindow = new CDummyWindow(hWndParent);
300     ShowWindow(hWndParent, SW_SHOW);
301 
302     if (!CreateCShellMenu(&shellMenu, &dockingMenu, &menuWithSite))
303     {
304         skip("failed to create CShellMenuObject\n");
305         delete dummyWindow;
306         return;
307     }
308 
309     struct _test_info cbtest_info[] =  { {1, SMC_CREATE},
310                                          {2, SMC_GETSFOBJECT},
311                                          {3, 0x31},
312                                          {4, SMC_INITMENU},
313                                          {4, 53},
314                                          {4, 19},
315                                          {4, 0x10000000},
316                                          {4, SMC_NEWITEM},
317                                          {4, 20},
318                                          {4, 19},
319                                          {4, 6},
320                                          {4, 20},
321                                          {4, 8},
322                                          {4, 24},
323                                          {4, 5},
324                                          {4, 5},
325                                          {4, 5}};
326 
327     callback = new CMenuCallback(cbtest_info,18);
328 
329     callback->SetTest(1);
330     hResult = shellMenu->Initialize(callback, 0,ANCESTORDEFAULT, SMINIT_TOPLEVEL|SMINIT_VERTICAL);
331     test_S_OK(hResult, "Initialize failed");
332 
333     callback->SetTest(2);
334     hResult = shellMenu->SetShellFolder(shellFolder, NULL, NULL, 0);
335     test_S_OK(hResult, "SetShellFolder failed");
336 
337     callback->SetTest(3);
338     hResult = shellMenu->SetMenu(hmenu, hWndParent, SMSET_TOP);
339     test_S_OK(hResult, "SetMenu failed");
340 
341     hResult = menuWithSite->SetSite(dummyWindow);
342     test_S_OK(hResult, "SetSite failed");
343 
344     callback->SetTest(4);
345     hResult = dockingMenu->ShowDW(TRUE);
346     test_HRES(hResult, S_FALSE, "ShowDW failed");
347 }
348 
349 void test_CShellMenu_with_DeskBar(IShellFolder *shellFolder, HMENU hmenu)
350 {
351     HRESULT hResult;
352     IShellMenu* shellMenu;
353     IDockingWindow* dockingMenu;
354     IObjectWithSite *menuWithSite;
355     IMenuPopup* menuPopup;
356     IBandSite* bandSite;
357 
358     /* Create the tree objects and query the nescesary interfaces */
359     BOOL bCreated = CreateCShellMenu(&shellMenu, &dockingMenu, &menuWithSite);
360     hResult = CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER, IID_IMenuPopup, reinterpret_cast<void **>(&menuPopup));
361     test_S_OK(hResult, "Failed to instantiate CLSID_MenuDeskBar");
362     hResult = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_IBandSite, reinterpret_cast<void **>(&bandSite));
363     test_S_OK(hResult, "Failed to instantiate CLSID_MenuBandSite");
364     if (!bCreated || !menuPopup || !bandSite)
365     {
366         skip("failed to create MenuBandSite object\n");
367         return;
368     }
369 
370     /* Create the popup menu */
371     hResult = shellMenu->Initialize(NULL, 0, ANCESTORDEFAULT, SMINIT_TOPLEVEL|SMINIT_VERTICAL);
372     test_S_OK(hResult, "Initialize failed");
373     hResult = shellMenu->SetMenu( hmenu, NULL, SMSET_TOP);
374     test_S_OK(hResult, "SetMenu failed");
375     hResult = menuPopup->SetClient(bandSite);
376     test_S_OK(hResult, "SetClient failed");
377     hResult = bandSite->AddBand(shellMenu);
378     test_S_OK(hResult, "AddBand failed");
379 
380     /* Show the popum menu */
381     POINTL p = {10,10};
382     hResult = menuPopup->Popup(&p, NULL, 0);
383     test_HRES(hResult, S_FALSE, "Popup failed");
384 
385     HWND hWndToolbar, hWndToplevel;
386 
387     /* Ensure that the created windows are correct */
388     hResult = dockingMenu->GetWindow(&hWndToolbar);
389     test_S_OK(hResult, "GetWindow failed");
390     ok(hWndToolbar != NULL, "GetWindow should return a window\n");
391 
392     hResult = menuPopup->GetWindow(&hWndToplevel);
393     test_S_OK(hResult, "GetWindow failed");
394     ok(hWndToolbar != NULL, "GetWindow should return a window\n");
395 
396     HWND hwndRealParent = GetParent(hWndToolbar);
397     ok(GetParent(hwndRealParent) == hWndToplevel, "Wrong parent\n");
398     ok(CheckWindowClass(hWndToolbar, L"ToolbarWindow32"), "Wrong class\n");
399     ok(CheckWindowClass(hwndRealParent, L"MenuSite"), "Wrong class\n");
400     ok(CheckWindowClass(hWndToplevel, L"BaseBar"), "Wrong class\n");
401 
402     ok(GetAncestor (hWndToplevel, GA_PARENT) == GetDesktopWindow(), "Expected the BaseBar window to be top level\n");
403 }
404 
405 START_TEST(menu)
406 {
407     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
408 
409     IShellFolder *shellFolder;
410     HRESULT hResult;
411     hResult = SHGetDesktopFolder(&shellFolder);
412     test_S_OK(hResult, "SHGetDesktopFolder failed");
413 
414     HMENU hSubMenu = CreatePopupMenu();
415     AppendMenuW(hSubMenu, 0,0, L"Submenu item1");
416     AppendMenuW(hSubMenu, 0,0, L"Submenu item2");
417     HMENU hmenu = CreatePopupMenu();
418     AppendMenuW(hmenu, 0,0, L"test");
419     AppendMenuW(hmenu, 0,1, L"test1");
420     MENUITEMINFOW iteminfo = {0};
421     iteminfo.cbSize = sizeof(iteminfo);
422     iteminfo.hSubMenu = hSubMenu;
423     iteminfo.fMask = MIIM_STRING | MIIM_SUBMENU;
424     iteminfo.dwTypeData = const_cast<LPWSTR>(L"submenu");
425     iteminfo.cch = 7;
426     InsertMenuItemW(hmenu, 0, TRUE, &iteminfo);
427 
428     test_CShellMenu_params();
429     test_CShellMenu();
430     test_CShellMenu_callbacks(shellFolder, hmenu);
431     test_CShellMenu_with_DeskBar(shellFolder, hmenu);
432 }
433 
434