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>
8 #include "com_apitest.h"
13 #define myskip(c, ...) ((c) ? 0 : (skip(__VA_ARGS__), 1))
14 #define mytrace(...) do { \
15 int debug = winetest_debug; \
18 winetest_debug = debug; \
21 typedef struct _KNOWN_INTERFACE
26 typedef const KNOWN_INTERFACE
*PCKNOWN_INTERFACE
;
29 #define ID_NAME(c) { &c, #c }
30 static KNOWN_INTERFACE KnownInterfaces
[] =
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_IAutoComplete
),
44 ID_NAME(IID_IAutoComplete2
),
45 ID_NAME(IID_IBandNavigate
),
46 ID_NAME(IID_IBandProxy
),
47 ID_NAME(IID_IBandSite
),
48 ID_NAME(IID_IBandSiteHelper
),
49 ID_NAME(IID_IBanneredBar
),
50 ID_NAME(IID_IBindCtx
),
51 ID_NAME(IID_IBrowserFrameOptions
),
52 ID_NAME(IID_IBrowserService
),
53 ID_NAME(IID_IBrowserService2
),
54 ID_NAME(IID_IBrowserService3
),
55 ID_NAME(IID_IClassFactory
),
56 ID_NAME(IID_IClassFactory2
),
57 ID_NAME(IID_IClassFactory3
),
58 ID_NAME(IID_IClientSecurity
),
59 ID_NAME(IID_IComThreadingInfo
),
60 ID_NAME(IID_IContext
),
61 ID_NAME(IID_IContextMenu
),
62 ID_NAME(IID_IContextMenu2
),
63 ID_NAME(IID_IContextMenu3
),
64 ID_NAME(IID_IContextMenuCB
),
65 ID_NAME(IID_IContextMenuSite
),
66 ID_NAME(IID_IContinue
),
67 ID_NAME(IID_IDVGetEnum
),
68 ID_NAME(IID_IDataObject
),
69 ID_NAME(IID_IDefViewID
),
70 ID_NAME(IID_IDefViewFrame
),
71 ID_NAME(IID_IDefViewFrame3
),
72 ID_NAME(IID_IDefViewFrameGroup
),
73 ID_NAME(IID_IDefViewSafety
),
74 ID_NAME(IID_IDefViewScript
),
75 ID_NAME(IID_IDeskBand
),
76 ID_NAME(IID_IDeskBandEx
),
77 ID_NAME(IID_IDeskBar
),
78 ID_NAME(IID_IDeskBarClient
),
79 ID_NAME(IID_IDeskMovr
),
80 ID_NAME(IID_IDispatch
),
81 ID_NAME(IID_IDockingWindow
),
82 ID_NAME(IID_IDockingWindowFrame
),
83 ID_NAME(IID_IDockingWindowSite
),
84 ID_NAME(IID_IDocViewSite
),
85 ID_NAME(IID_IDragSourceHelper
),
86 ID_NAME(IID_IDropSource
),
87 ID_NAME(IID_IDropTarget
),
88 ID_NAME(IID_IDropTargetHelper
),
89 ID_NAME(IID_IEnumExtraSearch
),
90 ID_NAME(IID_IEnumGUID
),
91 ID_NAME(IID_IEnumIDList
),
92 ID_NAME(IID_IEnumShellItems
),
93 ID_NAME(IID_IEnumString
),
94 ID_NAME(IID_IEnumUnknown
),
95 ID_NAME(IID_IEnumVARIANT
),
96 ID_NAME(IID_IErrorLog
),
97 ID_NAME(IID_IExplorerToolbar
),
98 ID_NAME(IID_IExtractIconA
),
99 ID_NAME(IID_IExtractIconW
),
100 ID_NAME(IID_IExtractImage
),
101 ID_NAME(IID_IExtractImage2
),
102 ID_NAME(IID_IFileDialog
),
103 ID_NAME(IID_IFileDialog2
),
104 ID_NAME(IID_IFileSearchBand
),
105 ID_NAME(IID_IFolderBandPriv
),
106 ID_NAME(IID_IFolderFilter
),
107 ID_NAME(IID_IFolderFilterSite
),
108 ID_NAME(IID_IFolderView
),
109 ID_NAME(IID_IFolderView2
),
110 ID_NAME(IID_IFolderViewOC
),
111 ID_NAME(IID_IFolderViewSettings
),
112 ID_NAME(IID_IGlobalFolderSettings
),
113 ID_NAME(IID_IInitializeObject
),
114 ID_NAME(IID_IInputObject
),
115 ID_NAME(IID_IInputObjectSite
),
116 ID_NAME(IID_IInternalUnknown
),
117 ID_NAME(IID_IMarshal
),
118 ID_NAME(IID_IMenuBand
),
119 ID_NAME(IID_IMenuPopup
),
120 ID_NAME(IID_IMoniker
),
121 ID_NAME(IID_IMultiMonitorDockingSite
),
122 ID_NAME(IID_IMultiQI
),
123 ID_NAME(IID_INamespaceProxy
),
124 ID_NAME(IID_INameSpaceTreeControl
),
125 ID_NAME(IID_INSCTree
),
126 ID_NAME(IID_INSCTree2
),
127 ID_NAME(IID_IObjMgr
),
128 ID_NAME(IID_IObjectSafety
),
129 ID_NAME(IID_IObjectWithSite
),
130 ID_NAME(IID_IOleCommandTarget
),
131 ID_NAME(IID_IOleInPlaceActiveObject
),
132 ID_NAME(IID_IOleInPlaceFrame
),
133 ID_NAME(IID_IOleInPlaceObject
),
134 ID_NAME(IID_IOleInPlaceObjectWindowless
),
135 ID_NAME(IID_IOleInPlaceSite
),
136 ID_NAME(IID_IOleInPlaceSiteEx
),
137 ID_NAME(IID_IOleInPlaceSiteWindowless
),
138 ID_NAME(IID_IOleInPlaceUIWindow
),
139 ID_NAME(IID_IOleObject
),
140 ID_NAME(IID_IOleWindow
),
141 ID_NAME(IID_IPersist
),
142 ID_NAME(IID_IPersistFile
),
143 ID_NAME(IID_IPersistFolder
),
144 ID_NAME(IID_IPersistFolder2
),
145 ID_NAME(IID_IPersistFolder3
),
146 ID_NAME(IID_IPersistHistory
),
147 ID_NAME(IID_IPersistIDList
),
148 ID_NAME(IID_IPersistMemory
),
149 ID_NAME(IID_IPersistPropertyBag
),
150 ID_NAME(IID_IPersistPropertyBag2
),
151 ID_NAME(IID_IPersistStorage
),
152 ID_NAME(IID_IPersistStream
),
153 ID_NAME(IID_IPersistStreamInit
),
154 ID_NAME(IID_IProgressDialog
),
155 ID_NAME(IID_IPropertyBag
),
156 ID_NAME(IID_IPropertyBag2
),
157 ID_NAME(IID_IQueryAssociations
),
158 ID_NAME(IID_IQueryInfo
),
159 ID_NAME(IID_IRegTreeOptions
),
160 ID_NAME(IID_IRunnableObject
),
161 ID_NAME(IID_IServerSecurity
),
162 ID_NAME(IID_IServiceProvider
),
163 ID_NAME(IID_IShellBrowser
),
164 ID_NAME(IID_IShellBrowserService
),
165 ID_NAME(IID_IShellChangeNotify
),
166 ID_NAME(IID_IShellDesktopTray
),
167 ID_NAME(IID_IShellDispatch
),
168 ID_NAME(IID_IShellDispatch2
),
169 ID_NAME(IID_IShellDispatch3
),
170 ID_NAME(IID_IShellDispatch4
),
171 ID_NAME(IID_IShellDispatch5
),
172 ID_NAME(IID_IShellExtInit
),
173 ID_NAME(IID_IShellPropSheetExt
),
174 ID_NAME(IID_IShellIconOverlayIdentifier
),
175 ID_NAME(IID_IShellFolder
),
176 ID_NAME(IID_IShellFolder2
),
177 ID_NAME(IID_IShellFolderBand
),
178 ID_NAME(IID_IShellFolderView
),
179 ID_NAME(IID_IShellFolderViewCB
),
180 ID_NAME(IID_IShellFolderViewDual
),
181 ID_NAME(IID_IShellFolderViewDual2
),
182 ID_NAME(IID_IShellIcon
),
183 ID_NAME(IID_IShellItem
),
184 ID_NAME(IID_IShellItem2
),
185 ID_NAME(IID_IShellItemArray
),
186 ID_NAME(IID_IShellItemFilter
),
187 ID_NAME(IID_IShellLinkA
),
188 ID_NAME(IID_IShellLinkDual
),
189 ID_NAME(IID_IShellLinkDual2
),
190 ID_NAME(IID_IShellLinkW
),
191 ID_NAME(IID_IShellMenu
),
192 ID_NAME(IID_IShellMenu2
),
193 ID_NAME(IID_IShellMenuAcc
),
194 ID_NAME(IID_IShellMenuCallback
),
195 ID_NAME(IID_IShellNameSpace
),
196 ID_NAME(IID_IShellService
),
197 ID_NAME(IID_IShellView
),
198 ID_NAME(IID_IShellView2
),
199 ID_NAME(IID_IShellView3
),
200 ID_NAME(IID_IShellWindows
),
201 ID_NAME(IID_IStream
),
202 ID_NAME(IID_ISurrogate
),
203 ID_NAME(IID_ISynchronize
),
204 ID_NAME(IID_ISynchronizeContainer
),
205 ID_NAME(IID_ISynchronizeEvent
),
206 ID_NAME(IID_ISynchronizeHandle
),
207 ID_NAME(IID_ITaskbarList
),
208 ID_NAME(IID_ITaskbarList2
),
209 ID_NAME(IID_ITrackShellMenu
),
210 ID_NAME(IID_ITrayPriv
),
211 ID_NAME(IID_ITrayPriv2
),
212 ID_NAME(IID_IUnknown
),
213 ID_NAME(IID_IViewObject
),
214 ID_NAME(IID_IViewObject2
),
215 ID_NAME(IID_IViewObjectEx
),
216 ID_NAME(IID_IWinEventHandler
),
218 ID_NAME(IID_DFConstraint
),
219 ID_NAME(DIID_DShellFolderViewEvents
),
221 ID_NAME(IID_CDefView
),
223 ID_NAME(IID_Folder2
),
224 ID_NAME(IID_Folder3
),
225 ID_NAME(IID_FolderItem
),
226 ID_NAME(IID_FolderItem2
),
227 ID_NAME(IID_FolderItems
),
228 ID_NAME(IID_FolderItems2
),
229 ID_NAME(IID_FolderItems3
),
230 ID_NAME(IID_FolderItemVerb
),
231 ID_NAME(IID_FolderItemVerbs
),
233 ID_NAME(CLSID_ShellDesktop
)
235 static const INT KnownInterfaceCount
= RTL_NUMBER_OF(KnownInterfaces
);
244 for (i
= 0; i
< KnownInterfaceCount
; i
++)
245 if (IsEqualIID(KnownInterfaces
[i
].iid
, iid
))
246 return &KnownInterfaces
[i
];
247 ASSERT(i
!= KnownInterfaceCount
);
254 _In_ PCCLASS_AND_INTERFACES
class,
259 for (i
= 0; class->ifaces
[i
].iid
; i
++)
260 if (IsEqualIID(class->ifaces
[i
].iid
, iid
))
265 #define INTF_NOT_EXPOSED LONG_MAX
277 hr
= IUnknown_QueryInterface(pUnk
, iid
, &pObj
);
278 ok(hr
== S_OK
|| hr
== E_NOINTERFACE
, "IUnknown::QueryInterface returned 0x%lx\n", hr
);
280 return INTF_NOT_EXPOSED
;
283 offset
= (LONG_PTR
)pObj
- (LONG_PTR
)pUnk
;
284 IUnknown_Release(pUnk2
);
290 TestModuleInterfaces(
291 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces
,
292 _In_ INT ExpectedInterfaceCount
)
298 PCCLASS_AND_INTERFACES
class;
300 for (iClass
= 0; iClass
< ExpectedInterfaceCount
; iClass
++)
302 class = &ExpectedInterfaces
[iClass
];
303 hr
= CoCreateInstance(class->clsid
,
305 CLSCTX_INPROC_SERVER
,
308 ok(hr
== S_OK
, "CoCreateInstance failed. hr=0x%lx\n", hr
);
311 skip("Failed to instantiate %s.\n", class->name
);
317 /* Check that all expected interfaces are present and have the right offset */
318 for (iIntf
= 0; class->ifaces
[iIntf
].iid
; iIntf
++)
320 PCKNOWN_INTERFACE iface
= FindInterface(class->ifaces
[iIntf
].iid
);
321 LONG offset
= GetInterfaceOffset(pUnk
, iface
->iid
);
322 if (offset
== INTF_NOT_EXPOSED
)
323 ok(0, "%s is missing %s (offset %ld)\n", class->name
, iface
->name
, class->ifaces
[iIntf
].offset
);
324 else if (class->ifaces
[iIntf
].offset
!= FARAWY
)
326 #ifdef FAIL_WRONG_OFFSET
327 ok(offset
== class->ifaces
[iIntf
].offset
, "%s, %s offset is %ld, expected %ld\n", class->name
, iface
->name
, offset
, class->ifaces
[iIntf
].offset
);
329 if (offset
!= class->ifaces
[iIntf
].offset
)
330 mytrace("%s, %s offset is %ld, expected %ld\n", class->name
, iface
->name
, offset
, class->ifaces
[iIntf
].offset
);
335 /* Check that none other than the expected interfaces are present */
336 for (iIntf
= 0; iIntf
< KnownInterfaceCount
; iIntf
++)
338 PCKNOWN_INTERFACE iface
= &KnownInterfaces
[iIntf
];
340 if (IsInterfaceExpected(class, iface
->iid
))
342 offset
= GetInterfaceOffset(pUnk
, iface
->iid
);
343 #ifdef GENERATE_TABLE_ENTRIES
344 ok(offset
== INTF_NOT_EXPOSED
, "%s: { %s%x, &%s },\n", class->name
, offset
< 0 ? "-" : "", offset
< 0 ? -offset
: offset
, iface
->name
);
346 ok(offset
== INTF_NOT_EXPOSED
, "%s exposes %s (offset %ld), but shouldn't\n", class->name
, iface
->name
, offset
);
350 // TODO: do some aggregation
352 IUnknown_Release(pUnk
);
359 _In_ PCWSTR ModuleName
,
360 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces
,
361 _In_ INT ExpectedInterfaceCount
)
364 PCCLASS_AND_INTERFACES
class;
368 result
= RegOpenKeyEx(HKEY_LOCAL_MACHINE
, L
"Software\\Classes\\CLSID", 0, KEY_ENUMERATE_SUB_KEYS
, &hKeyClasses
);
369 ok(result
== NO_ERROR
, "Failed to open classes key, error %lu\n", result
);
370 if (!myskip(result
== NO_ERROR
, "No classes key\n"))
372 for (iClass
= 0; iClass
< ExpectedInterfaceCount
; iClass
++)
377 UNICODE_STRING clsid
;
381 PCWSTR expectedThreadingModel
;
383 class = &ExpectedInterfaces
[iClass
];
384 status
= RtlStringFromGUID(class->clsid
, &clsid
);
385 ok(status
== STATUS_SUCCESS
, "Failed to convert guid to string for %s, status %lx\n", class->name
, status
);
386 if (myskip(NT_SUCCESS(status
), "No guid string\n"))
389 result
= RegOpenKeyEx(hKeyClasses
, clsid
.Buffer
, 0, KEY_ENUMERATE_SUB_KEYS
, &hKey
);
390 ok(result
== NO_ERROR
, "Failed to open key for %s, error %lu\n", class->name
, result
);
391 RtlFreeUnicodeString(&clsid
);
392 if (myskip(result
== NO_ERROR
, "No key\n"))
395 result
= RegOpenKeyEx(hKey
, L
"InProcServer32", 0, KEY_QUERY_VALUE
, &hKeyServer
);
396 ok(result
== NO_ERROR
, "Failed to open key for %s, error %lu\n", class->name
, result
);
398 if (myskip(result
== NO_ERROR
, "No key\n"))
401 dataSize
= sizeof(data
);
402 result
= RegQueryValueEx(hKeyServer
, NULL
, NULL
, &type
, (PBYTE
)data
, &dataSize
);
403 ok(result
== NO_ERROR
, "Failed to query value for %s, error %lu\n", class->name
, result
);
404 if (!myskip(result
== NO_ERROR
, "No module name\n"))
406 ok(type
== REG_SZ
|| type
== REG_EXPAND_SZ
, "type %lu for %s\n", type
, class->name
);
407 ok(dataSize
% sizeof(WCHAR
) == 0, "size %lu for %s\n", dataSize
, class->name
);
408 ok(dataSize
<= sizeof(data
), "size %lu for %s\n", dataSize
, class->name
);
409 ok(data
[dataSize
/ sizeof(WCHAR
) - 1] == UNICODE_NULL
, "Not null terminated for %s\n", class->name
);
410 // TODO: Use SearchPath (or assume everything's in system32) and do a proper full path compare
411 PathStripPathW(data
);
412 PathRemoveExtensionW(data
);
413 ok(!wcsicmp(data
, ModuleName
), "Server is %ls, expected %ls for %s\n", data
, ModuleName
, class->name
);
416 dataSize
= sizeof(data
);
417 result
= RegQueryValueEx(hKeyServer
, L
"ThreadingModel", NULL
, &type
, (PBYTE
)data
, &dataSize
);
418 ok(result
== NO_ERROR
, "Failed to query value for %s, error %lu\n", class->name
, result
);
419 if (!myskip(result
== NO_ERROR
, "No ThreadingModel\n"))
421 ok(type
== REG_SZ
|| type
== REG_EXPAND_SZ
, "type %lu for %s\n", type
, class->name
);
422 ok(dataSize
% sizeof(WCHAR
) == 0, "size %lu for %s\n", dataSize
, class->name
);
423 ok(dataSize
<= sizeof(data
), "size %lu for %s\n", dataSize
, class->name
);
424 ok(data
[dataSize
/ sizeof(WCHAR
) - 1] == UNICODE_NULL
, "Not null terminated for %s\n", class->name
);
425 expectedThreadingModel
= class->ThreadingModel
;
426 if (!expectedThreadingModel
)
427 expectedThreadingModel
= L
"Apartment";
428 ok(!wcsicmp(data
, expectedThreadingModel
), "Server is %ls, expected %ls for %s\n", data
, expectedThreadingModel
, class->name
);
431 RegCloseKey(hKeyServer
);
433 RegCloseKey(hKeyClasses
);
439 TestManualInstantiation(
440 _In_ PCWSTR ModuleName
,
441 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces
,
442 _In_ INT ExpectedInterfaceCount
)
445 PCCLASS_AND_INTERFACES
class;
446 HRESULT (__stdcall
*DllGetClassObject
)(REFCLSID
, REFIID
, PVOID
*);
448 DllGetClassObject
= (PVOID
)GetProcAddress(GetModuleHandle(ModuleName
), "DllGetClassObject");
449 ok(DllGetClassObject
!= NULL
, "DllGetClassObject not found in %ls, error %lu\n", ModuleName
, GetLastError());
450 if (myskip(DllGetClassObject
!= NULL
, "No DllGetClassObject\n"))
453 for (iClass
= 0; iClass
< ExpectedInterfaceCount
; iClass
++)
457 class = &ExpectedInterfaces
[iClass
];
458 hr
= DllGetClassObject(class->clsid
, &IID_IClassFactory
, &pv
);
459 ok(hr
== S_OK
, "DllGetClassObject failed for %s, hr = 0x%lx\n", class->name
, hr
);
460 if (!myskip(SUCCEEDED(hr
), "No class factory\n"))
462 IClassFactory
*pCF
= pv
;
463 hr
= IClassFactory_CreateInstance(pCF
, NULL
, &IID_IUnknown
, &pv
);
464 ok(hr
== S_OK
, "IClassFactory::CreateInstance failed for %s, hr = 0x%lx\n", class->name
, hr
);
465 if (!myskip(SUCCEEDED(hr
), "No instance\n"))
468 IUnknown_Release(pUnk
);
470 IClassFactory_Release(pCF
);
477 _In_ PCWSTR ModuleName
,
478 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces
,
479 _In_ INT ExpectedInterfaceCount
)
483 hr
= CoInitializeEx(NULL
, COINIT_APARTMENTTHREADED
);
484 ok(hr
== S_OK
, "CoInitializeEx failed. hr=0x%lx\n", hr
);
485 if (myskip(SUCCEEDED(hr
), "Failed to initialize COM. Cannot perform tests\n"))
488 TestModuleInterfaces(ExpectedInterfaces
, ExpectedInterfaceCount
);
489 TestModuleRegistry(ModuleName
, ExpectedInterfaces
, ExpectedInterfaceCount
);
490 TestManualInstantiation(ModuleName
, ExpectedInterfaces
, ExpectedInterfaceCount
);