RPC is frequently not used directly, so, usually you don't have to know the RPC API when you are using DCOM to communicate between systems on the www. But - as you will see later - there are times when the un-cover RPC is very useful.
For instance: In the conventional RPC programming, you have two pieces of software, each in distinguishable executable form.
app A : the client - send request to be served.
app B : the server - complies to the client request.
This is nice and satisfactory most of the time. But, for getting this goal - you don't need to use the RPC directly- the distributed COM is more than enough.
So, when do you really need the RPC?
Well, suppose that one executable plays the role of either the client or the server, alternatively. Beyond that, suppose that the client-role and the server-role use the same interface and the same functions.
Confused?
Don't be !!!
I assume you already know how to produce the RPC code by the MIDL compiler, so I'll explain just the changes needed to be done:
__MIDL_ProcFormatString replaced by __MIDL_ProcFormatString2
__MIDL_TypeFormatString replaced by __MIDL_TypeFormatString2
<interface_name>_StubDesc replaced by <interface_name>_StubDesc2
The changes above should be done just in the client side of the source code.
We can't use one instance of those variables for the client and the server side, because in each situation those variables have different values;
<func-name>(<parameters>) to <func-name>2(<parameters>).
This is a snippet code from the demo project. The server side interface implements the GetName
function. Because both the client side and the server side implements the following function:
void GetName( /* [size_is][string][out] */ unsigned char __RPC_FAR name[ ]) { .... }
...and because we compiled together the client and the server code, we have to change the func-name in the server side to avoid duplication, as follows:
void GetName2(
/* [size_is][string][out] */ unsigned char __RPC_FAR name[ ])
{
....
}
This is the function generated by MIDL - the one and only change is the name of the internal function GetName
(the pure implementation func - stub naked).
void __RPC_STUB
details_GetName(
PRPC_MESSAGE _pRpcMessage )
{
MIDL_STUB_MESSAGE _StubMsg;
unsigned char ( __RPC_FAR *name )[ ];
RPC_STATUS _Status;
((void)(_Status));
NdrServerInitializeNew(
_pRpcMessage,
&_StubMsg,
&details_StubDesc);
name = 0;
RpcTryFinally
{
RpcTryExcept
{
if(_StubMsg.Buffer > _StubMsg.BufferEnd)
{
RpcRaiseException(RPC_X_BAD_STUB_DATA);
}
}
RpcExcept( RPC_BAD_STUB_DATA_EXCEPTION_FILTER )
{
RpcRaiseException(RPC_X_BAD_STUB_DATA);
}
RpcEndExcept
if(100 * 1 < 0)
{
RpcRaiseException(RPC_X_INVALID_BOUND);
}
name = (unsigned char (*)[])NdrAllocate(&_StubMsg,100 * 1);
// GetName(*name) is the original source line produced
// by midl compiler.
GetName2(*name);
_StubMsg.BufferLength = 0U;
_StubMsg.MaxCount = 100;
NdrConformantStringBufferSize(
(PMIDL_STUB_MESSAGE) &_StubMsg,
(unsigned char __RPC_FAR *)*name,
(PFORMAT_STRING) &__MIDL_TypeFormatString.Format[2] );
_pRpcMessage->BufferLength = _StubMsg.BufferLength;
_Status = I_RpcGetBuffer( _pRpcMessage );
if ( _Status )
RpcRaiseException( _Status );
_StubMsg.Buffer =
(unsigned char __RPC_FAR *) _pRpcMessage->Buffer;
_StubMsg.MaxCount = 100;
NdrConformantStringMarshall(
(PMIDL_STUB_MESSAGE)& _StubMsg,
(unsigned char __RPC_FAR *)*name,
(PFORMAT_STRING) &__MIDL_TypeFormatString.Format[2] );
}
RpcFinally
{
if ( name )
_StubMsg.pfnFree( name );
}
RpcEndFinally
_pRpcMessage->BufferLength =
(unsigned int)((long)_StubMsg.Buffer -
(long)_pRpcMessage->Buffer);
}
To test the demo-project, launch two copies of the GETNAME application. In any copy type two distinguish end points, and your name against your enemy's name (or your wife - ditto).
end point of end point of your name
remote server local server
--------------- ------------- ------------
app A - 55459 55460 moshe
app B - 55460 55459 shoshana
Click on PlayServer button on both copies. As you can see, each application points to the other application, so the communication is bi-directional. Thus each application is both the client and the server, with the same request (i.e. - get the name from the remote application).
The IP in this demo, represented by 'localhost' string, points to the local computer, represented by 'localhost'.
Click on 'get name from remote server' button - in each copy of the application, and you will get the name of the opposite application's user.
Bravo !!!
The demo project works well in Windows 98 and Windows 2000.
To make it simple and to highlight the topic of this article, I didn't use some necessary code (RPC exception, thread's status checking of activities from cradle to garden of Eden and other useful status handling).