第1步:编写 IDL(Interface Description Language,接口描述语言)文件-------------------------------------------------------------------------IDL 是一个通用的工业标准语言,大家应该不陌生,因为 COM 里面也是用它来描述接口的。Hello.idl:[ uuid("4556509F-618A-46CF-AB3D-ED736ED66477"), // 唯一的UUID,用 GUIDGen 生成 version(1.0)]interface HelloWorld { // 我们定义的方法 void Hello([in,string]const char * psz) void Shutdown(void) }一个可选的文件是应用程序配置文件(.acf),它的作用是对 RPC 接口进行配置,例如下面的 Hello.acf 文件:Hello.acf:[ implicit_handle(handle_t HelloWorld_Binding) ] interface HelloWorld{}上面定义了 implicit_handle,这样客户端将绑定句柄 HelloWorld_Binding 了,后面的客户端代码中我们会看到。编译 IDL 文件:>midl Hello.idlMicrosoft (R) 32b/64b MIDL Compiler Version 6.00.0366Copyright (c) Microsoft Corporation 1991-2002. All rights reserved.Processing .Hello.idlHello.idlProcessing .Hello.acfHello.acf我们可以看到自动生成了 Hello.h, Hello_s.c, Hello_c.c 文件,这些叫做 rpc stub 程序,不过我们可以不管这个概念,我们只需要知道 Hello.h 里面定义了一个extern RPC_IF_HANDLE HelloWorld_v1_0_s_ifspec这个 RPC_IF_HANDLE 将在后面用到。第2步:编写服务端程序------------------------------------------------------------------------- 第1步中我们已经约定了调用的接口,那么现在我们开始实现其服务端。代码如下:server.c#include <stdlib.h>#include <stdio.h>#include "Hello.h" // 引用MIDL 生成的头文件/** * 这是我们在IDL 中定义的接口方法 * 需要注意一点,IDL 里面的声明是:void Hello([in,string]const char * psz) * 但是这里变成了const unsigned char *,为什么呢 * 参见MSDN 中的MIDL Command-Line Reference -> /char Switch * 默认的编译选项,对 IDL 中的char 按照unsigned char 处理 */void Hello(const unsigned char * psz){ printf("%sn", psz)}/** 这也是我们在IDL 中定义的接口方法,提供关闭server 的机制*/void Shutdown(void){ // 下面的操作将导致 RpcServerListen() 退出 RpcMgmtStopServerListening(NULL) RpcServerUnregisterIf(NULL, NULL, FALSE)}int main(int argc,char * argv[]){ // 用Named Pipe 作为RPC 的通道,这样EndPoint 参数就是Named Pipe 的名字 // 按照Named Pipe 的命名规范,pipepipename,其中pipename 可以是除了 // 之外的任意字符,那么这里用一个GUID 串来命名,可以保证不会重复 RpcServerUseProtseqEp((unsigned char *)"ncacn_np", 20, (unsigned char *)"\pipe\{8dd50205-3108-498f-96e8-dbc4ec074cf9}", NULL) // 注册接口,HelloWorld_v1_0_s_ifspec 是在MIDL 生成的Hello.h 中定义的 RpcServerRegisterIf(HelloWorld_v1_0_s_ifspec, NULL, NULL) // 开始监听,本函数将一直阻塞 RpcServerListen(1,20,FALSE) return 0}// 下面的函数是为了满足链接需要而写的,没有的话会出现链接错误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)}编译:>cl /D_WIN32_WINNT=0x500 server.c Hello_s.c rpcrt4.lib用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 14.00.50727.42 版版权所有(C) Microsoft Corporation。保留所有权利。server.cHello_s.c正在生成代码...Microsoft (R) Incremental Linker Version 8.00.50727.42Copyright (C) Microsoft Corporation. All rights reserved./out:server.exeserver.objHello_s.objrpcrt4.lib编译时为什么要指定 _WIN32_WINNT=0x500 呢因为如果没有的话会报告下面的错误:Hello_s.c(88) : fatal error C1189: #error : You need a Windows 2000 or later torun this stub because it uses these features:第3步:编写客户端程序------------------------------------------------------------------------- 客户端的代码:client.c#include <stdlib.h>#include <stdio.h>#include <string.h>#include "Hello.h" // 引用MIDL 生成的头文件int main(int argc, char * argv[]){ unsigned char * pszStringBinding = NULL if ( argc != 2 ) { printf("Usage:%s <Hello Text>n", argv[0]) return 1 } // 用Named Pipe 作为RPC 的通道。参见server.c 中的RpcServerUseProtseqEp() 部分 // 第3 个参数NetworkAddr 如果取NULL,那么就是连接本机服务 // 否则要取\\servername 这样的格式,例如你的计算机名为jack,那么就是\jack RpcStringBindingCompose( NULL, (unsigned char*)"ncacn_np", /*(unsigned char*)"\\servername"*/ NULL, (unsigned char*)"\pipe\{8dd50205-3108-498f-96e8-dbc4ec074cf9}", NULL, &pszStringBinding ) // 绑定接口,这里要和 Hello.acf 的配置一致,那么就是HelloWorld_Binding RpcBindingFromStringBinding(pszStringBinding, & HelloWorld_Binding ) // 下面是调用服务端的函数了 RpcTryExcept { if ( _stricmp(argv[1], "SHUTDOWN") == 0 ) { Shutdown() } else { Hello((unsigned char*)argv[1]) } } RpcExcept(1) { printf( "RPC Exception %dn", RpcExceptionCode() ) } RpcEndExcept// 释放资源 RpcStringFree(&pszStringBinding) RpcBindingFree(&HelloWorld_Binding) return 0}// 下面的函数是为了满足链接需要而写的,没有的话会出现链接错误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)}编译:>cl /D_WIN32_WINNT=0x500 client.c Hello_c.c rpcrt4.lib用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 14.00.50727.42 版版权所有(C) Microsoft Corporation。保留所有权利。client.cHello_c.c正在生成代码...Microsoft (R) Incremental Linker Version 8.00.50727.42Copyright (C) Microsoft Corporation. All rights reserved./out:client.execlient.objHello_c.objrpcrt4.lib第4步:测试:------------------------------------------------------------------------- 运行 server.exe,将弹出一个 console 窗口,等待客户端调用。运行客户端 client.exe:>client hello可以看到 server.exe 的 console 窗口出现 hello 的字符串。>client shutdownserver.exe 退出。