概述:windows 创建 RPC调用过程实例详解
[toc]
相关参考文章
0x01、生成 UUID 和模版(IDL)文件
定义接口的第一步是使用 uuidgen 实用工具生成通用唯一标识符(UUID)。UUID使客户端和服务端能够相互识别。该工具包含在阿庄平台软件开发工具包中(SDK)。
一般安装路径位于:D:\Windows Kits\10\bin\10.0.22621.0\x64
以下命令生成 UUID 并创建名为 Hello.idl 的模版文件。
模版内容大致如下:
1 2 3 4 5 6 7 8
| [ uuid(7a98c250-6808-11cf-b73b-00aa00b677a7), version(1.0) ] interface hello { }
|
在模版中添加接口:
1 2 3 4 5 6 7 8 9 10
| //file hello.idl [ uuid(7a98c250-6808-11cf-b73b-00aa00b677a7), version(1.0) ] interface hello { void HelloProc([in, string] unsigned char * pszString); void Shutdown(void); }
|
0x02、添加 acf 文件
acf文件内容如下所示,导出接口需要与 idl 文件一致:
1 2 3 4 5 6 7
| //file: hello.acf [ implicit_handle (handle_t hello_IfHandle) ] interface hello { }
|
0x03、编译 idl 文件
-
打开 visual studio,新建一个空项目
-
空项目中添加上述 idl文件 和 acf文件
-
编译项目
-
生成 hello_h.h、hello_c.c、hello_s.c
- hello_h.h: 服务端和客户端共用文件
- hello_c.c: 客户端文件
- hello_s.c: 服务端文件
需要补充说明的是,在 hello_h.h 头文件中有两个导出接口,导出接口即为rpc调用的接口。
1 2
| extern RPC_IF_HANDLE hello_v1_0_c_ifspec; extern RPC_IF_HANDLE hello_v1_0_s_ifspec;
|
包含函数
函数MIDL_user_allocate和MIDL_user_free用于为RPC存根分配和释放内存。
MIDL_user_allocate和MIDL_user_free在实现RPC应用程序时,它们必须在应用程序的某个地方定义,这里直接在主文件定义即可。
1 2 3 4 5 6 7 8 9 10
| 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); }
|
0x04、客户端
新建工程文件如下所示:
clientrpc.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| #include <iostream> #include <string> using namespace std;
#include "hello_h.h"
#pragma comment(lib,"Rpcrt4.lib")
void doRpcCall();
int main(int argc, char** argv) { int i = 0; RPC_STATUS status = 0;
unsigned char* pszNetworkAddr = NULL; unsigned char* pszStringBinding = NULL;
for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-ip") == 0) { pszNetworkAddr = (unsigned char*)argv[++i]; break; } }
status = RpcStringBindingCompose(NULL, (unsigned char*)"ncacn_np", pszNetworkAddr, (unsigned char*)"\\pipe\\hello", NULL, &pszStringBinding); if (status != 0) { cout << "RpcStringBindingCompose returns: " << status << "!" << endl; return -1; }
cout << "pszStringBinding = " << pszStringBinding << endl; status = RpcBindingFromStringBinding(pszStringBinding, &hello_IfHandle); if (status != 0) { cout << "RpcBindingFromStringBinding returns: " << status << "!" << endl; return -1; }
doRpcCall();
status = RpcStringFree(&pszStringBinding); if (status != 0) cout << "RpcStringFree returns: " << status << "!" << endl;
status = RpcBindingFree(&hello_IfHandle); if (status != 0) cout << "RpcBindingFree returns: " << status << "!" << endl;
cin.get(); return 0; }
void doRpcCall(void) { char buff[1024]; RpcTryExcept{ while (true) { cout << "Please input a string param for Rpc call:" << endl; cin.getline(buff, 1023); if (strcmp(buff, "exit") == 0 || strcmp(buff, "quit") == 0) { Shutdown(); } else { HelloProc((unsigned char*)buff); cout << "call helloproc succeed!" << endl; } } }
RpcExcept(1) { unsigned long ulCode = RpcExceptionCode(); cout << "RPC exception occured! code: " << ulCode << endl; } RpcEndExcept }
void* __RPC_USER MIDL_user_allocate(size_t len) { return (malloc(len)); }
void __RPC_USER MIDL_user_free(void* ptr) { free(ptr); }
|
0x05、服务端
新建工程文件如下所示:
serverrpc.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| #include <iostream> using namespace std;
#include "hello_h.h"
#pragma comment(lib,"Rpcrt4.lib")
int main(void) { RPC_STATUS status = 0;
unsigned int mincall = 1; unsigned int maxcall = 20;
status = RpcServerUseProtseqEp( (unsigned char*)"ncacn_np", maxcall, (unsigned char*)"\\pipe\\hello", NULL); if (status != 0) { cout << "RpcServerUseProtseqEp returns: " << status << endl; return -1; }
status = RpcServerRegisterIf( hello_v1_0_s_ifspec, NULL, NULL); if (status != 0) { cout << "RpcServerRegisterIf returns: " << status << endl; return -1; }
cout << "Rpc Server Begin Listening..." << endl; status = RpcServerListen(mincall, maxcall, FALSE); if (status != 0) { cout << "RpcServerListen returns: " << status << endl; return -1; }
cin.get(); return 0; }
void* __RPC_USER MIDL_user_allocate(size_t len) { return (malloc(len)); }
void __RPC_USER MIDL_user_free(void* ptr) { free(ptr); }
void HelloProc(unsigned char* szhello) { cout << szhello << endl; }
void Shutdown(void) { RPC_STATUS status = 0;
status = RpcMgmtStopServerListening(NULL); if (status != 0) { cout << "RpcMgmtStopServerListening returns: " << status << "!" << endl; }
status = RpcServerUnregisterIf(NULL, NULL, FALSE); if (status != 0) { cout << "RpcServerUnregisterIf returns: " << status << "!" << endl; } }
|
0x06、编译并运行
分别编译客户端和服务端程序,得到 server.exe 和 client.exe
- 先运行 server.exe
- 在 client.exe 目录运行
client -ip 192.168.106.128
来启动客户端程序并与服务器端相连
- 在 client 的窗口输入任意字符串,回车后可看到server窗口上有显示
- 在 client 窗口内 输入 exit 或 quit, server 窗口关闭
0x07、运行示例
Client
Server