针对3、4节进行补充讲解
idl中的版本号可确保当RPC接口有多个版本时,仅连接客户端和服务器的兼容版本。
//file hello.idl
[
uuid(7A385E9F-A4F9-4F6D-AD2E-E155AB618471),
version(1.0)
]
interface hello
{
void HelloProc([in, string] unsigned char * pszString);
void Shutdown(void);
}
[string]
属性指定存根应该把参数作为C风格的字符串。
客户端应用程序应该能够关闭服务器应用程序,因此该接口包含另一个远程功能Shutdown的原型,该原型将在本教程的后面部分实现。
ACF标头中的[ implicit_handle]
属性允许客户端应用程序为其远程过程调用选择服务器。ACF将句柄定义为handle_t
类型(MIDL基本数据类型)。MIDL编译器会将ACF指定的绑定句柄名称hello_IfHandle放入它生成的头文件中。
确保Hello.idl和Hello.acf位于同一目录中。以下命令将生成头文件Hello.h
,以及客户端和服务器存根Hello_c.c, Hello_s.c
。
midl hello.idl
hello.h重要定义:
void HelloProc(
/* [string][in] */ unsigned char *pszString);
void Shutdown( void);
extern handle_t hello_IfHandle;
extern RPC_IF_HANDLE hello_v1_0_c_ifspec;
extern RPC_IF_HANDLE hello_v1_0_s_ifspec;
void * __RPC_USER MIDL_user_allocate( _In_ size_t size);
void __RPC_USER MIDL_user_free( _Pre_maybenull_ _Post_invalid_ void * );
不需要对存根文件两个.c文件进行任何操作。
ncacn_np
协议序列对应管道名"\\pipe\\pipename."
#include
#include "hello_h.h"
#pragma comment(lib, "rpcrt4.lib")
int main()
{
RPC_STATUS status;
unsigned char * pszUuid = NULL;
unsigned char * pszProtocolSequence = (unsigned char *)"ncalrpc";
unsigned char * pszNetworkAddress = NULL;
unsigned char * pszEndpoint = (unsigned char *)"12888";
unsigned char * pszOptions = NULL;
unsigned char * pszStringBinding = NULL;
unsigned char * pszString = (unsigned char *)"hello, world";
unsigned long ulCode;
status = RpcStringBindingComposeA(pszUuid,
pszProtocolSequence,
pszNetworkAddress,
pszEndpoint,
pszOptions,
&pszStringBinding);
if (status) exit(status);
status = RpcBindingFromStringBinding(pszStringBinding, &hello_IfHandle);
if (status) exit(status);
RpcTryExcept
{
HelloProc(pszString);
getchar();
Shutdown();
}
RpcExcept(1)
{
ulCode = RpcExceptionCode();
printf("Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
}
RpcEndExcept
status = RpcStringFree(&pszStringBinding);
if (status) exit(status);
status = RpcBindingFree(&hello_IfHandle);
if (status) exit(status);
return 0;
}
/******************************************************/
/* MIDL allocate and free */
/******************************************************/
void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
return(malloc(len));
}
void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
free(ptr);
}
#include
#include "hello_h.h"
#pragma comment(lib, "rpcrt4.lib")
int main()
{
RPC_STATUS status;
unsigned char * pszProtocolSequence = (unsigned char *)"ncalrpc";
unsigned char * pszEndpoint = (unsigned char *)"12888";
unsigned char * pszSecurity = NULL;
unsigned int cMinCalls = 1;
unsigned int fDontWait = FALSE;
status = RpcServerUseProtseqEp(
pszProtocolSequence,
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
pszEndpoint,
pszSecurity);
if (status) exit(status);
status = RpcServerRegisterIf(
hello_v1_0_s_ifspec,
NULL,
NULL);
if (status) exit(status);
status = RpcServerListen(
cMinCalls,
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
fDontWait
);
if (status) exit(status);
return 0;
}
void HelloProc(unsigned char * pszString)
{
printf("%s\n", pszString);
}
/* add this function to hellop.c */
void Shutdown(void)
{
RPC_STATUS status;
status = RpcMgmtStopServerListening(NULL);
if (status)
{
exit(status);
}
status = RpcServerUnregisterIf(
NULL, // unregister all
NULL, // interfaces
FALSE); // interface should be removed from the registry immediately, dont't wait for pending
if (status)
{
exit(status);
}
} //end Shutdown
/******************************************************/
/* MIDL allocate and free */
/******************************************************/
void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
return(malloc(len));
}
void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
free(ptr);
}
停止侦听客户端的两种方法:
RpcServerListen直到发生异常或调用RpcMgmtStopServerListening才会停止,默认只有另一台服务器的线程才能调用RpcMgmtStopServerListening来停止一台服务器。客户端若调用则会返回RPC_S_ACCESS_DENIED
,但服务端通过RpcMgmtSetAuthorizationFn 可以授权客户端停止服务。
msdn上用的makefile。