samlib: Add stubbed samlib.dll.
[wine] / dlls / dispex / tests / marshal.c
1 /*
2  * Tests for marshaling IDispatchEx
3  *
4  * Copyright 2005-2006 Robert Shearman
5  * Copyright 2010 Huw Davies
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
23 #define COBJMACROS
24 #define CONST_VTABLE
25
26 #include <stdarg.h>
27
28 #include "initguid.h"
29 #include "objidl.h"
30 #include "dispex.h"
31
32 #include "wine/test.h"
33
34 #define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08lx\n", (unsigned long int)hr)
35
36 #define RELEASEMARSHALDATA WM_USER
37
38 struct host_object_data
39 {
40     IStream *stream;
41     IID iid;
42     IUnknown *object;
43     MSHLFLAGS marshal_flags;
44     HANDLE marshal_event;
45     HANDLE error_event;
46     IMessageFilter *filter;
47 };
48
49 static DWORD CALLBACK host_object_proc(LPVOID p)
50 {
51     struct host_object_data *data = p;
52     HRESULT hr;
53     MSG msg;
54
55     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
56
57     if (data->filter)
58     {
59         IMessageFilter * prev_filter = NULL;
60         hr = CoRegisterMessageFilter(data->filter, &prev_filter);
61         if (prev_filter) IMessageFilter_Release(prev_filter);
62         ok_ole_success(hr, CoRegisterMessageFilter);
63     }
64
65     hr = CoMarshalInterface(data->stream, &data->iid, data->object, MSHCTX_INPROC, NULL, data->marshal_flags);
66
67     /* force the message queue to be created before signaling parent thread */
68     PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
69
70     if(hr == S_OK)
71         SetEvent(data->marshal_event);
72     else
73     {
74         win_skip("IDispatchEx marshaller not available.\n");
75         SetEvent(data->error_event);
76         return hr;
77     }
78
79     while (GetMessage(&msg, NULL, 0, 0))
80     {
81         if (msg.hwnd == NULL && msg.message == RELEASEMARSHALDATA)
82         {
83             trace("releasing marshal data\n");
84             CoReleaseMarshalData(data->stream);
85             SetEvent((HANDLE)msg.lParam);
86         }
87         else
88             DispatchMessage(&msg);
89     }
90
91     HeapFree(GetProcessHeap(), 0, data);
92
93     CoUninitialize();
94
95     return hr;
96 }
97
98 static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, IMessageFilter *filter, HANDLE *thread)
99 {
100     DWORD tid = 0, ret;
101     HANDLE events[2];
102     struct host_object_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data));
103
104     data->stream = stream;
105     data->iid = *riid;
106     data->object = object;
107     data->marshal_flags = marshal_flags;
108     data->marshal_event = events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
109     data->error_event   = events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
110     data->filter = filter;
111
112     *thread = CreateThread(NULL, 0, host_object_proc, data, 0, &tid);
113
114     /* wait for marshaling to complete before returning */
115     ret = WaitForMultipleObjects(2, events, FALSE, INFINITE);
116     CloseHandle(events[0]);
117     CloseHandle(events[1]);
118
119     if(ret == WAIT_OBJECT_0) return tid;
120
121     WaitForSingleObject(*thread, INFINITE);
122     CloseHandle(*thread);
123     *thread = INVALID_HANDLE_VALUE;
124     return 0;
125 }
126
127 static DWORD start_host_object(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, HANDLE *thread)
128 {
129     return start_host_object2(stream, riid, object, marshal_flags, NULL, thread);
130 }
131
132 static void end_host_object(DWORD tid, HANDLE thread)
133 {
134     BOOL ret = PostThreadMessage(tid, WM_QUIT, 0, 0);
135     ok(ret, "PostThreadMessage failed with error %d\n", GetLastError());
136     /* be careful of races - don't return until hosting thread has terminated */
137     WaitForSingleObject(thread, INFINITE);
138     CloseHandle(thread);
139 }
140
141 typedef struct
142 {
143     const IDispatchExVtbl *lpVtbl;
144     LONG refs;
145 } dispex;
146
147 static HRESULT WINAPI dispex_QueryInterface(IDispatchEx* iface,
148                                             REFIID iid,  void **obj)
149 {
150     trace("QI {%08x-...}\n", iid->Data1);
151     if(IsEqualIID(iid, &IID_IUnknown) ||
152        IsEqualIID(iid, &IID_IDispatchEx))
153     {
154         IDispatchEx_AddRef(iface);
155         *obj = iface;
156         return S_OK;
157     }
158     else
159     {
160         *obj = NULL;
161         return E_NOINTERFACE;
162     }
163 }
164
165 static ULONG WINAPI dispex_AddRef(IDispatchEx* iface)
166 {
167     dispex *This = (dispex *)iface;
168     trace("AddRef\n");
169
170     return InterlockedIncrement(&This->refs);
171 }
172
173 static ULONG WINAPI dispex_Release(IDispatchEx* iface)
174 {
175     dispex *This = (dispex *)iface;
176     ULONG refs = InterlockedDecrement(&This->refs);
177     trace("Release\n");
178     if(!refs)
179     {
180         HeapFree(GetProcessHeap(), 0, This);
181     }
182     return refs;
183 }
184
185  static HRESULT WINAPI dispex_GetTypeInfoCount(IDispatchEx* iface,
186                                                UINT *pctinfo)
187 {
188     trace("\n");
189     return E_NOTIMPL;
190 }
191
192 static HRESULT WINAPI dispex_GetTypeInfo(IDispatchEx* iface,
193                                          UINT iTInfo,
194                                          LCID lcid,
195                                          ITypeInfo **ppTInfo)
196 {
197     trace("\n");
198     return E_NOTIMPL;
199 }
200
201 static HRESULT WINAPI dispex_GetIDsOfNames(IDispatchEx* iface,
202                                            REFIID riid,
203                                            LPOLESTR *rgszNames,
204                                            UINT cNames,
205                                            LCID lcid,
206                                            DISPID *rgDispId)
207 {
208     trace("\n");
209     return E_NOTIMPL;
210 }
211
212 static HRESULT WINAPI dispex_Invoke(IDispatchEx* iface,
213                                     DISPID dispIdMember,
214                                     REFIID riid,
215                                     LCID lcid,
216                                     WORD wFlags,
217                                     DISPPARAMS *pDispParams,
218                                     VARIANT *pVarResult,
219                                     EXCEPINFO *pExcepInfo,
220                                     UINT *puArgErr)
221 {
222     trace("\n");
223     return E_NOTIMPL;
224 }
225
226 static HRESULT WINAPI dispex_GetDispID(IDispatchEx* iface,
227                                        BSTR bstrName,
228                                        DWORD grfdex,
229                                        DISPID *pid)
230 {
231     trace("\n");
232     return E_NOTIMPL;
233 }
234
235 static HRESULT WINAPI defer_fn(EXCEPINFO *except)
236 {
237     except->scode = E_OUTOFMEMORY;
238     return S_OK;
239 }
240
241 static HRESULT WINAPI dispex_InvokeEx(IDispatchEx* iface,
242                                       DISPID id,
243                                       LCID lcid,
244                                       WORD wFlags,
245                                       DISPPARAMS *pdp,
246                                       VARIANT *pvarRes,
247                                       EXCEPINFO *pei,
248                                       IServiceProvider *pspCaller)
249 {
250     if(id == 1)
251     {
252         ok(pdp->cArgs == 0, "got %d\n", pdp->cArgs);
253         ok(pei == NULL, "got non-NULL excepinfo\n");
254         ok(pvarRes == NULL, "got non-NULL result\n");
255     }
256     else if(id == 2)
257     {
258         ok(pdp->cArgs == 2, "got %d\n", pdp->cArgs);
259         ok(V_VT(&pdp->rgvarg[0]) == VT_INT, "got %04x\n", V_VT(&pdp->rgvarg[0]));
260         ok(V_VT(&pdp->rgvarg[1]) == (VT_INT | VT_BYREF), "got %04x\n", V_VT(&pdp->rgvarg[1]));
261         ok(*V_INTREF(&pdp->rgvarg[1]) == 0xbeef, "got %08x\n", *V_INTREF(&pdp->rgvarg[1]));
262         *V_INTREF(&pdp->rgvarg[1]) = 0xdead;
263     }
264     else if(id == 3)
265     {
266         ok(pdp->cArgs == 2, "got %d\n", pdp->cArgs);
267         ok(V_VT(&pdp->rgvarg[0]) == VT_INT, "got %04x\n", V_VT(&pdp->rgvarg[0]));
268         ok(V_VT(&pdp->rgvarg[1]) == (VT_INT | VT_BYREF), "got %04x\n", V_VT(&pdp->rgvarg[1]));
269         V_VT(&pdp->rgvarg[0]) = VT_I4;
270     }
271     else if(id == 4)
272     {
273         ok(wFlags == 0xf, "got %04x\n", wFlags);
274     }
275     else if(id == 5)
276     {
277         if(pei) pei->pfnDeferredFillIn = defer_fn;
278         return DISP_E_EXCEPTION;
279     }
280     return S_OK;
281 }
282
283 static HRESULT WINAPI dispex_DeleteMemberByName(IDispatchEx* iface,
284                                                 BSTR bstrName,
285                                                 DWORD grfdex)
286 {
287     trace("\n");
288     return E_NOTIMPL;
289 }
290
291 static HRESULT WINAPI dispex_DeleteMemberByDispID(IDispatchEx* iface, DISPID id)
292 {
293     trace("\n");
294     return E_NOTIMPL;
295 }
296
297 static HRESULT WINAPI dispex_GetMemberProperties(IDispatchEx* iface, DISPID id,
298                                                  DWORD grfdexFetch, DWORD *pgrfdex)
299 {
300     trace("\n");
301     return E_NOTIMPL;
302 }
303
304 static HRESULT WINAPI dispex_GetMemberName(IDispatchEx* iface,
305                                            DISPID id, BSTR *pbstrName)
306 {
307     trace("\n");
308     return E_NOTIMPL;
309 }
310
311 static HRESULT WINAPI dispex_GetNextDispID(IDispatchEx* iface,
312                                            DWORD grfdex,
313                                            DISPID id,
314                                            DISPID *pid)
315 {
316     trace("\n");
317     return E_NOTIMPL;
318 }
319
320 static HRESULT WINAPI dispex_GetNameSpaceParent(IDispatchEx* iface,
321                                                 IUnknown **ppunk)
322 {
323     trace("\n");
324     return E_NOTIMPL;
325 }
326
327 static const IDispatchExVtbl dispex_vtable =
328 {
329     dispex_QueryInterface,
330     dispex_AddRef,
331     dispex_Release,
332     dispex_GetTypeInfoCount,
333     dispex_GetTypeInfo,
334     dispex_GetIDsOfNames,
335     dispex_Invoke,
336     dispex_GetDispID,
337     dispex_InvokeEx,
338     dispex_DeleteMemberByName,
339     dispex_DeleteMemberByDispID,
340     dispex_GetMemberProperties,
341     dispex_GetMemberName,
342     dispex_GetNextDispID,
343     dispex_GetNameSpaceParent
344 };
345
346 static IDispatchEx *dispex_create(void)
347 {
348     dispex *This;
349
350     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
351     if (!This) return NULL;
352     This->lpVtbl = &dispex_vtable;
353     This->refs = 1;
354     return (IDispatchEx *)This;
355 }
356
357 static void test_dispex(void)
358 {
359     HRESULT hr;
360     IStream *stream;
361     DWORD tid;
362     HANDLE thread;
363     static const LARGE_INTEGER zero;
364     IDispatchEx *dispex = dispex_create();
365     DISPPARAMS params;
366     VARIANTARG args[10];
367     INT i;
368     EXCEPINFO excepinfo;
369
370     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
371     ok(hr == S_OK, "got %08x\n", hr);
372     tid = start_host_object(stream, &IID_IDispatchEx, (IUnknown *)dispex, MSHLFLAGS_NORMAL, &thread);
373     IDispatchEx_Release(dispex);
374     if(tid == 0)
375     {
376         IStream_Release(stream);
377         return;
378     }
379
380     IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
381     hr = CoUnmarshalInterface(stream, &IID_IDispatchEx, (void **)&dispex);
382     ok(hr == S_OK, "got %08x\n", hr);
383     IStream_Release(stream);
384
385     params.rgvarg = NULL;
386     params.rgdispidNamedArgs = NULL;
387     params.cArgs = 0;
388     params.cNamedArgs = 0;
389     hr = IDispatchEx_InvokeEx(dispex, 1, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
390     ok(hr == S_OK, "got %08x\n", hr);
391
392     params.rgvarg = args;
393     params.rgdispidNamedArgs = NULL;
394     params.cArgs = 2;
395     params.cNamedArgs = 0;
396     V_VT(&args[0]) = VT_INT;
397     V_INT(&args[0]) = 0xcafe;
398     V_VT(&args[1]) = VT_INT | VT_BYREF;
399     V_INTREF(&args[1]) = &i;
400     i = 0xbeef;
401     hr = IDispatchEx_InvokeEx(dispex, 2, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
402     ok(hr == S_OK, "got %08x\n", hr);
403     ok(i == 0xdead, "got %08x\n", i);
404
405     /* change one of the argument vts */
406     i = 0xbeef;
407     hr = IDispatchEx_InvokeEx(dispex, 3, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
408     ok(hr == DISP_E_BADCALLEE, "got %08x\n", hr);
409
410     hr = IDispatchEx_InvokeEx(dispex, 4, LOCALE_SYSTEM_DEFAULT, 0xffff, &params, NULL, NULL, NULL);
411     ok(hr == S_OK, "got %08x\n", hr);
412
413     params.cArgs = 0;
414     hr = IDispatchEx_InvokeEx(dispex, 5, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
415     ok(hr == DISP_E_EXCEPTION, "got %08x\n", hr);
416     hr = IDispatchEx_InvokeEx(dispex, 5, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, &excepinfo, NULL);
417     ok(hr == DISP_E_EXCEPTION, "got %08x\n", hr);
418     ok(excepinfo.scode == E_OUTOFMEMORY, "got scode %08x\n", excepinfo.scode);
419     ok(excepinfo.pfnDeferredFillIn == NULL, "got non-NULL pfnDeferredFillIn\n");
420
421     IDispatchEx_Release(dispex);
422     end_host_object(tid, thread);
423 }
424
425 START_TEST(marshal)
426 {
427     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
428
429     test_dispex();
430
431     CoUninitialize();
432 }