Win32 RPC 編程(一)

咱們從一個簡單的 RPC 「Hello, world!」的例子開始。
參考資料:MSDN: Win32 and COM Development -> Networking -> Network Protocols -> Remote Procedure Calls (RPC)


第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.idl
Microsoft (R) 32b/64b MIDL Compiler Version 6.00.0366
Copyright (c) Microsoft Corporation 1991-2002. All rights reserved.
Processing .\Hello.idl
Hello.idl
Processing .\Hello.acf
Hello.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("%s\n", 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 的命名規範,\pipe\pipename,其中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.c
Hello_s.c
正在生成代碼...
Microsoft (R) Incremental Linker Version 8.00.50727.42
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:server.exe
server.obj
Hello_s.obj
rpcrt4.lib

編譯時爲何要指定 _WIN32_WINNT=0x500 呢?由於若是沒有的話會報告下面的錯誤:
Hello_s.c(88) : fatal error C1189: #error :  You need a Windows 2000 or later to
run 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 %d\n", 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.c
Hello_c.c
正在生成代碼...
Microsoft (R) Incremental Linker Version 8.00.50727.42
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:client.exe
client.obj
Hello_c.obj
rpcrt4.lib

 

第4步:測試:
-------------------------------------------------------------------------
運行 server.exe,將彈出一個 console 窗口,等待客戶端調用。
運行客戶端 client.exe:

>client hello
能夠看到 server.exe 的 console 窗口出現 hello 的字符串。


>client shutdown
server.exe 退出。

示例下載
 ide

相關文章
相關標籤/搜索