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 "rpcproxy.h" 40 #include "wine/rpcfc.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 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 87 static inline RpcStreamImpl *impl_from_IStream(IStream *iface) 88 { 89 return CONTAINING_RECORD(iface, RpcStreamImpl, IStream_iface); 90 } 91 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 108 static ULONG WINAPI RpcStream_AddRef(LPSTREAM iface) 109 { 110 RpcStreamImpl *This = impl_from_IStream(iface); 111 return InterlockedIncrement( &This->RefCount ); 112 } 113 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 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 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 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 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 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 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 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 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 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 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 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 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 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] != RPC_FC_IP) FIXME("format=%d\n", pFormat[0]); 292 if (pFormat[1] == RPC_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 */ 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 */ 336 unsigned char * WINAPI NdrInterfacePointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg, 337 unsigned char **ppMemory, 338 PFORMAT_STRING pFormat, 339 unsigned char fMustAlloc) 340 { 341 LPSTREAM stream; 342 HRESULT hr; 343 344 TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc); 345 if (!LoadCOM()) return NULL; 346 *(LPVOID*)ppMemory = NULL; 347 if (pStubMsg->Buffer + sizeof(DWORD) < (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength) { 348 ULONG size; 349 350 hr = RpcStream_Create(pStubMsg, FALSE, &size, &stream); 351 if (hr == S_OK) { 352 if (size != 0) 353 hr = COM_UnmarshalInterface(stream, &IID_NULL, (LPVOID*)ppMemory); 354 355 IStream_Release(stream); 356 } 357 358 if (FAILED(hr)) 359 RpcRaiseException(hr); 360 } 361 return NULL; 362 } 363 364 /*********************************************************************** 365 * NdrInterfacePointerBufferSize [RPCRT4.@] 366 */ 367 void WINAPI NdrInterfacePointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg, 368 unsigned char *pMemory, 369 PFORMAT_STRING pFormat) 370 { 371 const IID *riid = get_ip_iid(pStubMsg, pMemory, pFormat); 372 ULONG size = 0; 373 374 TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat); 375 if (!LoadCOM()) return; 376 COM_GetMarshalSizeMax(&size, riid, (LPUNKNOWN)pMemory, 377 pStubMsg->dwDestContext, pStubMsg->pvDestContext, 378 MSHLFLAGS_NORMAL); 379 TRACE("size=%d\n", size); 380 pStubMsg->BufferLength += sizeof(DWORD) + size; 381 } 382 383 /*********************************************************************** 384 * NdrInterfacePointerMemorySize [RPCRT4.@] 385 */ 386 ULONG WINAPI NdrInterfacePointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg, 387 PFORMAT_STRING pFormat) 388 { 389 ULONG size; 390 391 TRACE("(%p,%p)\n", pStubMsg, pFormat); 392 393 size = *(ULONG *)pStubMsg->Buffer; 394 pStubMsg->Buffer += 4; 395 pStubMsg->MemorySize += 4; 396 397 pStubMsg->Buffer += size; 398 399 return pStubMsg->MemorySize; 400 } 401 402 /*********************************************************************** 403 * NdrInterfacePointerFree [RPCRT4.@] 404 */ 405 void WINAPI NdrInterfacePointerFree(PMIDL_STUB_MESSAGE pStubMsg, 406 unsigned char *pMemory, 407 PFORMAT_STRING pFormat) 408 { 409 LPUNKNOWN pUnk = (LPUNKNOWN)pMemory; 410 TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat); 411 if (pUnk) IUnknown_Release(pUnk); 412 } 413 414 /*********************************************************************** 415 * NdrOleAllocate [RPCRT4.@] 416 */ 417 void * WINAPI NdrOleAllocate(SIZE_T Size) 418 { 419 if (!LoadCOM()) return NULL; 420 return COM_MemAlloc(Size); 421 } 422 423 /*********************************************************************** 424 * NdrOleFree [RPCRT4.@] 425 */ 426 void WINAPI NdrOleFree(void *NodeToFree) 427 { 428 if (!LoadCOM()) return; 429 COM_MemFree(NodeToFree); 430 } 431 432 /*********************************************************************** 433 * Helper function to create a proxy. 434 * Probably similar to NdrpCreateProxy. 435 */ 436 HRESULT create_proxy(REFIID iid, IUnknown *pUnkOuter, IRpcProxyBuffer **pproxy, void **ppv) 437 { 438 CLSID clsid; 439 IPSFactoryBuffer *psfac; 440 HRESULT r; 441 442 if(!LoadCOM()) return E_FAIL; 443 444 r = COM_GetPSClsid( iid, &clsid ); 445 if(FAILED(r)) return r; 446 447 r = COM_GetClassObject( &clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (void**)&psfac ); 448 if(FAILED(r)) return r; 449 450 r = IPSFactoryBuffer_CreateProxy(psfac, pUnkOuter, iid, pproxy, ppv); 451 452 IPSFactoryBuffer_Release(psfac); 453 return r; 454 } 455 456 /*********************************************************************** 457 * Helper function to create a stub. 458 * This probably looks very much like NdrpCreateStub. 459 */ 460 HRESULT create_stub(REFIID iid, IUnknown *pUnk, IRpcStubBuffer **ppstub) 461 { 462 CLSID clsid; 463 IPSFactoryBuffer *psfac; 464 HRESULT r; 465 466 if(!LoadCOM()) return E_FAIL; 467 468 r = COM_GetPSClsid( iid, &clsid ); 469 if(FAILED(r)) return r; 470 471 r = COM_GetClassObject( &clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (void**)&psfac ); 472 if(FAILED(r)) return r; 473 474 r = IPSFactoryBuffer_CreateStub(psfac, iid, pUnk, ppstub); 475 476 IPSFactoryBuffer_Release(psfac); 477 return r; 478 } 479