[COM_APITEST]
[reactos.git] / rostests / apitests / com / com_apitest.c
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: COM interface test
5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
6 */
7
8 #include "com_apitest.h"
9
10 #define NDEBUG
11 #include <debug.h>
12
13 #define myskip(c, ...) ((c) ? 0 : (skip(__VA_ARGS__), 1))
14 #define mytrace(...) do { \
15 int debug = winetest_debug; \
16 winetest_debug = 1; \
17 trace(__VA_ARGS__); \
18 winetest_debug = debug; \
19 } while (0)
20
21 typedef struct _KNOWN_INTERFACE
22 {
23 const IID *iid;
24 PCSTR name;
25 } KNOWN_INTERFACE;
26 typedef const KNOWN_INTERFACE *PCKNOWN_INTERFACE;
27
28 #undef ID_NAME
29 #define ID_NAME(c) { &c, #c }
30 static KNOWN_INTERFACE KnownInterfaces[] =
31 {
32 ID_NAME(IID_IACList),
33 ID_NAME(IID_IACList2),
34 ID_NAME(IID_IADesktopP2),
35 ID_NAME(IID_IAccessible),
36 ID_NAME(IID_IAccessControl),
37 ID_NAME(IID_IAccessor),
38 ID_NAME(IID_IActiveDesktop),
39 ID_NAME(IID_IActiveDesktopP),
40 ID_NAME(IID_IAddressBarParser),
41 ID_NAME(IID_IAddressBand),
42 ID_NAME(IID_IAddressEditBox),
43 ID_NAME(IID_IAugmentedShellFolder),
44 ID_NAME(IID_IAugmentedShellFolder2),
45 ID_NAME(IID_IAutoComplete),
46 ID_NAME(IID_IAutoComplete2),
47 ID_NAME(IID_IBandNavigate),
48 ID_NAME(IID_IBandProxy),
49 ID_NAME(IID_IBandSite),
50 ID_NAME(IID_IBandSiteHelper),
51 ID_NAME(IID_IBanneredBar),
52 ID_NAME(IID_IBindCtx),
53 ID_NAME(IID_IBrowserFrameOptions),
54 ID_NAME(IID_IBrowserService),
55 ID_NAME(IID_IBrowserService2),
56 ID_NAME(IID_IBrowserService3),
57 ID_NAME(IID_IClassFactory),
58 ID_NAME(IID_IClassFactory2),
59 ID_NAME(IID_IClassFactory3),
60 ID_NAME(IID_IClientSecurity),
61 ID_NAME(IID_IComThreadingInfo),
62 ID_NAME(IID_IContext),
63 ID_NAME(IID_IContextMenu),
64 ID_NAME(IID_IContextMenu2),
65 ID_NAME(IID_IContextMenu3),
66 ID_NAME(IID_IContextMenuCB),
67 ID_NAME(IID_IContextMenuSite),
68 ID_NAME(IID_IContinue),
69 ID_NAME(IID_IDVGetEnum),
70 ID_NAME(IID_IDataObject),
71 ID_NAME(IID_IDefViewID),
72 ID_NAME(IID_IDefViewFrame),
73 ID_NAME(IID_IDefViewFrame3),
74 ID_NAME(IID_IDefViewFrameGroup),
75 ID_NAME(IID_IDefViewSafety),
76 ID_NAME(IID_IDefViewScript),
77 ID_NAME(IID_IDeskBand),
78 ID_NAME(IID_IDeskBandEx),
79 ID_NAME(IID_IDeskBar),
80 ID_NAME(IID_IDeskBarClient),
81 ID_NAME(IID_IDeskMovr),
82 ID_NAME(IID_IDispatch),
83 ID_NAME(IID_IDockingWindow),
84 ID_NAME(IID_IDockingWindowFrame),
85 ID_NAME(IID_IDockingWindowSite),
86 ID_NAME(IID_IDocViewSite),
87 ID_NAME(IID_IDragSourceHelper),
88 ID_NAME(IID_IDropSource),
89 ID_NAME(IID_IDropTarget),
90 ID_NAME(IID_IDropTargetHelper),
91 ID_NAME(IID_IEnumExtraSearch),
92 ID_NAME(IID_IEnumGUID),
93 ID_NAME(IID_IEnumIDList),
94 ID_NAME(IID_IEnumShellItems),
95 ID_NAME(IID_IEnumString),
96 ID_NAME(IID_IEnumUnknown),
97 ID_NAME(IID_IEnumVARIANT),
98 ID_NAME(IID_IErrorLog),
99 ID_NAME(IID_IExplorerToolbar),
100 ID_NAME(IID_IExtractIconA),
101 ID_NAME(IID_IExtractIconW),
102 ID_NAME(IID_IExtractImage),
103 ID_NAME(IID_IExtractImage2),
104 ID_NAME(IID_IFileDialog),
105 ID_NAME(IID_IFileDialog2),
106 ID_NAME(IID_IFileSearchBand),
107 ID_NAME(IID_IFolderBandPriv),
108 ID_NAME(IID_IFolderFilter),
109 ID_NAME(IID_IFolderFilterSite),
110 ID_NAME(IID_IFolderView),
111 ID_NAME(IID_IFolderView2),
112 ID_NAME(IID_IFolderViewOC),
113 ID_NAME(IID_IFolderViewSettings),
114 ID_NAME(IID_IGlobalFolderSettings),
115 ID_NAME(IID_IInitializeObject),
116 ID_NAME(IID_IInputObject),
117 ID_NAME(IID_IInputObjectSite),
118 ID_NAME(IID_IInternalUnknown),
119 ID_NAME(IID_IMarshal),
120 ID_NAME(IID_IMenuBand),
121 ID_NAME(IID_IMenuPopup),
122 ID_NAME(IID_IMoniker),
123 ID_NAME(IID_IMultiMonitorDockingSite),
124 ID_NAME(IID_IMultiQI),
125 ID_NAME(IID_INamespaceProxy),
126 ID_NAME(IID_INameSpaceTreeControl),
127 ID_NAME(IID_INSCTree),
128 ID_NAME(IID_INSCTree2),
129 ID_NAME(IID_IObjMgr),
130 ID_NAME(IID_IObjectSafety),
131 ID_NAME(IID_IObjectWithSite),
132 ID_NAME(IID_IOleCommandTarget),
133 ID_NAME(IID_IOleInPlaceActiveObject),
134 ID_NAME(IID_IOleInPlaceFrame),
135 ID_NAME(IID_IOleInPlaceObject),
136 ID_NAME(IID_IOleInPlaceObjectWindowless),
137 ID_NAME(IID_IOleInPlaceSite),
138 ID_NAME(IID_IOleInPlaceSiteEx),
139 ID_NAME(IID_IOleInPlaceSiteWindowless),
140 ID_NAME(IID_IOleInPlaceUIWindow),
141 ID_NAME(IID_IOleObject),
142 ID_NAME(IID_IOleWindow),
143 ID_NAME(IID_IPersist),
144 ID_NAME(IID_IPersistFile),
145 ID_NAME(IID_IPersistFolder),
146 ID_NAME(IID_IPersistFolder2),
147 ID_NAME(IID_IPersistFolder3),
148 ID_NAME(IID_IPersistHistory),
149 ID_NAME(IID_IPersistIDList),
150 ID_NAME(IID_IPersistMemory),
151 ID_NAME(IID_IPersistPropertyBag),
152 ID_NAME(IID_IPersistPropertyBag2),
153 ID_NAME(IID_IPersistStorage),
154 ID_NAME(IID_IPersistStream),
155 ID_NAME(IID_IPersistStreamInit),
156 ID_NAME(IID_IProgressDialog),
157 ID_NAME(IID_IPropertyBag),
158 ID_NAME(IID_IPropertyBag2),
159 ID_NAME(IID_IQueryAssociations),
160 ID_NAME(IID_IQueryInfo),
161 ID_NAME(IID_IRegTreeOptions),
162 ID_NAME(IID_IRunnableObject),
163 ID_NAME(IID_IServerSecurity),
164 ID_NAME(IID_IServiceProvider),
165 ID_NAME(IID_ISFHelper),
166 ID_NAME(IID_IShellBrowser),
167 ID_NAME(IID_IShellBrowserService),
168 ID_NAME(IID_IShellChangeNotify),
169 ID_NAME(IID_IShellDesktopTray),
170 ID_NAME(IID_IShellDispatch),
171 ID_NAME(IID_IShellDispatch2),
172 ID_NAME(IID_IShellDispatch3),
173 ID_NAME(IID_IShellDispatch4),
174 ID_NAME(IID_IShellDispatch5),
175 ID_NAME(IID_IShellExtInit),
176 ID_NAME(IID_IShellPropSheetExt),
177 ID_NAME(IID_IShellIconOverlayIdentifier),
178 ID_NAME(IID_IShellFolder),
179 ID_NAME(IID_IShellFolder2),
180 ID_NAME(IID_IShellFolderBand),
181 ID_NAME(IID_IShellFolderView),
182 ID_NAME(IID_IShellFolderViewCB),
183 ID_NAME(IID_IShellFolderViewDual),
184 ID_NAME(IID_IShellFolderViewDual2),
185 ID_NAME(IID_IShellIcon),
186 ID_NAME(IID_IShellItem),
187 ID_NAME(IID_IShellItem2),
188 ID_NAME(IID_IShellItemArray),
189 ID_NAME(IID_IShellItemFilter),
190 ID_NAME(IID_IShellLinkA),
191 ID_NAME(IID_IShellLinkDual),
192 ID_NAME(IID_IShellLinkDual2),
193 ID_NAME(IID_IShellLinkW),
194 ID_NAME(IID_IShellMenu),
195 ID_NAME(IID_IShellMenu2),
196 ID_NAME(IID_IShellMenuAcc),
197 ID_NAME(IID_IShellMenuCallback),
198 ID_NAME(IID_IShellNameSpace),
199 ID_NAME(IID_IShellService),
200 ID_NAME(IID_IShellView),
201 ID_NAME(IID_IShellView2),
202 ID_NAME(IID_IShellView3),
203 ID_NAME(IID_IShellWindows),
204 ID_NAME(IID_IStorage),
205 ID_NAME(IID_IStream),
206 ID_NAME(IID_ISurrogate),
207 ID_NAME(IID_ISynchronize),
208 ID_NAME(IID_ISynchronizeContainer),
209 ID_NAME(IID_ISynchronizeEvent),
210 ID_NAME(IID_ISynchronizeHandle),
211 ID_NAME(IID_ITaskbarList),
212 ID_NAME(IID_ITaskbarList2),
213 ID_NAME(IID_ITrackShellMenu),
214 ID_NAME(IID_ITrayPriv),
215 ID_NAME(IID_ITrayPriv2),
216 ID_NAME(IID_IUnknown),
217 ID_NAME(IID_IViewObject),
218 ID_NAME(IID_IViewObject2),
219 ID_NAME(IID_IViewObjectEx),
220 ID_NAME(IID_IWinEventHandler),
221
222 ID_NAME(IID_DFConstraint),
223 ID_NAME(DIID_DShellFolderViewEvents),
224
225 ID_NAME(IID_CDefView),
226 ID_NAME(IID_Folder),
227 ID_NAME(IID_Folder2),
228 ID_NAME(IID_Folder3),
229 ID_NAME(IID_FolderItem),
230 ID_NAME(IID_FolderItem2),
231 ID_NAME(IID_FolderItems),
232 ID_NAME(IID_FolderItems2),
233 ID_NAME(IID_FolderItems3),
234 ID_NAME(IID_FolderItemVerb),
235 ID_NAME(IID_FolderItemVerbs),
236
237 ID_NAME(CLSID_ShellDesktop)
238 };
239 static const INT KnownInterfaceCount = RTL_NUMBER_OF(KnownInterfaces);
240
241 static
242 PCKNOWN_INTERFACE
243 FindInterface(
244 _In_ const IID *iid)
245 {
246 INT i;
247
248 for (i = 0; i < KnownInterfaceCount; i++)
249 if (IsEqualIID(KnownInterfaces[i].iid, iid))
250 return &KnownInterfaces[i];
251 ASSERT(i != KnownInterfaceCount);
252 return NULL;
253 }
254
255 static
256 BOOLEAN
257 IsInterfaceExpected(
258 _In_ PCCLASS_AND_INTERFACES class,
259 _In_ const IID *iid)
260 {
261 INT i;
262
263 for (i = 0; class->ifaces[i].iid; i++)
264 if (IsEqualIID(class->ifaces[i].iid, iid))
265 return TRUE;
266 return FALSE;
267 }
268
269 #define INTF_NOT_EXPOSED LONG_MAX
270 static
271 LONG
272 GetInterfaceOffset(
273 _In_ PUNKNOWN pUnk,
274 _In_ const IID *iid)
275 {
276 HRESULT hr;
277 PVOID pObj;
278 PUNKNOWN pUnk2;
279 LONG offset;
280
281 hr = IUnknown_QueryInterface(pUnk, iid, &pObj);
282 ok(hr == S_OK || hr == E_NOINTERFACE, "IUnknown::QueryInterface returned 0x%lx\n", hr);
283 if (FAILED(hr))
284 return INTF_NOT_EXPOSED;
285
286 pUnk2 = pObj;
287 offset = (LONG_PTR)pObj - (LONG_PTR)pUnk;
288 IUnknown_Release(pUnk2);
289 return offset;
290 }
291
292 static
293 VOID
294 TestModuleInterfaces(
295 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces,
296 _In_ INT ExpectedInterfaceCount)
297 {
298 HRESULT hr;
299 PVOID pObj;
300 PUNKNOWN pUnk;
301 INT iClass, iIntf;
302 PCCLASS_AND_INTERFACES class;
303
304 for (iClass = 0; iClass < ExpectedInterfaceCount; iClass++)
305 {
306 class = &ExpectedInterfaces[iClass];
307 hr = CoCreateInstance(class->clsid,
308 NULL,
309 CLSCTX_INPROC_SERVER,
310 &IID_IUnknown,
311 &pObj);
312 ok(hr == S_OK, "CoCreateInstance failed. hr=0x%lx\n", hr);
313 if (FAILED(hr))
314 {
315 skip("Failed to instantiate %s.\n", class->name);
316 continue;
317 }
318
319 pUnk = pObj;
320
321 /* Check that all expected interfaces are present and have the right offset */
322 for (iIntf = 0; class->ifaces[iIntf].iid; iIntf++)
323 {
324 PCKNOWN_INTERFACE iface = FindInterface(class->ifaces[iIntf].iid);
325 LONG offset = GetInterfaceOffset(pUnk, iface->iid);
326 if (offset == INTF_NOT_EXPOSED)
327 ok(0, "%s is missing %s (offset %ld)\n", class->name, iface->name, class->ifaces[iIntf].offset);
328 else if (class->ifaces[iIntf].offset != FARAWY)
329 {
330 #ifdef FAIL_WRONG_OFFSET
331 ok(offset == class->ifaces[iIntf].offset, "%s, %s offset is %ld, expected %ld\n", class->name, iface->name, offset, class->ifaces[iIntf].offset);
332 #else
333 if (offset != class->ifaces[iIntf].offset)
334 mytrace("%s, %s offset is %ld, expected %ld\n", class->name, iface->name, offset, class->ifaces[iIntf].offset);
335 #endif
336 }
337 }
338
339 /* Check that none other than the expected interfaces are present */
340 for (iIntf = 0; iIntf < KnownInterfaceCount; iIntf++)
341 {
342 PCKNOWN_INTERFACE iface = &KnownInterfaces[iIntf];
343 LONG offset;
344 if (IsInterfaceExpected(class, iface->iid))
345 continue;
346 offset = GetInterfaceOffset(pUnk, iface->iid);
347 #ifdef GENERATE_TABLE_ENTRIES
348 ok(offset == INTF_NOT_EXPOSED, "%s: { %s%x, &%s },\n", class->name, offset < 0 ? "-" : "", offset < 0 ? -offset : offset, iface->name);
349 #else
350 ok(offset == INTF_NOT_EXPOSED, "%s exposes %s (offset %ld), but shouldn't\n", class->name, iface->name, offset);
351 #endif
352 }
353
354 // TODO: do some aggregation
355
356 IUnknown_Release(pUnk);
357 }
358 }
359
360 static
361 VOID
362 TestModuleRegistry(
363 _In_ PCWSTR ModuleName,
364 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces,
365 _In_ INT ExpectedInterfaceCount)
366 {
367 INT iClass;
368 PCCLASS_AND_INTERFACES class;
369 HKEY hKeyClasses;
370 LONG result;
371
372 result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID", 0, KEY_ENUMERATE_SUB_KEYS, &hKeyClasses);
373 ok(result == NO_ERROR, "Failed to open classes key, error %lu\n", result);
374 if (!myskip(result == NO_ERROR, "No classes key\n"))
375 {
376 for (iClass = 0; iClass < ExpectedInterfaceCount; iClass++)
377 {
378 HKEY hKey;
379 HKEY hKeyServer;
380 NTSTATUS status;
381 UNICODE_STRING clsid;
382 DWORD type;
383 WCHAR data[100];
384 DWORD dataSize;
385 PCWSTR expectedThreadingModel;
386
387 class = &ExpectedInterfaces[iClass];
388 status = RtlStringFromGUID(class->clsid, &clsid);
389 ok(status == STATUS_SUCCESS, "Failed to convert guid to string for %s, status %lx\n", class->name, status);
390 if (myskip(NT_SUCCESS(status), "No guid string\n"))
391 continue;
392
393 result = RegOpenKeyEx(hKeyClasses, clsid.Buffer, 0, KEY_ENUMERATE_SUB_KEYS, &hKey);
394 ok(result == NO_ERROR, "Failed to open key for %s, error %lu\n", class->name, result);
395 RtlFreeUnicodeString(&clsid);
396 if (myskip(result == NO_ERROR, "No key\n"))
397 continue;
398
399 result = RegOpenKeyEx(hKey, L"InProcServer32", 0, KEY_QUERY_VALUE, &hKeyServer);
400 ok(result == NO_ERROR, "Failed to open key for %s, error %lu\n", class->name, result);
401 RegCloseKey(hKey);
402 if (myskip(result == NO_ERROR, "No key\n"))
403 continue;
404
405 dataSize = sizeof(data);
406 result = RegQueryValueEx(hKeyServer, NULL, NULL, &type, (PBYTE)data, &dataSize);
407 ok(result == NO_ERROR, "Failed to query value for %s, error %lu\n", class->name, result);
408 if (!myskip(result == NO_ERROR, "No module name\n"))
409 {
410 ok(type == REG_SZ || type == REG_EXPAND_SZ, "type %lu for %s\n", type, class->name);
411 ok(dataSize % sizeof(WCHAR) == 0, "size %lu for %s\n", dataSize, class->name);
412 ok(dataSize <= sizeof(data), "size %lu for %s\n", dataSize, class->name);
413 ok(data[dataSize / sizeof(WCHAR) - 1] == UNICODE_NULL, "Not null terminated for %s\n", class->name);
414 // TODO: Use SearchPath (or assume everything's in system32) and do a proper full path compare
415 PathStripPathW(data);
416 PathRemoveExtensionW(data);
417 ok(!wcsicmp(data, ModuleName), "Server is %ls, expected %ls for %s\n", data, ModuleName, class->name);
418 }
419
420 dataSize = sizeof(data);
421 result = RegQueryValueEx(hKeyServer, L"ThreadingModel", NULL, &type, (PBYTE)data, &dataSize);
422 ok(result == NO_ERROR, "Failed to query value for %s, error %lu\n", class->name, result);
423 if (!myskip(result == NO_ERROR, "No ThreadingModel\n"))
424 {
425 ok(type == REG_SZ || type == REG_EXPAND_SZ, "type %lu for %s\n", type, class->name);
426 ok(dataSize % sizeof(WCHAR) == 0, "size %lu for %s\n", dataSize, class->name);
427 ok(dataSize <= sizeof(data), "size %lu for %s\n", dataSize, class->name);
428 ok(data[dataSize / sizeof(WCHAR) - 1] == UNICODE_NULL, "Not null terminated for %s\n", class->name);
429 expectedThreadingModel = class->ThreadingModel;
430 if (!expectedThreadingModel)
431 expectedThreadingModel = L"Apartment";
432 ok(!wcsicmp(data, expectedThreadingModel), "Server is %ls, expected %ls for %s\n", data, expectedThreadingModel, class->name);
433 }
434
435 RegCloseKey(hKeyServer);
436 }
437 RegCloseKey(hKeyClasses);
438 }
439 }
440
441 static
442 VOID
443 TestManualInstantiation(
444 _In_ PCWSTR ModuleName,
445 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces,
446 _In_ INT ExpectedInterfaceCount)
447 {
448 INT iClass;
449 PCCLASS_AND_INTERFACES class;
450 HRESULT (__stdcall *DllGetClassObject)(REFCLSID, REFIID, PVOID *);
451
452 DllGetClassObject = (PVOID)GetProcAddress(GetModuleHandle(ModuleName), "DllGetClassObject");
453 ok(DllGetClassObject != NULL, "DllGetClassObject not found in %ls, error %lu\n", ModuleName, GetLastError());
454 if (myskip(DllGetClassObject != NULL, "No DllGetClassObject\n"))
455 return;
456
457 for (iClass = 0; iClass < ExpectedInterfaceCount; iClass++)
458 {
459 PVOID pv;
460 HRESULT hr;
461 class = &ExpectedInterfaces[iClass];
462 hr = DllGetClassObject(class->clsid, &IID_IClassFactory, &pv);
463 ok(hr == S_OK, "DllGetClassObject failed for %s, hr = 0x%lx\n", class->name, hr);
464 if (!myskip(SUCCEEDED(hr), "No class factory\n"))
465 {
466 IClassFactory *pCF = pv;
467 hr = IClassFactory_CreateInstance(pCF, NULL, &IID_IUnknown, &pv);
468 ok(hr == S_OK, "IClassFactory::CreateInstance failed for %s, hr = 0x%lx\n", class->name, hr);
469 if (!myskip(SUCCEEDED(hr), "No instance\n"))
470 {
471 IUnknown *pUnk = pv;
472 IUnknown_Release(pUnk);
473 }
474 IClassFactory_Release(pCF);
475 }
476 }
477 }
478
479 VOID
480 TestClasses(
481 _In_ PCWSTR ModuleName,
482 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces,
483 _In_ INT ExpectedInterfaceCount)
484 {
485 HRESULT hr;
486
487 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
488 ok(hr == S_OK, "CoInitializeEx failed. hr=0x%lx\n", hr);
489 if (myskip(SUCCEEDED(hr), "Failed to initialize COM. Cannot perform tests\n"))
490 return;
491
492 TestModuleInterfaces(ExpectedInterfaces, ExpectedInterfaceCount);
493 TestModuleRegistry(ModuleName, ExpectedInterfaces, ExpectedInterfaceCount);
494 TestManualInstantiation(ModuleName, ExpectedInterfaces, ExpectedInterfaceCount);
495
496 CoUninitialize();
497 }