2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: COM interface test
5 * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
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_IAccessible
),
35 ID_NAME(IID_IAddressBand
),
36 ID_NAME(IID_IAddressEditBox
),
37 ID_NAME(IID_IBandNavigate
),
38 ID_NAME(IID_IBandProxy
),
39 ID_NAME(IID_IBandSite
),
40 ID_NAME(IID_IBandSiteHelper
),
41 ID_NAME(IID_IBanneredBar
),
42 ID_NAME(IID_IBindCtx
),
43 ID_NAME(IID_IBrowserService
),
44 ID_NAME(IID_IBrowserService2
),
45 ID_NAME(IID_IClassFactory
),
46 ID_NAME(IID_IClassFactory2
),
47 ID_NAME(IID_IClassFactory3
),
48 ID_NAME(IID_IClientSecurity
),
49 ID_NAME(IID_IComThreadingInfo
),
50 ID_NAME(IID_IContextMenu
),
51 ID_NAME(IID_IContextMenu2
),
52 ID_NAME(IID_IContextMenu3
),
53 ID_NAME(IID_IContextMenuCB
),
54 ID_NAME(IID_IContextMenuSite
),
55 ID_NAME(IID_IContinue
),
56 ID_NAME(IID_IDataObject
),
57 ID_NAME(IID_IDefViewFrame
),
58 ID_NAME(IID_IDeskBand
),
59 ID_NAME(IID_IDeskBar
),
60 ID_NAME(IID_IDeskBarClient
),
61 ID_NAME(IID_IDispatch
),
62 ID_NAME(IID_IDockingWindow
),
63 ID_NAME(IID_IDockingWindowFrame
),
64 ID_NAME(IID_IDockingWindowSite
),
65 ID_NAME(IID_IDocViewSite
),
66 ID_NAME(IID_IDragSourceHelper
),
67 ID_NAME(IID_IDropSource
),
68 ID_NAME(IID_IDropTarget
),
69 ID_NAME(IID_IDropTargetHelper
),
70 ID_NAME(IID_IEnumExtraSearch
),
71 ID_NAME(IID_IEnumGUID
),
72 ID_NAME(IID_IEnumIDList
),
73 ID_NAME(IID_IEnumShellItems
),
74 ID_NAME(IID_IEnumString
),
75 ID_NAME(IID_IEnumUnknown
),
76 ID_NAME(IID_IErrorLog
),
77 ID_NAME(IID_IExplorerToolbar
),
78 ID_NAME(IID_IExtractIconA
),
79 ID_NAME(IID_IExtractIconW
),
80 ID_NAME(IID_IExtractImage
),
81 ID_NAME(IID_IExtractImage2
),
82 ID_NAME(IID_IFileDialog
),
83 ID_NAME(IID_IFileDialog2
),
84 ID_NAME(IID_IFileSearchBand
),
85 ID_NAME(IID_IFolderBandPriv
),
86 ID_NAME(IID_IFolderView
),
87 ID_NAME(IID_IFolderView2
),
88 ID_NAME(IID_IFolderViewOC
),
89 ID_NAME(IID_IFolderViewSettings
),
90 ID_NAME(IID_IGlobalFolderSettings
),
91 ID_NAME(IID_IInitializeObject
),
92 ID_NAME(IID_IInputObject
),
93 ID_NAME(IID_IInputObjectSite
),
94 ID_NAME(IID_IInternalUnknown
),
95 ID_NAME(IID_IMarshal
),
96 ID_NAME(IID_IMenuBand
),
97 ID_NAME(IID_IMenuPopup
),
98 ID_NAME(IID_IMoniker
),
99 ID_NAME(IID_IMultiQI
),
100 ID_NAME(IID_INamespaceProxy
),
101 ID_NAME(IID_INameSpaceTreeControl
),
102 ID_NAME(IID_INSCTree
),
103 ID_NAME(IID_INSCTree2
),
104 ID_NAME(IID_IObjMgr
),
105 ID_NAME(IID_IObjectSafety
),
106 ID_NAME(IID_IObjectWithSite
),
107 ID_NAME(IID_IOleCommandTarget
),
108 ID_NAME(IID_IOleInPlaceActiveObject
),
109 ID_NAME(IID_IOleInPlaceFrame
),
110 ID_NAME(IID_IOleInPlaceObject
),
111 ID_NAME(IID_IOleInPlaceObjectWindowless
),
112 ID_NAME(IID_IOleInPlaceSite
),
113 ID_NAME(IID_IOleInPlaceSiteEx
),
114 ID_NAME(IID_IOleInPlaceSiteWindowless
),
115 ID_NAME(IID_IOleInPlaceUIWindow
),
116 ID_NAME(IID_IOleObject
),
117 ID_NAME(IID_IOleWindow
),
118 ID_NAME(IID_IPersist
),
119 ID_NAME(IID_IPersistFile
),
120 ID_NAME(IID_IPersistFolder
),
121 ID_NAME(IID_IPersistFolder2
),
122 ID_NAME(IID_IPersistFolder3
),
123 ID_NAME(IID_IPersistHistory
),
124 ID_NAME(IID_IPersistIDList
),
125 ID_NAME(IID_IPersistMemory
),
126 ID_NAME(IID_IPersistPropertyBag
),
127 ID_NAME(IID_IPersistPropertyBag2
),
128 ID_NAME(IID_IPersistStorage
),
129 ID_NAME(IID_IPersistStream
),
130 ID_NAME(IID_IPersistStreamInit
),
131 ID_NAME(IID_IProgressDialog
),
132 ID_NAME(IID_IPropertyBag
),
133 ID_NAME(IID_IPropertyBag2
),
134 ID_NAME(IID_IQueryAssociations
),
135 ID_NAME(IID_IQueryInfo
),
136 ID_NAME(IID_IRegTreeOptions
),
137 ID_NAME(IID_IRunnableObject
),
138 ID_NAME(IID_IServerSecurity
),
139 ID_NAME(IID_IServiceProvider
),
140 ID_NAME(IID_IShellBrowser
),
141 ID_NAME(IID_IShellBrowserService
),
142 ID_NAME(IID_IShellChangeNotify
),
143 ID_NAME(IID_IShellDesktopTray
),
144 ID_NAME(IID_IShellDispatch
),
145 ID_NAME(IID_IShellDispatch2
),
146 ID_NAME(IID_IShellDispatch3
),
147 ID_NAME(IID_IShellDispatch4
),
148 ID_NAME(IID_IShellDispatch5
),
149 ID_NAME(IID_IShellIconOverlayIdentifier
),
150 ID_NAME(IID_IShellFolder
),
151 ID_NAME(IID_IShellFolder2
),
152 ID_NAME(IID_IShellFolderBand
),
153 ID_NAME(IID_IShellFolderView
),
154 ID_NAME(IID_IShellFolderViewCB
),
155 ID_NAME(IID_IShellFolderViewDual
),
156 ID_NAME(IID_IShellFolderViewDual2
),
157 ID_NAME(IID_IShellIcon
),
158 ID_NAME(IID_IShellItem
),
159 ID_NAME(IID_IShellItem2
),
160 ID_NAME(IID_IShellItemArray
),
161 ID_NAME(IID_IShellItemFilter
),
162 ID_NAME(IID_IShellLinkA
),
163 ID_NAME(IID_IShellLinkDual
),
164 ID_NAME(IID_IShellLinkDual2
),
165 ID_NAME(IID_IShellLinkW
),
166 ID_NAME(IID_IShellMenu
),
167 ID_NAME(IID_IShellMenu2
),
168 ID_NAME(IID_IShellMenuAcc
),
169 ID_NAME(IID_IShellMenuCallback
),
170 ID_NAME(IID_IShellService
),
171 ID_NAME(IID_IShellView
),
172 ID_NAME(IID_IShellView2
),
173 ID_NAME(IID_IShellView3
),
174 ID_NAME(IID_IShellWindows
),
175 ID_NAME(IID_IStream
),
176 ID_NAME(IID_ISurrogate
),
177 ID_NAME(IID_ISynchronize
),
178 ID_NAME(IID_ISynchronizeContainer
),
179 ID_NAME(IID_ISynchronizeEvent
),
180 ID_NAME(IID_ISynchronizeHandle
),
181 ID_NAME(IID_ITaskbarList
),
182 ID_NAME(IID_ITaskbarList2
),
183 ID_NAME(IID_ITrackShellMenu
),
184 ID_NAME(IID_ITrayPriv
),
185 ID_NAME(IID_ITrayPriv2
),
186 ID_NAME(IID_IUnknown
),
187 ID_NAME(IID_IViewObject
),
188 ID_NAME(IID_IViewObject2
),
189 ID_NAME(IID_IWinEventHandler
),
191 ID_NAME(IID_DFConstraint
),
192 ID_NAME(DIID_DShellFolderViewEvents
),
195 ID_NAME(IID_Folder2
),
196 ID_NAME(IID_Folder3
),
197 ID_NAME(IID_FolderItem
),
198 ID_NAME(IID_FolderItem2
),
199 ID_NAME(IID_FolderItems
),
200 ID_NAME(IID_FolderItems2
),
201 ID_NAME(IID_FolderItems3
),
202 ID_NAME(IID_FolderItemVerb
),
203 ID_NAME(IID_FolderItemVerbs
),
205 static const INT KnownInterfaceCount
= RTL_NUMBER_OF(KnownInterfaces
);
214 for (i
= 0; i
< KnownInterfaceCount
; i
++)
215 if (IsEqualIID(KnownInterfaces
[i
].iid
, iid
))
216 return &KnownInterfaces
[i
];
217 ASSERT(i
!= KnownInterfaceCount
);
224 _In_ PCCLASS_AND_INTERFACES
class,
229 for (i
= 0; class->ifaces
[i
].iid
; i
++)
230 if (IsEqualIID(class->ifaces
[i
].iid
, iid
))
235 #define INTF_NOT_EXPOSED LONG_MAX
247 hr
= IUnknown_QueryInterface(pUnk
, iid
, &pObj
);
248 ok(hr
== S_OK
|| hr
== E_NOINTERFACE
, "IUnknown::QueryInterface returned 0x%lx\n", hr
);
250 return INTF_NOT_EXPOSED
;
253 offset
= (LONG_PTR
)pObj
- (LONG_PTR
)pUnk
;
254 IUnknown_Release(pUnk2
);
260 TestModuleInterfaces(
261 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces
,
262 _In_ INT ExpectedInterfaceCount
)
268 PCCLASS_AND_INTERFACES
class;
270 for (iClass
= 0; iClass
< ExpectedInterfaceCount
; iClass
++)
272 class = &ExpectedInterfaces
[iClass
];
273 hr
= CoCreateInstance(class->clsid
,
275 CLSCTX_INPROC_SERVER
,
278 ok(hr
== S_OK
, "CoCreateInstance failed. hr=0x%lx\n", hr
);
281 skip("Failed to instantiate %s.\n", class->name
);
287 /* Check that all expected interfaces are present and have the right offset */
288 for (iIntf
= 0; class->ifaces
[iIntf
].iid
; iIntf
++)
290 PCKNOWN_INTERFACE iface
= FindInterface(class->ifaces
[iIntf
].iid
);
291 LONG offset
= GetInterfaceOffset(pUnk
, iface
->iid
);
292 if (offset
== INTF_NOT_EXPOSED
)
293 ok(0, "%s is missing %s (offset %ld)\n", class->name
, iface
->name
, class->ifaces
[iIntf
].offset
);
294 else if (class->ifaces
[iIntf
].offset
!= FARAWY
)
296 #ifdef FAIL_WRONG_OFFSET
297 ok(offset
== class->ifaces
[iIntf
].offset
, "%s, %s offset is %ld, expected %ld\n", class->name
, iface
->name
, offset
, class->ifaces
[iIntf
].offset
);
299 if (offset
!= class->ifaces
[iIntf
].offset
)
300 mytrace("%s, %s offset is %ld, expected %ld\n", class->name
, iface
->name
, offset
, class->ifaces
[iIntf
].offset
);
305 /* Check that none other than the expected interfaces are present */
306 for (iIntf
= 0; iIntf
< KnownInterfaceCount
; iIntf
++)
308 PCKNOWN_INTERFACE iface
= &KnownInterfaces
[iIntf
];
310 if (IsInterfaceExpected(class, iface
->iid
))
312 offset
= GetInterfaceOffset(pUnk
, iface
->iid
);
313 #ifdef GENERATE_TABLE_ENTRIES
314 ok(offset
== INTF_NOT_EXPOSED
, "%s: { %s%x, &%s },\n", class->name
, offset
< 0 ? "-" : "", offset
< 0 ? -offset
: offset
, iface
->name
);
316 ok(offset
== INTF_NOT_EXPOSED
, "%s exposes %s (offset %ld), but shouldn't\n", class->name
, iface
->name
, offset
);
320 // TODO: do some aggregation
322 IUnknown_Release(pUnk
);
329 _In_ PCWSTR ModuleName
,
330 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces
,
331 _In_ INT ExpectedInterfaceCount
)
334 PCCLASS_AND_INTERFACES
class;
338 result
= RegOpenKeyEx(HKEY_LOCAL_MACHINE
, L
"Software\\Classes\\CLSID", 0, KEY_ENUMERATE_SUB_KEYS
, &hKeyClasses
);
339 ok(result
== NO_ERROR
, "Failed to open classes key, error %lu\n", result
);
340 if (!myskip(result
== NO_ERROR
, "No classes key\n"))
342 for (iClass
= 0; iClass
< ExpectedInterfaceCount
; iClass
++)
347 UNICODE_STRING clsid
;
351 PCWSTR expectedThreadingModel
;
353 class = &ExpectedInterfaces
[iClass
];
354 status
= RtlStringFromGUID(class->clsid
, &clsid
);
355 ok(status
== STATUS_SUCCESS
, "Failed to convert guid to string for %s, status %lx\n", class->name
, status
);
356 if (myskip(NT_SUCCESS(status
), "No guid string\n"))
359 result
= RegOpenKeyEx(hKeyClasses
, clsid
.Buffer
, 0, KEY_ENUMERATE_SUB_KEYS
, &hKey
);
360 ok(result
== NO_ERROR
, "Failed to open key for %s, error %lu\n", class->name
, result
);
361 RtlFreeUnicodeString(&clsid
);
362 if (myskip(result
== NO_ERROR
, "No key\n"))
365 result
= RegOpenKeyEx(hKey
, L
"InProcServer32", 0, KEY_QUERY_VALUE
, &hKeyServer
);
366 ok(result
== NO_ERROR
, "Failed to open key for %s, error %lu\n", class->name
, result
);
368 if (myskip(result
== NO_ERROR
, "No key\n"))
371 dataSize
= sizeof(data
);
372 result
= RegQueryValueEx(hKeyServer
, NULL
, NULL
, &type
, (PBYTE
)data
, &dataSize
);
373 ok(result
== NO_ERROR
, "Failed to query value for %s, error %lu\n", class->name
, result
);
374 if (!myskip(result
== NO_ERROR
, "No module name\n"))
376 ok(type
== REG_SZ
|| type
== REG_EXPAND_SZ
, "type %lu for %s\n", type
, class->name
);
377 ok(dataSize
% sizeof(WCHAR
) == 0, "size %lu for %s\n", dataSize
, class->name
);
378 ok(dataSize
<= sizeof(data
), "size %lu for %s\n", dataSize
, class->name
);
379 ok(data
[dataSize
/ sizeof(WCHAR
) - 1] == UNICODE_NULL
, "Not null terminated for %s\n", class->name
);
380 // TODO: Use SearchPath (or assume everything's in system32) and do a proper full path compare
381 PathStripPathW(data
);
382 PathRemoveExtensionW(data
);
383 ok(!wcsicmp(data
, ModuleName
), "Server is %ls, expected %ls for %s\n", data
, ModuleName
, class->name
);
386 dataSize
= sizeof(data
);
387 result
= RegQueryValueEx(hKeyServer
, L
"ThreadingModel", NULL
, &type
, (PBYTE
)data
, &dataSize
);
388 ok(result
== NO_ERROR
, "Failed to query value for %s, error %lu\n", class->name
, result
);
389 if (!myskip(result
== NO_ERROR
, "No ThreadingModel\n"))
391 ok(type
== REG_SZ
|| type
== REG_EXPAND_SZ
, "type %lu for %s\n", type
, class->name
);
392 ok(dataSize
% sizeof(WCHAR
) == 0, "size %lu for %s\n", dataSize
, class->name
);
393 ok(dataSize
<= sizeof(data
), "size %lu for %s\n", dataSize
, class->name
);
394 ok(data
[dataSize
/ sizeof(WCHAR
) - 1] == UNICODE_NULL
, "Not null terminated for %s\n", class->name
);
395 expectedThreadingModel
= class->ThreadingModel
;
396 if (!expectedThreadingModel
)
397 expectedThreadingModel
= L
"Apartment";
398 ok(!wcsicmp(data
, expectedThreadingModel
), "Server is %ls, expected %ls for %s\n", data
, expectedThreadingModel
, class->name
);
401 RegCloseKey(hKeyServer
);
403 RegCloseKey(hKeyClasses
);
409 TestManualInstantiation(
410 _In_ PCWSTR ModuleName
,
411 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces
,
412 _In_ INT ExpectedInterfaceCount
)
415 PCCLASS_AND_INTERFACES
class;
416 HRESULT (__stdcall
*DllGetClassObject
)(REFCLSID
, REFIID
, PVOID
*);
418 DllGetClassObject
= (PVOID
)GetProcAddress(GetModuleHandle(ModuleName
), "DllGetClassObject");
419 ok(DllGetClassObject
!= NULL
, "DllGetClassObject not found in %ls, error %lu\n", ModuleName
, GetLastError());
420 if (myskip(DllGetClassObject
!= NULL
, "No DllGetClassObject\n"))
423 for (iClass
= 0; iClass
< ExpectedInterfaceCount
; iClass
++)
427 class = &ExpectedInterfaces
[iClass
];
428 hr
= DllGetClassObject(class->clsid
, &IID_IClassFactory
, &pv
);
429 ok(hr
== S_OK
, "DllGetClassObject failed for %s, hr = 0x%lx\n", class->name
, hr
);
430 if (!myskip(SUCCEEDED(hr
), "No class factory\n"))
432 IClassFactory
*pCF
= pv
;
433 hr
= IClassFactory_CreateInstance(pCF
, NULL
, &IID_IUnknown
, &pv
);
434 ok(hr
== S_OK
, "IClassFactory::CreateInstance failed for %s, hr = 0x%lx\n", class->name
, hr
);
435 if (!myskip(SUCCEEDED(hr
), "No instance\n"))
438 IUnknown_Release(pUnk
);
440 IClassFactory_Release(pCF
);
447 _In_ PCWSTR ModuleName
,
448 _In_ PCCLASS_AND_INTERFACES ExpectedInterfaces
,
449 _In_ INT ExpectedInterfaceCount
)
453 hr
= CoInitializeEx(NULL
, COINIT_APARTMENTTHREADED
);
454 ok(hr
== S_OK
, "CoInitializeEx failed. hr=0x%lx\n", hr
);
455 if (myskip(SUCCEEDED(hr
), "Failed to initialize COM. Cannot perform tests\n"))
458 TestModuleInterfaces(ExpectedInterfaces
, ExpectedInterfaceCount
);
459 TestModuleRegistry(ModuleName
, ExpectedInterfaces
, ExpectedInterfaceCount
);
460 TestManualInstantiation(ModuleName
, ExpectedInterfaces
, ExpectedInterfaceCount
);