1 /*
2 * OLE32 callouts, COM interface marshalling
3 *
4 * Copyright 2001 Ove Kåven, TransGaming Technologies
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 *
20 * TODO:
21 * - fix the wire-protocol to match MS/RPC
22 */
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #define COBJMACROS
29 #define NONAMELESSUNION
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winerror.h"
34
35 #include "objbase.h"
36
37 #include "ndr_misc.h"
38 #include "rpcndr.h"
39 #include "ndrtypes.h"
40 #include "rpcproxy.h"
41 #include "cpsf.h"
42
43 #include "wine/debug.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(ole);
46
47 static HMODULE hOLE;
48
49 static HRESULT (WINAPI *COM_GetMarshalSizeMax)(ULONG *,REFIID,LPUNKNOWN,DWORD,LPVOID,DWORD);
50 static HRESULT (WINAPI *COM_MarshalInterface)(LPSTREAM,REFIID,LPUNKNOWN,DWORD,LPVOID,DWORD);
51 static HRESULT (WINAPI *COM_UnmarshalInterface)(LPSTREAM,REFIID,LPVOID*);
52 static HRESULT (WINAPI *COM_ReleaseMarshalData)(LPSTREAM);
53 static HRESULT (WINAPI *COM_GetClassObject)(REFCLSID,DWORD,COSERVERINFO *,REFIID,LPVOID *);
54 static HRESULT (WINAPI *COM_GetPSClsid)(REFIID,CLSID *);
55 static LPVOID (WINAPI *COM_MemAlloc)(ULONG);
56 static void (WINAPI *COM_MemFree)(LPVOID);
57
LoadCOM(void)58 static HMODULE LoadCOM(void)
59 {
60 if (hOLE) return hOLE;
61 hOLE = LoadLibraryA("OLE32.DLL");
62 if (!hOLE) return 0;
63 COM_GetMarshalSizeMax = (LPVOID)GetProcAddress(hOLE, "CoGetMarshalSizeMax");
64 COM_MarshalInterface = (LPVOID)GetProcAddress(hOLE, "CoMarshalInterface");
65 COM_UnmarshalInterface = (LPVOID)GetProcAddress(hOLE, "CoUnmarshalInterface");
66 COM_ReleaseMarshalData = (LPVOID)GetProcAddress(hOLE, "CoReleaseMarshalData");
67 COM_GetClassObject = (LPVOID)GetProcAddress(hOLE, "CoGetClassObject");
68 COM_GetPSClsid = (LPVOID)GetProcAddress(hOLE, "CoGetPSClsid");
69 COM_MemAlloc = (LPVOID)GetProcAddress(hOLE, "CoTaskMemAlloc");
70 COM_MemFree = (LPVOID)GetProcAddress(hOLE, "CoTaskMemFree");
71 return hOLE;
72 }
73
74 /* CoMarshalInterface/CoUnmarshalInterface works on streams,
75 * so implement a simple stream on top of the RPC buffer
76 * (which also implements the MInterfacePointer structure) */
77 typedef struct RpcStreamImpl
78 {
79 IStream IStream_iface;
80 LONG RefCount;
81 PMIDL_STUB_MESSAGE pMsg;
82 LPDWORD size;
83 unsigned char *data;
84 DWORD pos;
85 } RpcStreamImpl;
86
impl_from_IStream(IStream * iface)87 static inline RpcStreamImpl *impl_from_IStream(IStream *iface)
88 {
89 return CONTAINING_RECORD(iface, RpcStreamImpl, IStream_iface);
90 }
91
RpcStream_QueryInterface(LPSTREAM iface,REFIID riid,LPVOID * obj)92 static HRESULT WINAPI RpcStream_QueryInterface(LPSTREAM iface,
93 REFIID riid,
94 LPVOID *obj)
95 {
96 if (IsEqualGUID(&IID_IUnknown, riid) ||
97 IsEqualGUID(&IID_ISequentialStream, riid) ||
98 IsEqualGUID(&IID_IStream, riid)) {
99 *obj = iface;
100 IStream_AddRef(iface);
101 return S_OK;
102 }
103
104 *obj = NULL;
105 return E_NOINTERFACE;
106 }
107
RpcStream_AddRef(LPSTREAM iface)108 static ULONG WINAPI RpcStream_AddRef(LPSTREAM iface)
109 {
110 RpcStreamImpl *This = impl_from_IStream(iface);
111 return InterlockedIncrement( &This->RefCount );
112 }
113
RpcStream_Release(LPSTREAM iface)114 static ULONG WINAPI RpcStream_Release(LPSTREAM iface)
115 {
116 RpcStreamImpl *This = impl_from_IStream(iface);
117 ULONG ref = InterlockedDecrement( &This->RefCount );
118 if (!ref) {
119 TRACE("size=%d\n", *This->size);
120 This->pMsg->Buffer = This->data + *This->size;
121 HeapFree(GetProcessHeap(),0,This);
122 }
123 return ref;
124 }
125
RpcStream_Read(LPSTREAM iface,void * pv,ULONG cb,ULONG * pcbRead)126 static HRESULT WINAPI RpcStream_Read(LPSTREAM iface,
127 void *pv,
128 ULONG cb,
129 ULONG *pcbRead)
130 {
131 RpcStreamImpl *This = impl_from_IStream(iface);
132 HRESULT hr = S_OK;
133 if (This->pos + cb > *This->size)
134 {
135 cb = *This->size - This->pos;
136 hr = S_FALSE;
137 }
138 if (cb) {
139 memcpy(pv, This->data + This->pos, cb);
140 This->pos += cb;
141 }
142 if (pcbRead) *pcbRead = cb;
143 return hr;
144 }
145
RpcStream_Write(LPSTREAM iface,const void * pv,ULONG cb,ULONG * pcbWritten)146 static HRESULT WINAPI RpcStream_Write(LPSTREAM iface,
147 const void *pv,
148 ULONG cb,
149 ULONG *pcbWritten)
150 {
151 RpcStreamImpl *This = impl_from_IStream(iface);
152 if (This->data + cb > (unsigned char *)This->pMsg->RpcMsg->Buffer + This->pMsg->BufferLength)
153 return STG_E_MEDIUMFULL;
154 memcpy(This->data + This->pos, pv, cb);
155 This->pos += cb;
156 if (This->pos > *This->size) *This->size = This->pos;
157 if (pcbWritten) *pcbWritten = cb;
158 return S_OK;
159 }
160
RpcStream_Seek(LPSTREAM iface,LARGE_INTEGER move,DWORD origin,ULARGE_INTEGER * newPos)161 static HRESULT WINAPI RpcStream_Seek(LPSTREAM iface,
162 LARGE_INTEGER move,
163 DWORD origin,
164 ULARGE_INTEGER *newPos)
165 {
166 RpcStreamImpl *This = impl_from_IStream(iface);
167 switch (origin) {
168 case STREAM_SEEK_SET:
169 This->pos = move.u.LowPart;
170 break;
171 case STREAM_SEEK_CUR:
172 This->pos = This->pos + move.u.LowPart;
173 break;
174 case STREAM_SEEK_END:
175 This->pos = *This->size + move.u.LowPart;
176 break;
177 default:
178 return STG_E_INVALIDFUNCTION;
179 }
180 if (newPos) {
181 newPos->u.LowPart = This->pos;
182 newPos->u.HighPart = 0;
183 }
184 return S_OK;
185 }
186
RpcStream_SetSize(LPSTREAM iface,ULARGE_INTEGER newSize)187 static HRESULT WINAPI RpcStream_SetSize(LPSTREAM iface,
188 ULARGE_INTEGER newSize)
189 {
190 RpcStreamImpl *This = impl_from_IStream(iface);
191 *This->size = newSize.u.LowPart;
192 return S_OK;
193 }
194
RpcStream_CopyTo(IStream * iface,IStream * dest,ULARGE_INTEGER len,ULARGE_INTEGER * read,ULARGE_INTEGER * written)195 static HRESULT WINAPI RpcStream_CopyTo(IStream *iface, IStream *dest,
196 ULARGE_INTEGER len, ULARGE_INTEGER *read, ULARGE_INTEGER *written)
197 {
198 RpcStreamImpl *This = impl_from_IStream(iface);
199 FIXME("(%p): stub\n", This);
200 return E_NOTIMPL;
201 }
202
RpcStream_Commit(IStream * iface,DWORD flags)203 static HRESULT WINAPI RpcStream_Commit(IStream *iface, DWORD flags)
204 {
205 RpcStreamImpl *This = impl_from_IStream(iface);
206 FIXME("(%p)->(0x%08x): stub\n", This, flags);
207 return E_NOTIMPL;
208 }
209
RpcStream_Revert(IStream * iface)210 static HRESULT WINAPI RpcStream_Revert(IStream *iface)
211 {
212 RpcStreamImpl *This = impl_from_IStream(iface);
213 FIXME("(%p): stub\n", This);
214 return E_NOTIMPL;
215 }
216
RpcStream_LockRegion(IStream * iface,ULARGE_INTEGER offset,ULARGE_INTEGER len,DWORD locktype)217 static HRESULT WINAPI RpcStream_LockRegion(IStream *iface,
218 ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype)
219 {
220 RpcStreamImpl *This = impl_from_IStream(iface);
221 FIXME("(%p): stub\n", This);
222 return E_NOTIMPL;
223 }
224
RpcStream_UnlockRegion(IStream * iface,ULARGE_INTEGER offset,ULARGE_INTEGER len,DWORD locktype)225 static HRESULT WINAPI RpcStream_UnlockRegion(IStream *iface,
226 ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype)
227 {
228 RpcStreamImpl *This = impl_from_IStream(iface);
229 FIXME("(%p): stub\n", This);
230 return E_NOTIMPL;
231 }
232
RpcStream_Stat(IStream * iface,STATSTG * stat,DWORD flag)233 static HRESULT WINAPI RpcStream_Stat(IStream *iface, STATSTG *stat, DWORD flag)
234 {
235 RpcStreamImpl *This = impl_from_IStream(iface);
236 FIXME("(%p): stub\n", This);
237 return E_NOTIMPL;
238 }
239
RpcStream_Clone(IStream * iface,IStream ** cloned)240 static HRESULT WINAPI RpcStream_Clone(IStream *iface, IStream **cloned)
241 {
242 RpcStreamImpl *This = impl_from_IStream(iface);
243 FIXME("(%p): stub\n", This);
244 return E_NOTIMPL;
245 }
246
247 static const IStreamVtbl RpcStream_Vtbl =
248 {
249 RpcStream_QueryInterface,
250 RpcStream_AddRef,
251 RpcStream_Release,
252 RpcStream_Read,
253 RpcStream_Write,
254 RpcStream_Seek,
255 RpcStream_SetSize,
256 RpcStream_CopyTo,
257 RpcStream_Commit,
258 RpcStream_Revert,
259 RpcStream_LockRegion,
260 RpcStream_UnlockRegion,
261 RpcStream_Stat,
262 RpcStream_Clone
263 };
264
RpcStream_Create(PMIDL_STUB_MESSAGE pStubMsg,BOOL init,ULONG * size,IStream ** stream)265 static HRESULT RpcStream_Create(PMIDL_STUB_MESSAGE pStubMsg, BOOL init, ULONG *size, IStream **stream)
266 {
267 RpcStreamImpl *This;
268
269 *stream = NULL;
270 This = HeapAlloc(GetProcessHeap(), 0, sizeof(RpcStreamImpl));
271 if (!This) return E_OUTOFMEMORY;
272 This->IStream_iface.lpVtbl = &RpcStream_Vtbl;
273 This->RefCount = 1;
274 This->pMsg = pStubMsg;
275 This->size = (LPDWORD)pStubMsg->Buffer;
276 This->data = pStubMsg->Buffer + sizeof(DWORD);
277 This->pos = 0;
278 if (init) *This->size = 0;
279 TRACE("init size=%d\n", *This->size);
280
281 if (size) *size = *This->size;
282 *stream = &This->IStream_iface;
283 return S_OK;
284 }
285
get_ip_iid(PMIDL_STUB_MESSAGE pStubMsg,unsigned char * pMemory,PFORMAT_STRING pFormat)286 static const IID* get_ip_iid(PMIDL_STUB_MESSAGE pStubMsg, unsigned char *pMemory, PFORMAT_STRING pFormat)
287 {
288 const IID *riid;
289 if (!pFormat) return &IID_IUnknown;
290 TRACE("format=%02x %02x\n", pFormat[0], pFormat[1]);
291 if (pFormat[0] != FC_IP) FIXME("format=%d\n", pFormat[0]);
292 if (pFormat[1] == FC_CONSTANT_IID) {
293 riid = (const IID *)&pFormat[2];
294 } else {
295 ComputeConformance(pStubMsg, pMemory, pFormat+2, 0);
296 riid = (const IID *)pStubMsg->MaxCount;
297 }
298 if (!riid) riid = &IID_IUnknown;
299 TRACE("got %s\n", debugstr_guid(riid));
300 return riid;
301 }
302
303 /***********************************************************************
304 * NdrInterfacePointerMarshall [RPCRT4.@]
305 */
NdrInterfacePointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,unsigned char * pMemory,PFORMAT_STRING pFormat)306 unsigned char * WINAPI NdrInterfacePointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,
307 unsigned char *pMemory,
308 PFORMAT_STRING pFormat)
309 {
310 const IID *riid = get_ip_iid(pStubMsg, pMemory, pFormat);
311 LPSTREAM stream;
312 HRESULT hr;
313
314 TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
315 pStubMsg->MaxCount = 0;
316 if (!LoadCOM()) return NULL;
317 if (pStubMsg->Buffer + sizeof(DWORD) <= (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength) {
318 hr = RpcStream_Create(pStubMsg, TRUE, NULL, &stream);
319 if (hr == S_OK) {
320 if (pMemory)
321 hr = COM_MarshalInterface(stream, riid, (LPUNKNOWN)pMemory,
322 pStubMsg->dwDestContext, pStubMsg->pvDestContext,
323 MSHLFLAGS_NORMAL);
324 IStream_Release(stream);
325 }
326
327 if (FAILED(hr))
328 RpcRaiseException(hr);
329 }
330 return NULL;
331 }
332
333 /***********************************************************************
334 * NdrInterfacePointerUnmarshall [RPCRT4.@]
335 */
NdrInterfacePointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,unsigned char ** ppMemory,PFORMAT_STRING pFormat,unsigned char fMustAlloc)336 unsigned char * WINAPI NdrInterfacePointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
337 unsigned char **ppMemory,
338 PFORMAT_STRING pFormat,
339 unsigned char fMustAlloc)
340 {
341 IUnknown **unk = (IUnknown **)ppMemory;
342 LPSTREAM stream;
343 HRESULT hr;
344
345 TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
346 if (!LoadCOM()) return NULL;
347
348 /* Avoid reference leaks for [in, out] pointers. */
349 if (pStubMsg->IsClient && *unk)
350 IUnknown_Release(*unk);
351
352 *unk = NULL;
353 if (pStubMsg->Buffer + sizeof(DWORD) < (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength) {
354 ULONG size;
355
356 hr = RpcStream_Create(pStubMsg, FALSE, &size, &stream);
357 if (hr == S_OK) {
358 if (size != 0)
359 hr = COM_UnmarshalInterface(stream, &IID_NULL, (void **)unk);
360
361 IStream_Release(stream);
362 }
363
364 if (FAILED(hr))
365 RpcRaiseException(hr);
366 }
367 return NULL;
368 }
369
370 /***********************************************************************
371 * NdrInterfacePointerBufferSize [RPCRT4.@]
372 */
NdrInterfacePointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,unsigned char * pMemory,PFORMAT_STRING pFormat)373 void WINAPI NdrInterfacePointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
374 unsigned char *pMemory,
375 PFORMAT_STRING pFormat)
376 {
377 const IID *riid = get_ip_iid(pStubMsg, pMemory, pFormat);
378 ULONG size = 0;
379
380 TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
381 if (!LoadCOM()) return;
382 COM_GetMarshalSizeMax(&size, riid, (LPUNKNOWN)pMemory,
383 pStubMsg->dwDestContext, pStubMsg->pvDestContext,
384 MSHLFLAGS_NORMAL);
385 TRACE("size=%d\n", size);
386 pStubMsg->BufferLength += sizeof(DWORD) + size;
387 }
388
389 /***********************************************************************
390 * NdrInterfacePointerMemorySize [RPCRT4.@]
391 */
NdrInterfacePointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,PFORMAT_STRING pFormat)392 ULONG WINAPI NdrInterfacePointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
393 PFORMAT_STRING pFormat)
394 {
395 ULONG size;
396
397 TRACE("(%p,%p)\n", pStubMsg, pFormat);
398
399 size = *(ULONG *)pStubMsg->Buffer;
400 pStubMsg->Buffer += 4;
401 pStubMsg->MemorySize += 4;
402
403 pStubMsg->Buffer += size;
404
405 return pStubMsg->MemorySize;
406 }
407
408 /***********************************************************************
409 * NdrInterfacePointerFree [RPCRT4.@]
410 */
NdrInterfacePointerFree(PMIDL_STUB_MESSAGE pStubMsg,unsigned char * pMemory,PFORMAT_STRING pFormat)411 void WINAPI NdrInterfacePointerFree(PMIDL_STUB_MESSAGE pStubMsg,
412 unsigned char *pMemory,
413 PFORMAT_STRING pFormat)
414 {
415 LPUNKNOWN pUnk = (LPUNKNOWN)pMemory;
416 TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
417 if (pUnk) IUnknown_Release(pUnk);
418 }
419
420 /***********************************************************************
421 * NdrOleAllocate [RPCRT4.@]
422 */
NdrOleAllocate(SIZE_T Size)423 void * WINAPI NdrOleAllocate(SIZE_T Size)
424 {
425 if (!LoadCOM()) return NULL;
426 return COM_MemAlloc(Size);
427 }
428
429 /***********************************************************************
430 * NdrOleFree [RPCRT4.@]
431 */
NdrOleFree(void * NodeToFree)432 void WINAPI NdrOleFree(void *NodeToFree)
433 {
434 if (!LoadCOM()) return;
435 COM_MemFree(NodeToFree);
436 }
437
438 /***********************************************************************
439 * Helper function to create a proxy.
440 * Probably similar to NdrpCreateProxy.
441 */
create_proxy(REFIID iid,IUnknown * pUnkOuter,IRpcProxyBuffer ** pproxy,void ** ppv)442 HRESULT create_proxy(REFIID iid, IUnknown *pUnkOuter, IRpcProxyBuffer **pproxy, void **ppv)
443 {
444 CLSID clsid;
445 IPSFactoryBuffer *psfac;
446 HRESULT r;
447
448 if(!LoadCOM()) return E_FAIL;
449
450 r = COM_GetPSClsid( iid, &clsid );
451 if(FAILED(r)) return r;
452
453 r = COM_GetClassObject( &clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (void**)&psfac );
454 if(FAILED(r)) return r;
455
456 r = IPSFactoryBuffer_CreateProxy(psfac, pUnkOuter, iid, pproxy, ppv);
457
458 IPSFactoryBuffer_Release(psfac);
459 return r;
460 }
461
462 /***********************************************************************
463 * Helper function to create a stub.
464 * This probably looks very much like NdrpCreateStub.
465 */
create_stub(REFIID iid,IUnknown * pUnk,IRpcStubBuffer ** ppstub)466 HRESULT create_stub(REFIID iid, IUnknown *pUnk, IRpcStubBuffer **ppstub)
467 {
468 CLSID clsid;
469 IPSFactoryBuffer *psfac;
470 HRESULT r;
471
472 if(!LoadCOM()) return E_FAIL;
473
474 r = COM_GetPSClsid( iid, &clsid );
475 if(FAILED(r)) return r;
476
477 r = COM_GetClassObject( &clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (void**)&psfac );
478 if(FAILED(r)) return r;
479
480 r = IPSFactoryBuffer_CreateStub(psfac, iid, pUnk, ppstub);
481
482 IPSFactoryBuffer_Release(psfac);
483 return r;
484 }
485