xref: /reactos/dll/win32/rpcrt4/cproxy.c (revision 07608028)
1 /*
2  * COM proxy implementation
3  *
4  * Copyright 2001 Ove Kåven, TransGaming Technologies
5  * Copyright 2009 Alexandre Julliard
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 #include "config.h"
23 #include "wine/port.h"
24 
25 #include <stdarg.h>
26 
27 #define COBJMACROS
28 
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winerror.h"
32 
33 #include "objbase.h"
34 #include "rpcproxy.h"
35 
36 #include "cpsf.h"
37 #include "ndr_misc.h"
38 #include "ndr_stubless.h"
39 #include "wine/debug.h"
40 
41 WINE_DEFAULT_DEBUG_CHANNEL(ole);
42 
43 static const IRpcProxyBufferVtbl StdProxy_Vtbl;
44 
45 static inline StdProxyImpl *impl_from_IRpcProxyBuffer(IRpcProxyBuffer *iface)
46 {
47     return CONTAINING_RECORD(iface, StdProxyImpl, IRpcProxyBuffer_iface);
48 }
49 
50 static inline StdProxyImpl *impl_from_proxy_obj( void *iface )
51 {
52     return CONTAINING_RECORD(iface, StdProxyImpl, PVtbl);
53 }
54 
55 #ifdef __i386__
56 
57 extern void call_stubless_func(void);
58 __ASM_GLOBAL_FUNC(call_stubless_func,
59                   "movl 4(%esp),%ecx\n\t"         /* This pointer */
60                   "movl (%ecx),%ecx\n\t"          /* This->lpVtbl */
61                   "movl -8(%ecx),%ecx\n\t"        /* MIDL_STUBLESS_PROXY_INFO */
62                   "movl 8(%ecx),%edx\n\t"         /* info->FormatStringOffset */
63                   "movzwl (%edx,%eax,2),%edx\n\t" /* FormatStringOffset[index] */
64                   "addl 4(%ecx),%edx\n\t"         /* info->ProcFormatString + offset */
65                   "movzbl 1(%edx),%eax\n\t"       /* Oi_flags */
66                   "andl $0x08,%eax\n\t"           /* Oi_HAS_RPCFLAGS */
67                   "shrl $1,%eax\n\t"
68                   "movzwl 4(%edx,%eax),%eax\n\t"  /* arguments size */
69                   "pushl %eax\n\t"
70                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
71                   "leal 8(%esp),%eax\n\t"         /* &This */
72                   "pushl %eax\n\t"
73                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
74                   "pushl %edx\n\t"                /* format string */
75                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
76                   "pushl (%ecx)\n\t"              /* info->pStubDesc */
77                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
78                   "call " __ASM_NAME("ndr_client_call") "\n\t"
79                   "leal 12(%esp),%esp\n\t"
80                   __ASM_CFI(".cfi_adjust_cfa_offset -12\n\t")
81                   "popl %edx\n\t"                 /* arguments size */
82                   __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t")
83                   "movl (%esp),%ecx\n\t"  /* return address */
84                   "addl %edx,%esp\n\t"
85                   "jmp *%ecx" );
86 
87 #include "pshpack1.h"
88 struct thunk
89 {
90   BYTE mov_eax;
91   DWORD index;
92   BYTE jmp;
93   LONG handler;
94 };
95 #include "poppack.h"
96 
97 static inline void init_thunk( struct thunk *thunk, unsigned int index )
98 {
99     thunk->mov_eax = 0xb8; /* movl $n,%eax */
100     thunk->index   = index;
101     thunk->jmp     = 0xe9; /* jmp */
102     thunk->handler = (char *)call_stubless_func - (char *)(&thunk->handler + 1);
103 }
104 
105 #elif defined(__x86_64__)
106 
107 extern void call_stubless_func(void);
108 __ASM_GLOBAL_FUNC(call_stubless_func,
109                   "movq %rcx,0x8(%rsp)\n\t"
110                   "movq %rdx,0x10(%rsp)\n\t"
111                   "movq %r8,0x18(%rsp)\n\t"
112                   "movq %r9,0x20(%rsp)\n\t"
113                   "leaq 0x8(%rsp),%r8\n\t"        /* &This */
114                   "movq (%rcx),%rcx\n\t"          /* This->lpVtbl */
115                   "movq -0x10(%rcx),%rcx\n\t"     /* MIDL_STUBLESS_PROXY_INFO */
116                   "movq 0x10(%rcx),%rdx\n\t"      /* info->FormatStringOffset */
117                   "movzwq (%rdx,%r10,2),%rdx\n\t" /* FormatStringOffset[index] */
118                   "addq 8(%rcx),%rdx\n\t"         /* info->ProcFormatString + offset */
119                   "movq (%rcx),%rcx\n\t"          /* info->pStubDesc */
120                   "subq $0x38,%rsp\n\t"
121                   __ASM_CFI(".cfi_adjust_cfa_offset 0x38\n\t")
122                   "movq %xmm1,0x20(%rsp)\n\t"
123                   "movq %xmm2,0x28(%rsp)\n\t"
124                   "movq %xmm3,0x30(%rsp)\n\t"
125                   "leaq 0x18(%rsp),%r9\n\t"       /* fpu_args */
126                   "call " __ASM_NAME("ndr_client_call") "\n\t"
127                   "addq $0x38,%rsp\n\t"
128                   __ASM_CFI(".cfi_adjust_cfa_offset -0x38\n\t")
129                   "ret" );
130 
131 #include "pshpack1.h"
132 struct thunk
133 {
134     BYTE mov_r10[3];
135     DWORD index;
136     BYTE mov_rax[2];
137     void *call_stubless;
138     BYTE jmp_rax[2];
139 };
140 #include "poppack.h"
141 
142 static const struct thunk thunk_template =
143 {
144     { 0x49, 0xc7, 0xc2 }, 0,  /* movq $index,%r10 */
145     { 0x48, 0xb8 }, 0,        /* movq $call_stubless_func,%rax */
146     { 0xff, 0xe0 }            /* jmp *%rax */
147 };
148 
149 static inline void init_thunk( struct thunk *thunk, unsigned int index )
150 {
151     *thunk = thunk_template;
152     thunk->index = index;
153     thunk->call_stubless = call_stubless_func;
154 }
155 
156 #elif defined(__arm__)
157 
158 extern void call_stubless_func(void);
159 __ASM_GLOBAL_FUNC(call_stubless_func,
160                   "push {r0-r3}\n\t"
161                   "mov r2, sp\n\t"              /* stack_top */
162                   "push {fp,lr}\n\t"
163                   "mov fp, sp\n\t"
164                   "ldr r0, [r0]\n\t"            /* This->lpVtbl */
165                   "ldr r0, [r0,#-8]\n\t"        /* MIDL_STUBLESS_PROXY_INFO */
166                   "ldr r1, [r0,#8]\n\t"         /* info->FormatStringOffset */
167                   "ldrh r1, [r1,ip]\n\t"        /* info->FormatStringOffset[index] */
168                   "ldr ip, [r0,#4]\n\t"         /* info->ProcFormatString */
169                   "add r1, ip\n\t"              /* info->ProcFormatString + offset */
170                   "ldr r0, [r0]\n\t"            /* info->pStubDesc */
171 #ifdef __SOFTFP__
172                   "mov r3, #0\n\t"
173 #else
174                   "vpush {s0-s15}\n\t"          /* store the s0-s15/d0-d7 arguments */
175                   "mov r3, sp\n\t"              /* fpu_stack */
176 #endif
177                   "bl " __ASM_NAME("ndr_client_call") "\n\t"
178                   "mov sp, fp\n\t"
179                   "pop {fp,lr}\n\t"
180                   "add sp, #16\n\t"
181                   "bx lr" );
182 
183 struct thunk
184 {
185     DWORD ldr_ip;         /* ldr ip,[pc] */
186     DWORD ldr_pc;         /* ldr pc,[pc] */
187     DWORD index;
188     void *func;
189 };
190 
191 static inline void init_thunk( struct thunk *thunk, unsigned int index )
192 {
193     thunk->ldr_ip = 0xe59fc000; /* ldr ip,[pc] */
194     thunk->ldr_pc = 0xe59ff000; /* ldr pc,[pc] */
195     thunk->index  = index * sizeof(unsigned short);
196     thunk->func   = call_stubless_func;
197 }
198 
199 #else  /* __i386__ */
200 
201 #warning You must implement stubless proxies for your CPU
202 
203 struct thunk
204 {
205   DWORD index;
206 };
207 
208 static inline void init_thunk( struct thunk *thunk, unsigned int index )
209 {
210     thunk->index = index;
211 }
212 
213 #endif  /* __i386__ */
214 
215 #define BLOCK_SIZE 1024
216 #define MAX_BLOCKS 64  /* 64k methods should be enough for anybody */
217 
218 static const struct thunk *method_blocks[MAX_BLOCKS];
219 
220 static const struct thunk *allocate_block( unsigned int num )
221 {
222     unsigned int i;
223     struct thunk *prev, *block;
224     DWORD oldprot;
225 
226     block = VirtualAlloc( NULL, BLOCK_SIZE * sizeof(*block),
227                           MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE );
228     if (!block) return NULL;
229 
230     for (i = 0; i < BLOCK_SIZE; i++) init_thunk( &block[i], BLOCK_SIZE * num + i + 3 );
231     VirtualProtect( block, BLOCK_SIZE * sizeof(*block), PAGE_EXECUTE_READ, &oldprot );
232     prev = InterlockedCompareExchangePointer( (void **)&method_blocks[num], block, NULL );
233     if (prev) /* someone beat us to it */
234     {
235         VirtualFree( block, 0, MEM_RELEASE );
236         block = prev;
237     }
238     return block;
239 }
240 
241 BOOL fill_stubless_table( IUnknownVtbl *vtbl, DWORD num )
242 {
243     const void **entry = (const void **)(vtbl + 1);
244     DWORD i, j;
245 
246     if (num - 3 > BLOCK_SIZE * MAX_BLOCKS)
247     {
248         FIXME( "%u methods not supported\n", num );
249         return FALSE;
250     }
251     for (i = 0; i < (num - 3 + BLOCK_SIZE - 1) / BLOCK_SIZE; i++)
252     {
253         const struct thunk *block = method_blocks[i];
254         if (!block && !(block = allocate_block( i ))) return FALSE;
255         for (j = 0; j < BLOCK_SIZE && j < num - 3 - i * BLOCK_SIZE; j++, entry++)
256             if (*entry == (LPVOID)-1) *entry = &block[j];
257     }
258     return TRUE;
259 }
260 
261 HRESULT StdProxy_Construct(REFIID riid,
262                            LPUNKNOWN pUnkOuter,
263                            const ProxyFileInfo *ProxyInfo,
264                            int Index,
265                            LPPSFACTORYBUFFER pPSFactory,
266                            LPRPCPROXYBUFFER *ppProxy,
267                            LPVOID *ppvObj)
268 {
269   StdProxyImpl *This;
270   PCInterfaceName name = ProxyInfo->pNamesArray[Index];
271   CInterfaceProxyVtbl *vtbl = ProxyInfo->pProxyVtblList[Index];
272 
273   TRACE("(%p,%p,%p,%p,%p) %s\n", pUnkOuter, vtbl, pPSFactory, ppProxy, ppvObj, name);
274 
275   /* TableVersion = 2 means it is the stubless version of CInterfaceProxyVtbl */
276   if (ProxyInfo->TableVersion > 1) {
277     ULONG count = ProxyInfo->pStubVtblList[Index]->header.DispatchTableCount;
278     vtbl = (CInterfaceProxyVtbl *)((const void **)vtbl + 1);
279     TRACE("stubless vtbl %p: count=%d\n", vtbl->Vtbl, count );
280     fill_stubless_table( (IUnknownVtbl *)vtbl->Vtbl, count );
281   }
282 
283   if (!IsEqualGUID(vtbl->header.piid, riid)) {
284     ERR("IID mismatch during proxy creation\n");
285     return RPC_E_UNEXPECTED;
286   }
287 
288   This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(StdProxyImpl));
289   if (!This) return E_OUTOFMEMORY;
290 
291   if (!pUnkOuter) pUnkOuter = (IUnknown *)This;
292   This->IRpcProxyBuffer_iface.lpVtbl = &StdProxy_Vtbl;
293   This->PVtbl = vtbl->Vtbl;
294   /* one reference for the proxy */
295   This->RefCount = 1;
296   This->piid = vtbl->header.piid;
297   This->base_object = NULL;
298   This->base_proxy = NULL;
299   This->pUnkOuter = pUnkOuter;
300   This->name = name;
301   This->pPSFactory = pPSFactory;
302   This->pChannel = NULL;
303 
304   if(ProxyInfo->pDelegatedIIDs && ProxyInfo->pDelegatedIIDs[Index])
305   {
306       HRESULT r = create_proxy( ProxyInfo->pDelegatedIIDs[Index], NULL,
307                                 &This->base_proxy, (void **)&This->base_object );
308       if (FAILED(r))
309       {
310           HeapFree( GetProcessHeap(), 0, This );
311           return r;
312       }
313   }
314 
315   *ppProxy = &This->IRpcProxyBuffer_iface;
316   *ppvObj = &This->PVtbl;
317   IUnknown_AddRef((IUnknown *)*ppvObj);
318   IPSFactoryBuffer_AddRef(pPSFactory);
319 
320   TRACE( "iid=%s this %p proxy %p obj %p vtbl %p base proxy %p base obj %p\n",
321          debugstr_guid(riid), This, *ppProxy, *ppvObj, This->PVtbl, This->base_proxy, This->base_object );
322   return S_OK;
323 }
324 
325 HRESULT WINAPI StdProxy_QueryInterface(IRpcProxyBuffer *iface, REFIID riid, void **obj)
326 {
327   StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
328   TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(riid),obj);
329 
330   if (IsEqualGUID(&IID_IUnknown,riid) ||
331       IsEqualGUID(This->piid,riid)) {
332     *obj = &This->PVtbl;
333     InterlockedIncrement(&This->RefCount);
334     return S_OK;
335   }
336 
337   if (IsEqualGUID(&IID_IRpcProxyBuffer,riid)) {
338     *obj = &This->IRpcProxyBuffer_iface;
339     InterlockedIncrement(&This->RefCount);
340     return S_OK;
341   }
342 
343   return E_NOINTERFACE;
344 }
345 
346 ULONG WINAPI StdProxy_AddRef(IRpcProxyBuffer *iface)
347 {
348   StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
349   TRACE("(%p)->AddRef()\n",This);
350 
351   return InterlockedIncrement(&This->RefCount);
352 }
353 
354 static ULONG WINAPI StdProxy_Release(LPRPCPROXYBUFFER iface)
355 {
356   ULONG refs;
357   StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
358   TRACE("(%p)->Release()\n",This);
359 
360   refs = InterlockedDecrement(&This->RefCount);
361   if (!refs)
362   {
363     if (This->pChannel)
364       IRpcProxyBuffer_Disconnect(&This->IRpcProxyBuffer_iface);
365 
366     if (This->base_object) IUnknown_Release( This->base_object );
367     if (This->base_proxy) IRpcProxyBuffer_Release( This->base_proxy );
368 
369     IPSFactoryBuffer_Release(This->pPSFactory);
370     HeapFree(GetProcessHeap(),0,This);
371   }
372 
373   return refs;
374 }
375 
376 HRESULT WINAPI StdProxy_Connect(IRpcProxyBuffer *iface, IRpcChannelBuffer *pChannel)
377 {
378   StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
379   TRACE("(%p)->Connect(%p)\n",This,pChannel);
380 
381   This->pChannel = pChannel;
382   IRpcChannelBuffer_AddRef(pChannel);
383   if (This->base_proxy) IRpcProxyBuffer_Connect( This->base_proxy, pChannel );
384   return S_OK;
385 }
386 
387 void WINAPI StdProxy_Disconnect(IRpcProxyBuffer *iface)
388 {
389   StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface);
390   TRACE("(%p)->Disconnect()\n",This);
391 
392   if (This->base_proxy) IRpcProxyBuffer_Disconnect( This->base_proxy );
393 
394   IRpcChannelBuffer_Release(This->pChannel);
395   This->pChannel = NULL;
396 }
397 
398 static const IRpcProxyBufferVtbl StdProxy_Vtbl =
399 {
400   StdProxy_QueryInterface,
401   StdProxy_AddRef,
402   StdProxy_Release,
403   StdProxy_Connect,
404   StdProxy_Disconnect
405 };
406 
407 static void StdProxy_GetChannel(LPVOID iface,
408                                    LPRPCCHANNELBUFFER *ppChannel)
409 {
410   StdProxyImpl *This = impl_from_proxy_obj( iface );
411   TRACE("(%p)->GetChannel(%p) %s\n",This,ppChannel,This->name);
412 
413   *ppChannel = This->pChannel;
414 }
415 
416 static void StdProxy_GetIID(LPVOID iface,
417                                const IID **ppiid)
418 {
419   StdProxyImpl *This = impl_from_proxy_obj( iface );
420   TRACE("(%p)->GetIID(%p) %s\n",This,ppiid,This->name);
421 
422   *ppiid = This->piid;
423 }
424 
425 HRESULT WINAPI IUnknown_QueryInterface_Proxy(LPUNKNOWN iface,
426                                             REFIID riid,
427                                             LPVOID *ppvObj)
428 {
429   StdProxyImpl *This = impl_from_proxy_obj( iface );
430   TRACE("(%p)->QueryInterface(%s,%p) %s\n",This,debugstr_guid(riid),ppvObj,This->name);
431   return IUnknown_QueryInterface(This->pUnkOuter,riid,ppvObj);
432 }
433 
434 ULONG WINAPI IUnknown_AddRef_Proxy(LPUNKNOWN iface)
435 {
436   StdProxyImpl *This = impl_from_proxy_obj( iface );
437   TRACE("(%p)->AddRef() %s\n",This,This->name);
438   return IUnknown_AddRef(This->pUnkOuter);
439 }
440 
441 ULONG WINAPI IUnknown_Release_Proxy(LPUNKNOWN iface)
442 {
443   StdProxyImpl *This = impl_from_proxy_obj( iface );
444   TRACE("(%p)->Release() %s\n",This,This->name);
445   return IUnknown_Release(This->pUnkOuter);
446 }
447 
448 /***********************************************************************
449  *           NdrProxyInitialize [RPCRT4.@]
450  */
451 void WINAPI NdrProxyInitialize(void *This,
452                               PRPC_MESSAGE pRpcMsg,
453                               PMIDL_STUB_MESSAGE pStubMsg,
454                               PMIDL_STUB_DESC pStubDescriptor,
455                               unsigned int ProcNum)
456 {
457   TRACE("(%p,%p,%p,%p,%d)\n", This, pRpcMsg, pStubMsg, pStubDescriptor, ProcNum);
458   NdrClientInitializeNew(pRpcMsg, pStubMsg, pStubDescriptor, ProcNum);
459   StdProxy_GetChannel(This, &pStubMsg->pRpcChannelBuffer);
460   IRpcChannelBuffer_GetDestCtx(pStubMsg->pRpcChannelBuffer,
461                                &pStubMsg->dwDestContext,
462                                &pStubMsg->pvDestContext);
463   TRACE("channel=%p\n", pStubMsg->pRpcChannelBuffer);
464 }
465 
466 /***********************************************************************
467  *           NdrProxyGetBuffer [RPCRT4.@]
468  */
469 void WINAPI NdrProxyGetBuffer(void *This,
470                              PMIDL_STUB_MESSAGE pStubMsg)
471 {
472   HRESULT hr;
473   const IID *riid = NULL;
474 
475   TRACE("(%p,%p)\n", This, pStubMsg);
476   pStubMsg->RpcMsg->BufferLength = pStubMsg->BufferLength;
477   pStubMsg->dwStubPhase = PROXY_GETBUFFER;
478   StdProxy_GetIID(This, &riid);
479   hr = IRpcChannelBuffer_GetBuffer(pStubMsg->pRpcChannelBuffer,
480                                   (RPCOLEMESSAGE*)pStubMsg->RpcMsg,
481                                   riid);
482   if (FAILED(hr))
483   {
484     RpcRaiseException(hr);
485     return;
486   }
487   pStubMsg->fBufferValid = TRUE;
488   pStubMsg->BufferStart = pStubMsg->RpcMsg->Buffer;
489   pStubMsg->BufferEnd = pStubMsg->BufferStart + pStubMsg->BufferLength;
490   pStubMsg->Buffer = pStubMsg->BufferStart;
491   pStubMsg->dwStubPhase = PROXY_MARSHAL;
492 }
493 
494 /***********************************************************************
495  *           NdrProxySendReceive [RPCRT4.@]
496  */
497 void WINAPI NdrProxySendReceive(void *This,
498                                PMIDL_STUB_MESSAGE pStubMsg)
499 {
500   ULONG Status = 0;
501   HRESULT hr;
502 
503   TRACE("(%p,%p)\n", This, pStubMsg);
504 
505   if (!pStubMsg->pRpcChannelBuffer)
506   {
507     WARN("Trying to use disconnected proxy %p\n", This);
508     RpcRaiseException(RPC_E_DISCONNECTED);
509   }
510 
511   pStubMsg->dwStubPhase = PROXY_SENDRECEIVE;
512   /* avoid sending uninitialised parts of the buffer on the wire */
513   pStubMsg->RpcMsg->BufferLength = pStubMsg->Buffer - (unsigned char *)pStubMsg->RpcMsg->Buffer;
514   hr = IRpcChannelBuffer_SendReceive(pStubMsg->pRpcChannelBuffer,
515                                     (RPCOLEMESSAGE*)pStubMsg->RpcMsg,
516                                     &Status);
517   pStubMsg->dwStubPhase = PROXY_UNMARSHAL;
518   pStubMsg->BufferLength = pStubMsg->RpcMsg->BufferLength;
519   pStubMsg->BufferStart = pStubMsg->RpcMsg->Buffer;
520   pStubMsg->BufferEnd = pStubMsg->BufferStart + pStubMsg->BufferLength;
521   pStubMsg->Buffer = pStubMsg->BufferStart;
522 
523   /* raise exception if call failed */
524   if (hr == RPC_S_CALL_FAILED) RpcRaiseException(*(DWORD*)pStubMsg->Buffer);
525   else if (FAILED(hr)) RpcRaiseException(hr);
526 }
527 
528 /***********************************************************************
529  *           NdrProxyFreeBuffer [RPCRT4.@]
530  */
531 void WINAPI NdrProxyFreeBuffer(void *This,
532                               PMIDL_STUB_MESSAGE pStubMsg)
533 {
534   TRACE("(%p,%p)\n", This, pStubMsg);
535 
536   if (pStubMsg->fBufferValid)
537   {
538     IRpcChannelBuffer_FreeBuffer(pStubMsg->pRpcChannelBuffer,
539                                  (RPCOLEMESSAGE*)pStubMsg->RpcMsg);
540     pStubMsg->fBufferValid = TRUE;
541   }
542 }
543 
544 /***********************************************************************
545  *           NdrProxyErrorHandler [RPCRT4.@]
546  */
547 HRESULT WINAPI NdrProxyErrorHandler(DWORD dwExceptionCode)
548 {
549   WARN("(0x%08x): a proxy call failed\n", dwExceptionCode);
550 
551   if (FAILED(dwExceptionCode))
552     return dwExceptionCode;
553   else
554     return HRESULT_FROM_WIN32(dwExceptionCode);
555 }
556