rpcrt4: Add a stub implementation of CreateProxyFromTypeInfo().
[wine.git] / dlls / rpcrt4 / cproxy.c
blob7ca05e724833bcdc0b8dd2e185dba856d40a821d
1 /*
2 * COM proxy implementation
4 * Copyright 2001 Ove Kåven, TransGaming Technologies
5 * Copyright 2009 Alexandre Julliard
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.
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.
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
21 * TODO: Handle non-i386 architectures
24 #include "config.h"
25 #include "wine/port.h"
27 #include <stdarg.h>
29 #define COBJMACROS
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winerror.h"
35 #include "objbase.h"
36 #include "rpcproxy.h"
38 #include "cpsf.h"
39 #include "ndr_misc.h"
40 #include "ndr_stubless.h"
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(ole);
45 static const IRpcProxyBufferVtbl StdProxy_Vtbl;
47 static inline StdProxyImpl *impl_from_IRpcProxyBuffer(IRpcProxyBuffer *iface)
49 return CONTAINING_RECORD(iface, StdProxyImpl, IRpcProxyBuffer_iface);
52 static inline StdProxyImpl *impl_from_proxy_obj( void *iface )
54 return CONTAINING_RECORD(iface, StdProxyImpl, PVtbl);
57 #ifdef __i386__
59 extern void call_stubless_func(void);
60 __ASM_GLOBAL_FUNC(call_stubless_func,
61 "movl 4(%esp),%ecx\n\t" /* This pointer */
62 "movl (%ecx),%ecx\n\t" /* This->lpVtbl */
63 "movl -8(%ecx),%ecx\n\t" /* MIDL_STUBLESS_PROXY_INFO */
64 "movl 8(%ecx),%edx\n\t" /* info->FormatStringOffset */
65 "movzwl (%edx,%eax,2),%edx\n\t" /* FormatStringOffset[index] */
66 "addl 4(%ecx),%edx\n\t" /* info->ProcFormatString + offset */
67 "movzbl 1(%edx),%eax\n\t" /* Oi_flags */
68 "andl $0x08,%eax\n\t" /* Oi_HAS_RPCFLAGS */
69 "shrl $1,%eax\n\t"
70 "movzwl 4(%edx,%eax),%eax\n\t" /* arguments size */
71 "pushl %eax\n\t"
72 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
73 "leal 8(%esp),%eax\n\t" /* &This */
74 "pushl %eax\n\t"
75 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
76 "pushl %edx\n\t" /* format string */
77 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
78 "pushl (%ecx)\n\t" /* info->pStubDesc */
79 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
80 "call " __ASM_NAME("ndr_client_call") "\n\t"
81 "leal 12(%esp),%esp\n\t"
82 __ASM_CFI(".cfi_adjust_cfa_offset -12\n\t")
83 "popl %edx\n\t" /* arguments size */
84 __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t")
85 "movl (%esp),%ecx\n\t" /* return address */
86 "addl %edx,%esp\n\t"
87 "jmp *%ecx" );
89 #include "pshpack1.h"
90 struct thunk
92 BYTE mov_eax;
93 DWORD index;
94 BYTE jmp;
95 LONG handler;
97 #include "poppack.h"
99 static inline void init_thunk( struct thunk *thunk, unsigned int index )
101 thunk->mov_eax = 0xb8; /* movl $n,%eax */
102 thunk->index = index;
103 thunk->jmp = 0xe9; /* jmp */
104 thunk->handler = (char *)call_stubless_func - (char *)(&thunk->handler + 1);
107 #elif defined(__x86_64__)
109 extern void call_stubless_func(void);
110 __ASM_GLOBAL_FUNC(call_stubless_func,
111 "movq %rcx,0x8(%rsp)\n\t"
112 "movq %rdx,0x10(%rsp)\n\t"
113 "movq %r8,0x18(%rsp)\n\t"
114 "movq %r9,0x20(%rsp)\n\t"
115 "leaq 0x8(%rsp),%r8\n\t" /* &This */
116 "movq (%rcx),%rcx\n\t" /* This->lpVtbl */
117 "movq -0x10(%rcx),%rcx\n\t" /* MIDL_STUBLESS_PROXY_INFO */
118 "movq 0x10(%rcx),%rdx\n\t" /* info->FormatStringOffset */
119 "movzwq (%rdx,%r10,2),%rdx\n\t" /* FormatStringOffset[index] */
120 "addq 8(%rcx),%rdx\n\t" /* info->ProcFormatString + offset */
121 "movq (%rcx),%rcx\n\t" /* info->pStubDesc */
122 "subq $0x38,%rsp\n\t"
123 __ASM_CFI(".cfi_adjust_cfa_offset 0x38\n\t")
124 "movq %xmm1,0x20(%rsp)\n\t"
125 "movq %xmm2,0x28(%rsp)\n\t"
126 "movq %xmm3,0x30(%rsp)\n\t"
127 "leaq 0x18(%rsp),%r9\n\t" /* fpu_args */
128 "call " __ASM_NAME("ndr_client_call") "\n\t"
129 "addq $0x38,%rsp\n\t"
130 __ASM_CFI(".cfi_adjust_cfa_offset -0x38\n\t")
131 "ret" );
133 #include "pshpack1.h"
134 struct thunk
136 BYTE mov_r10[3];
137 DWORD index;
138 BYTE mov_rax[2];
139 void *call_stubless;
140 BYTE jmp_rax[2];
142 #include "poppack.h"
144 static const struct thunk thunk_template =
146 { 0x49, 0xc7, 0xc2 }, 0, /* movq $index,%r10 */
147 { 0x48, 0xb8 }, 0, /* movq $call_stubless_func,%rax */
148 { 0xff, 0xe0 } /* jmp *%rax */
151 static inline void init_thunk( struct thunk *thunk, unsigned int index )
153 *thunk = thunk_template;
154 thunk->index = index;
155 thunk->call_stubless = call_stubless_func;
158 #else /* __i386__ */
160 #warning You must implement stubless proxies for your CPU
162 struct thunk
164 DWORD index;
167 static inline void init_thunk( struct thunk *thunk, unsigned int index )
169 thunk->index = index;
172 #endif /* __i386__ */
174 #define BLOCK_SIZE 1024
175 #define MAX_BLOCKS 64 /* 64k methods should be enough for anybody */
177 static const struct thunk *method_blocks[MAX_BLOCKS];
179 static const struct thunk *allocate_block( unsigned int num )
181 unsigned int i;
182 struct thunk *prev, *block;
183 DWORD oldprot;
185 block = VirtualAlloc( NULL, BLOCK_SIZE * sizeof(*block),
186 MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE );
187 if (!block) return NULL;
189 for (i = 0; i < BLOCK_SIZE; i++) init_thunk( &block[i], BLOCK_SIZE * num + i + 3 );
190 VirtualProtect( block, BLOCK_SIZE * sizeof(*block), PAGE_EXECUTE_READ, &oldprot );
191 prev = InterlockedCompareExchangePointer( (void **)&method_blocks[num], block, NULL );
192 if (prev) /* someone beat us to it */
194 VirtualFree( block, 0, MEM_RELEASE );
195 block = prev;
197 return block;
200 static BOOL fill_stubless_table( IUnknownVtbl *vtbl, DWORD num )
202 const void **entry = (const void **)(vtbl + 1);
203 DWORD i, j;
205 if (num - 3 > BLOCK_SIZE * MAX_BLOCKS)
207 FIXME( "%u methods not supported\n", num );
208 return FALSE;
210 for (i = 0; i < (num - 3 + BLOCK_SIZE - 1) / BLOCK_SIZE; i++)
212 const struct thunk *block = method_blocks[i];
213 if (!block && !(block = allocate_block( i ))) return FALSE;
214 for (j = 0; j < BLOCK_SIZE && j < num - 3 - i * BLOCK_SIZE; j++, entry++)
215 if (*entry == (LPVOID)-1) *entry = &block[j];
217 return TRUE;
220 HRESULT StdProxy_Construct(REFIID riid,
221 LPUNKNOWN pUnkOuter,
222 const ProxyFileInfo *ProxyInfo,
223 int Index,
224 LPPSFACTORYBUFFER pPSFactory,
225 LPRPCPROXYBUFFER *ppProxy,
226 LPVOID *ppvObj)
228 StdProxyImpl *This;
229 PCInterfaceName name = ProxyInfo->pNamesArray[Index];
230 CInterfaceProxyVtbl *vtbl = ProxyInfo->pProxyVtblList[Index];
232 TRACE("(%p,%p,%p,%p,%p) %s\n", pUnkOuter, vtbl, pPSFactory, ppProxy, ppvObj, name);
234 /* TableVersion = 2 means it is the stubless version of CInterfaceProxyVtbl */
235 if (ProxyInfo->TableVersion > 1) {
236 ULONG count = ProxyInfo->pStubVtblList[Index]->header.DispatchTableCount;
237 vtbl = (CInterfaceProxyVtbl *)((const void **)vtbl + 1);
238 TRACE("stubless vtbl %p: count=%d\n", vtbl->Vtbl, count );
239 fill_stubless_table( (IUnknownVtbl *)vtbl->Vtbl, count );
242 if (!IsEqualGUID(vtbl->header.piid, riid)) {
243 ERR("IID mismatch during proxy creation\n");
244 return RPC_E_UNEXPECTED;
247 This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(StdProxyImpl));
248 if (!This) return E_OUTOFMEMORY;
250 if (!pUnkOuter) pUnkOuter = (IUnknown *)This;
251 This->IRpcProxyBuffer_iface.lpVtbl = &StdProxy_Vtbl;
252 This->PVtbl = vtbl->Vtbl;
253 /* one reference for the proxy */
254 This->RefCount = 1;
255 This->piid = vtbl->header.piid;
256 This->base_object = NULL;
257 This->base_proxy = NULL;
258 This->pUnkOuter = pUnkOuter;
259 This->name = name;
260 This->pPSFactory = pPSFactory;
261 This->pChannel = NULL;
263 if(ProxyInfo->pDelegatedIIDs && ProxyInfo->pDelegatedIIDs[Index])
265 HRESULT r = create_proxy( ProxyInfo->pDelegatedIIDs[Index], NULL,
266 &This->base_proxy, (void **)&This->base_object );
267 if (FAILED(r))
269 HeapFree( GetProcessHeap(), 0, This );
270 return r;
274 *ppProxy = &This->IRpcProxyBuffer_iface;
275 *ppvObj = &This->PVtbl;
276 IUnknown_AddRef((IUnknown *)*ppvObj);
277 IPSFactoryBuffer_AddRef(pPSFactory);
279 TRACE( "iid=%s this %p proxy %p obj %p vtbl %p base proxy %p base obj %p\n",
280 debugstr_guid(riid), This, *ppProxy, *ppvObj, This->PVtbl, This->base_proxy, This->base_object );
281 return S_OK;
284 HRESULT WINAPI StdProxy_QueryInterface(IRpcProxyBuffer *iface, REFIID riid, void **obj)
286 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
287 TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(riid),obj);
289 if (IsEqualGUID(&IID_IUnknown,riid) ||
290 IsEqualGUID(This->piid,riid)) {
291 *obj = &This->PVtbl;
292 InterlockedIncrement(&This->RefCount);
293 return S_OK;
296 if (IsEqualGUID(&IID_IRpcProxyBuffer,riid)) {
297 *obj = &This->IRpcProxyBuffer_iface;
298 InterlockedIncrement(&This->RefCount);
299 return S_OK;
302 return E_NOINTERFACE;
305 ULONG WINAPI StdProxy_AddRef(IRpcProxyBuffer *iface)
307 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
308 TRACE("(%p)->AddRef()\n",This);
310 return InterlockedIncrement(&This->RefCount);
313 static ULONG WINAPI StdProxy_Release(LPRPCPROXYBUFFER iface)
315 ULONG refs;
316 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
317 TRACE("(%p)->Release()\n",This);
319 refs = InterlockedDecrement(&This->RefCount);
320 if (!refs)
322 if (This->pChannel)
323 IRpcProxyBuffer_Disconnect(&This->IRpcProxyBuffer_iface);
325 if (This->base_object) IUnknown_Release( This->base_object );
326 if (This->base_proxy) IRpcProxyBuffer_Release( This->base_proxy );
328 IPSFactoryBuffer_Release(This->pPSFactory);
329 HeapFree(GetProcessHeap(),0,This);
332 return refs;
335 HRESULT WINAPI StdProxy_Connect(IRpcProxyBuffer *iface, IRpcChannelBuffer *pChannel)
337 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
338 TRACE("(%p)->Connect(%p)\n",This,pChannel);
340 This->pChannel = pChannel;
341 IRpcChannelBuffer_AddRef(pChannel);
342 if (This->base_proxy) IRpcProxyBuffer_Connect( This->base_proxy, pChannel );
343 return S_OK;
346 void WINAPI StdProxy_Disconnect(IRpcProxyBuffer *iface)
348 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
349 TRACE("(%p)->Disconnect()\n",This);
351 if (This->base_proxy) IRpcProxyBuffer_Disconnect( This->base_proxy );
353 IRpcChannelBuffer_Release(This->pChannel);
354 This->pChannel = NULL;
357 static const IRpcProxyBufferVtbl StdProxy_Vtbl =
359 StdProxy_QueryInterface,
360 StdProxy_AddRef,
361 StdProxy_Release,
362 StdProxy_Connect,
363 StdProxy_Disconnect
366 static void StdProxy_GetChannel(LPVOID iface,
367 LPRPCCHANNELBUFFER *ppChannel)
369 StdProxyImpl *This = impl_from_proxy_obj( iface );
370 TRACE("(%p)->GetChannel(%p) %s\n",This,ppChannel,This->name);
372 *ppChannel = This->pChannel;
375 static void StdProxy_GetIID(LPVOID iface,
376 const IID **ppiid)
378 StdProxyImpl *This = impl_from_proxy_obj( iface );
379 TRACE("(%p)->GetIID(%p) %s\n",This,ppiid,This->name);
381 *ppiid = This->piid;
384 HRESULT WINAPI IUnknown_QueryInterface_Proxy(LPUNKNOWN iface,
385 REFIID riid,
386 LPVOID *ppvObj)
388 StdProxyImpl *This = impl_from_proxy_obj( iface );
389 TRACE("(%p)->QueryInterface(%s,%p) %s\n",This,debugstr_guid(riid),ppvObj,This->name);
390 return IUnknown_QueryInterface(This->pUnkOuter,riid,ppvObj);
393 ULONG WINAPI IUnknown_AddRef_Proxy(LPUNKNOWN iface)
395 StdProxyImpl *This = impl_from_proxy_obj( iface );
396 TRACE("(%p)->AddRef() %s\n",This,This->name);
397 return IUnknown_AddRef(This->pUnkOuter);
400 ULONG WINAPI IUnknown_Release_Proxy(LPUNKNOWN iface)
402 StdProxyImpl *This = impl_from_proxy_obj( iface );
403 TRACE("(%p)->Release() %s\n",This,This->name);
404 return IUnknown_Release(This->pUnkOuter);
407 /***********************************************************************
408 * NdrProxyInitialize [RPCRT4.@]
410 void WINAPI NdrProxyInitialize(void *This,
411 PRPC_MESSAGE pRpcMsg,
412 PMIDL_STUB_MESSAGE pStubMsg,
413 PMIDL_STUB_DESC pStubDescriptor,
414 unsigned int ProcNum)
416 TRACE("(%p,%p,%p,%p,%d)\n", This, pRpcMsg, pStubMsg, pStubDescriptor, ProcNum);
417 NdrClientInitializeNew(pRpcMsg, pStubMsg, pStubDescriptor, ProcNum);
418 StdProxy_GetChannel(This, &pStubMsg->pRpcChannelBuffer);
419 IRpcChannelBuffer_GetDestCtx(pStubMsg->pRpcChannelBuffer,
420 &pStubMsg->dwDestContext,
421 &pStubMsg->pvDestContext);
422 TRACE("channel=%p\n", pStubMsg->pRpcChannelBuffer);
425 /***********************************************************************
426 * NdrProxyGetBuffer [RPCRT4.@]
428 void WINAPI NdrProxyGetBuffer(void *This,
429 PMIDL_STUB_MESSAGE pStubMsg)
431 HRESULT hr;
432 const IID *riid = NULL;
434 TRACE("(%p,%p)\n", This, pStubMsg);
435 pStubMsg->RpcMsg->BufferLength = pStubMsg->BufferLength;
436 pStubMsg->dwStubPhase = PROXY_GETBUFFER;
437 StdProxy_GetIID(This, &riid);
438 hr = IRpcChannelBuffer_GetBuffer(pStubMsg->pRpcChannelBuffer,
439 (RPCOLEMESSAGE*)pStubMsg->RpcMsg,
440 riid);
441 if (FAILED(hr))
443 RpcRaiseException(hr);
444 return;
446 pStubMsg->fBufferValid = TRUE;
447 pStubMsg->BufferStart = pStubMsg->RpcMsg->Buffer;
448 pStubMsg->BufferEnd = pStubMsg->BufferStart + pStubMsg->BufferLength;
449 pStubMsg->Buffer = pStubMsg->BufferStart;
450 pStubMsg->dwStubPhase = PROXY_MARSHAL;
453 /***********************************************************************
454 * NdrProxySendReceive [RPCRT4.@]
456 void WINAPI NdrProxySendReceive(void *This,
457 PMIDL_STUB_MESSAGE pStubMsg)
459 ULONG Status = 0;
460 HRESULT hr;
462 TRACE("(%p,%p)\n", This, pStubMsg);
464 if (!pStubMsg->pRpcChannelBuffer)
466 WARN("Trying to use disconnected proxy %p\n", This);
467 RpcRaiseException(RPC_E_DISCONNECTED);
470 pStubMsg->dwStubPhase = PROXY_SENDRECEIVE;
471 /* avoid sending uninitialised parts of the buffer on the wire */
472 pStubMsg->RpcMsg->BufferLength = pStubMsg->Buffer - (unsigned char *)pStubMsg->RpcMsg->Buffer;
473 hr = IRpcChannelBuffer_SendReceive(pStubMsg->pRpcChannelBuffer,
474 (RPCOLEMESSAGE*)pStubMsg->RpcMsg,
475 &Status);
476 pStubMsg->dwStubPhase = PROXY_UNMARSHAL;
477 pStubMsg->BufferLength = pStubMsg->RpcMsg->BufferLength;
478 pStubMsg->BufferStart = pStubMsg->RpcMsg->Buffer;
479 pStubMsg->BufferEnd = pStubMsg->BufferStart + pStubMsg->BufferLength;
480 pStubMsg->Buffer = pStubMsg->BufferStart;
482 /* raise exception if call failed */
483 if (hr == RPC_S_CALL_FAILED) RpcRaiseException(*(DWORD*)pStubMsg->Buffer);
484 else if (FAILED(hr)) RpcRaiseException(hr);
487 /***********************************************************************
488 * NdrProxyFreeBuffer [RPCRT4.@]
490 void WINAPI NdrProxyFreeBuffer(void *This,
491 PMIDL_STUB_MESSAGE pStubMsg)
493 TRACE("(%p,%p)\n", This, pStubMsg);
495 if (pStubMsg->fBufferValid)
497 IRpcChannelBuffer_FreeBuffer(pStubMsg->pRpcChannelBuffer,
498 (RPCOLEMESSAGE*)pStubMsg->RpcMsg);
499 pStubMsg->fBufferValid = TRUE;
503 /***********************************************************************
504 * NdrProxyErrorHandler [RPCRT4.@]
506 HRESULT WINAPI NdrProxyErrorHandler(DWORD dwExceptionCode)
508 WARN("(0x%08x): a proxy call failed\n", dwExceptionCode);
510 if (FAILED(dwExceptionCode))
511 return dwExceptionCode;
512 else
513 return HRESULT_FROM_WIN32(dwExceptionCode);
516 HRESULT WINAPI
517 CreateStubFromTypeInfo(ITypeInfo *pTypeInfo, REFIID riid, IUnknown *pUnkServer,
518 IRpcStubBuffer **ppStub )
520 typedef INT (WINAPI *MessageBoxA)(HWND,LPCSTR,LPCSTR,UINT);
521 HMODULE hUser32 = LoadLibraryA("user32");
522 MessageBoxA pMessageBoxA = (void *)GetProcAddress(hUser32, "MessageBoxA");
524 FIXME("%p %s %p %p\n", pTypeInfo, debugstr_guid(riid), pUnkServer, ppStub);
525 if (pMessageBoxA)
527 pMessageBoxA(NULL,
528 "The native implementation of OLEAUT32.DLL cannot be used "
529 "with Wine's RPCRT4.DLL. Remove OLEAUT32.DLL and try again.\n",
530 "Wine: Unimplemented CreateProxyFromTypeInfo",
531 0x10);
532 ExitProcess(1);
534 return E_NOTIMPL;