Based on NdrStubCall2 and NdrAsyncClientCall implementations.
In order to test asynchronous RPC I used dlls/rpcrt4/tests/server.c as a base,
converted int_return() and sum() to use asynchronous RPC on both client and
server sides (server.idl doesn't need any changes), and added server.acf:
interface IServer
{
[async] int_return();
[async] sum();
}
With this implementation these tests pass under Wine. Since widl doesn't
support asynchronous RPC it's impossible to add appropriate Wine tests.
Signed-off-by: Dmitry Timoshkov <dmitry(a)baikal.ru>
---
dlls/rpcrt4/ndr_stubless.c | 190 +++++++++++++++++++++++++++++++++----
dlls/rpcrt4/ndr_stubless.h | 15 +++
dlls/rpcrt4/rpc_async.c | 11 ++-
3 files changed, 199 insertions(+), 17 deletions(-)
diff --git a/dlls/rpcrt4/ndr_stubless.c b/dlls/rpcrt4/ndr_stubless.c
index 90537515ea..70a26dd5b3 100644
--- a/dlls/rpcrt4/ndr_stubless.c
+++ b/dlls/rpcrt4/ndr_stubless.c
@@ -1559,21 +1559,6 @@ void WINAPI NdrServerCallAll( PRPC_MESSAGE msg )
FIXME("%p stub\n", msg);
}
-struct async_call_data
-{
- MIDL_STUB_MESSAGE *pStubMsg;
- const NDR_PROC_HEADER *pProcHeader;
- PFORMAT_STRING pHandleFormat;
- PFORMAT_STRING pParamFormat;
- RPC_BINDING_HANDLE hBinding;
- /* size of stack */
- unsigned short stack_size;
- /* number of parameters. optional for client to give it to us */
- unsigned int number_of_params;
- /* correlation cache */
- ULONG_PTR NdrCorrCache[256];
-};
-
/* Helper for ndr_async_client_call, to factor out the part that may or may not be
* guarded by a try/except block. */
static void do_ndr_async_client_call( const MIDL_STUB_DESC *pStubDesc, PFORMAT_STRING pFormat, void **stack_top )
@@ -1922,5 +1907,178 @@ RPCRTAPI LONG RPC_ENTRY NdrAsyncStubCall(struct IRpcStubBuffer* pThis,
void RPC_ENTRY NdrAsyncServerCall(PRPC_MESSAGE pRpcMsg)
{
- FIXME("unimplemented, %p\n", pRpcMsg);
+ const MIDL_SERVER_INFO *pServerInfo;
+ const MIDL_STUB_DESC *pStubDesc;
+ PFORMAT_STRING pFormat;
+ /* pointer to start of stack to pass into stub implementation */
+ unsigned char *args;
+ /* header for procedure string */
+ const NDR_PROC_HEADER *pProcHeader;
+ struct async_call_data *async_call_data;
+ PRPC_ASYNC_STATE pAsync;
+ RPC_STATUS status;
+
+ TRACE("%p\n", pRpcMsg);
+
+ pServerInfo = ((RPC_SERVER_INTERFACE *)pRpcMsg->RpcInterfaceInformation)->InterpreterInfo;
+
+ pStubDesc = pServerInfo->pStubDesc;
+ pFormat = pServerInfo->ProcString + pServerInfo->FmtStringOffset[pRpcMsg->ProcNum];
+ pProcHeader = (const NDR_PROC_HEADER *)&pFormat[0];
+
+ TRACE("NDR Version: 0x%x\n", pStubDesc->Version);
+
+ async_call_data = I_RpcAllocate(sizeof(*async_call_data) + sizeof(MIDL_STUB_MESSAGE) + sizeof(RPC_MESSAGE));
+ if (!async_call_data) RpcRaiseException(RPC_X_NO_MEMORY);
+ async_call_data->pProcHeader = pProcHeader;
+
+ async_call_data->pStubMsg = (PMIDL_STUB_MESSAGE)(async_call_data + 1);
+ *(PRPC_MESSAGE)(async_call_data->pStubMsg + 1) = *pRpcMsg;
+
+ if (pProcHeader->Oi_flags & Oi_HAS_RPCFLAGS)
+ {
+ const NDR_PROC_HEADER_RPC *header_rpc = (const NDR_PROC_HEADER_RPC *)&pFormat[0];
+ async_call_data->stack_size = header_rpc->stack_size;
+ pFormat += sizeof(NDR_PROC_HEADER_RPC);
+ }
+ else
+ {
+ async_call_data->stack_size = pProcHeader->stack_size;
+ pFormat += sizeof(NDR_PROC_HEADER);
+ }
+
+ TRACE("Oi_flags = 0x%02x\n", pProcHeader->Oi_flags);
+
+ /* binding */
+ switch (pProcHeader->handle_type)
+ {
+ /* explicit binding: parse additional section */
+ case 0:
+ switch (*pFormat) /* handle_type */
+ {
+ case FC_BIND_PRIMITIVE: /* explicit primitive */
+ pFormat += sizeof(NDR_EHD_PRIMITIVE);
+ break;
+ case FC_BIND_GENERIC: /* explicit generic */
+ pFormat += sizeof(NDR_EHD_GENERIC);
+ break;
+ case FC_BIND_CONTEXT: /* explicit context */
+ pFormat += sizeof(NDR_EHD_CONTEXT);
+ break;
+ default:
+ ERR("bad explicit binding handle type (0x%02x)\n", pProcHeader->handle_type);
+ RpcRaiseException(RPC_X_BAD_STUB_DATA);
+ }
+ break;
+ case FC_BIND_GENERIC: /* implicit generic */
+ case FC_BIND_PRIMITIVE: /* implicit primitive */
+ case FC_CALLBACK_HANDLE: /* implicit callback */
+ case FC_AUTO_HANDLE: /* implicit auto handle */
+ break;
+ default:
+ ERR("bad implicit binding handle type (0x%02x)\n", pProcHeader->handle_type);
+ RpcRaiseException(RPC_X_BAD_STUB_DATA);
+ }
+
+ if (pProcHeader->Oi_flags & Oi_OBJECT_PROC)
+ {
+ ERR("objects not supported\n");
+ I_RpcFree(async_call_data);
+ RpcRaiseException(RPC_X_BAD_STUB_DATA);
+ }
+
+ NdrServerInitializeNew(pRpcMsg, async_call_data->pStubMsg, pStubDesc);
+
+ /* create the full pointer translation tables, if requested */
+ if (pProcHeader->Oi_flags & Oi_FULL_PTR_USED)
+ async_call_data->pStubMsg->FullPtrXlatTables = NdrFullPointerXlatInit(0, XLAT_SERVER);
+
+ /* use alternate memory allocation routines */
+ if (pProcHeader->Oi_flags & Oi_RPCSS_ALLOC_USED)
+#if 0
+ NdrRpcSsEnableAllocate(&stubMsg);
+#else
+ FIXME("Set RPCSS memory allocation routines\n");
+#endif
+
+ TRACE("allocating memory for stack of size %x\n", async_call_data->stack_size);
+
+ args = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, async_call_data->stack_size);
+ async_call_data->pStubMsg->StackTop = args; /* used by conformance of top-level objects */
+
+ pAsync = I_RpcAllocate(sizeof(*pAsync));
+ if (!pAsync) RpcRaiseException(RPC_X_NO_MEMORY);
+
+ status = RpcAsyncInitializeHandle(pAsync, sizeof(*pAsync));
+ if (status != RPC_S_OK)
+ RpcRaiseException(status);
+
+ pAsync->StubInfo = async_call_data;
+ TRACE("pAsync %p, pAsync->StubInfo %p, pFormat %p\n", pAsync, pAsync->StubInfo, async_call_data->pHandleFormat);
+
+ /* add the implicit pAsync pointer as the first arg to the function */
+ *(void **)args = pAsync;
+
+ if (is_oicf_stubdesc(pStubDesc))
+ {
+ const NDR_PROC_PARTIAL_OIF_HEADER *pOIFHeader = (const NDR_PROC_PARTIAL_OIF_HEADER *)pFormat;
+ /* cache of Oif_flags from v2 procedure header */
+ INTERPRETER_OPT_FLAGS Oif_flags;
+ /* cache of extension flags from NDR_PROC_HEADER_EXTS */
+ INTERPRETER_OPT_FLAGS2 ext_flags = { 0 };
+
+ Oif_flags = pOIFHeader->Oi2Flags;
+ async_call_data->number_of_params = pOIFHeader->number_of_params;
+
+ pFormat += sizeof(NDR_PROC_PARTIAL_OIF_HEADER);
+
+ TRACE("Oif_flags = %s\n", debugstr_INTERPRETER_OPT_FLAGS(Oif_flags) );
+
+ if (Oif_flags.HasExtensions)
+ {
+ const NDR_PROC_HEADER_EXTS *pExtensions = (const NDR_PROC_HEADER_EXTS *)pFormat;
+ ext_flags = pExtensions->Flags2;
+ pFormat += pExtensions->Size;
+ }
+
+ if (Oif_flags.HasPipes)
+ {
+ FIXME("pipes not supported yet\n");
+ RpcRaiseException(RPC_X_WRONG_STUB_VERSION); /* FIXME: remove when implemented */
+ /* init pipes package */
+ /* NdrPipesInitialize(...) */
+ }
+ if (ext_flags.HasNewCorrDesc)
+ {
+ /* initialize extra correlation package */
+ NdrCorrelationInitialize(async_call_data->pStubMsg, async_call_data->NdrCorrCache, sizeof(async_call_data->NdrCorrCache), 0);
+ if (ext_flags.Unused & 0x2) /* has range on conformance */
+ async_call_data->pStubMsg->CorrDespIncrement = 12;
+ }
+ }
+ else
+ {
+ pFormat = convert_old_args( async_call_data->pStubMsg, pFormat, async_call_data->stack_size,
+ pProcHeader->Oi_flags & Oi_OBJECT_PROC,
+ /* reuse the correlation cache, it's not needed for v1 format */
+ async_call_data->NdrCorrCache, sizeof(async_call_data->NdrCorrCache), &async_call_data->number_of_params );
+ }
+
+ /* convert strings, floating point values and endianness into our
+ * preferred format */
+ if ((pRpcMsg->DataRepresentation & 0x0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION)
+ NdrConvert(async_call_data->pStubMsg, pFormat);
+
+ async_call_data->pHandleFormat = pFormat;
+
+ /* 1. UNMARSHAL */
+ TRACE("UNMARSHAL\n");
+ stub_do_args(async_call_data->pStubMsg, pFormat, STUBLESS_UNMARSHAL, async_call_data->number_of_params);
+
+ /* 2. CALLSERVER */
+ TRACE("CALLSERVER\n");
+ if (pServerInfo->ThunkTable && pServerInfo->ThunkTable[pRpcMsg->ProcNum])
+ pServerInfo->ThunkTable[pRpcMsg->ProcNum](async_call_data->pStubMsg);
+ else
+ call_server_func(pServerInfo->DispatchTable[pRpcMsg->ProcNum], args, async_call_data->stack_size);
}
diff --git a/dlls/rpcrt4/ndr_stubless.h b/dlls/rpcrt4/ndr_stubless.h
index 606513ff24..fc67cf9020 100644
--- a/dlls/rpcrt4/ndr_stubless.h
+++ b/dlls/rpcrt4/ndr_stubless.h
@@ -224,6 +224,21 @@ typedef struct _NDR_EHD_CONTEXT
#include "poppack.h"
+struct async_call_data
+{
+ MIDL_STUB_MESSAGE *pStubMsg;
+ const NDR_PROC_HEADER *pProcHeader;
+ PFORMAT_STRING pHandleFormat;
+ PFORMAT_STRING pParamFormat;
+ RPC_BINDING_HANDLE hBinding;
+ /* size of stack */
+ unsigned short stack_size;
+ /* number of parameters. optional for client to give it to us */
+ unsigned int number_of_params;
+ /* correlation cache */
+ ULONG_PTR NdrCorrCache[256];
+};
+
enum stubless_phase
{
STUBLESS_UNMARSHAL,
diff --git a/dlls/rpcrt4/rpc_async.c b/dlls/rpcrt4/rpc_async.c
index 891fc5ad72..9c3d90cfba 100644
--- a/dlls/rpcrt4/rpc_async.c
+++ b/dlls/rpcrt4/rpc_async.c
@@ -110,6 +110,8 @@ RPC_STATUS WINAPI RpcAsyncGetCallStatus(PRPC_ASYNC_STATE pAsync)
*/
RPC_STATUS WINAPI RpcAsyncCompleteCall(PRPC_ASYNC_STATE pAsync, void *Reply)
{
+ struct async_call_data *data;
+
TRACE("(%p, %p)\n", pAsync, Reply);
if (!valid_async_handle(pAsync))
@@ -117,7 +119,14 @@ RPC_STATUS WINAPI RpcAsyncCompleteCall(PRPC_ASYNC_STATE pAsync, void *Reply)
/* FIXME: check completed */
- return NdrpCompleteAsyncClientCall(pAsync, Reply);
+ TRACE("pAsync %p, pAsync->StubInfo %p\n", pAsync, pAsync->StubInfo);
+
+ data = pAsync->StubInfo;
+ if (data->pStubMsg->IsClient)
+ return NdrpCompleteAsyncClientCall(pAsync, Reply);
+
+ FIXME("not implemented for server side\n");
+ return RPC_S_CALL_FAILED;
}
/***********************************************************************
--
2.20.1