1. 程式人生 > >【C++】【RPC】Win32 RPC 程式設計(一)

【C++】【RPC】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 退出。

該文章轉自:http://www.cppblog.com/jb8164/archive/2009/10/14/48368.html

相關推薦

C++/數據結構單鏈表的基本操作

clear default als troy pub 插入 else fonts pac #pragma once #ifndef _CLIST_H_ #define _CLIST_H_ #include <iostream> #include <

HTTP模擬工具C#/Winform源碼、Json綁定TreeView控件、使用了MetroModernUI、RestSharp、Dapper.Net、Newtonsoft.Json、SmartThreadPool這幾個主要開源框架

type form num -m 請求 resource dap bool dev HTTP模擬工具 開發語言:C#/Winform開發工具:Visual Studio 2017數據庫: SQLite使用框架:界面-MetroModernUI

C++探索之旅第二部分第一課:面向對象初探,string的驚天內幕

信息技術 false cli 方法 復雜 weixin include 命令 就是 內容簡單介紹 1、第二部分第一課:面向對象初探。string的驚天內幕 2

C#復習總結匿名類型由來

數據類型 over 無效 訪問性 屬性。 知乎 私有 不能 默認構造函數 1 屬性 這得先從屬性開始說,為什麽外部代碼訪問對象內部的數據用屬性而不是直接訪問呢,這樣豈不是更方便一些,但是事實證明直接訪問是不安全的。那麽,Anders Hejlsberg(安德斯&mid

C#學習之路001.基本操作

arp main cti 字符 thread 程序 AI 報錯 float 001【HelloWorld】分析代碼塊 //這裏是註釋 下面是引入命名空間 using System; using System.Collections.Generic; using Syst

將文件拖曳到窗體上, 並獲取其完整路徑 C++ Builder下實現

pat ext stc fff led CP tle 聲明 net 1. 在窗體的頭文件.h裏聲明處理函數和消息映射, 如: [cpp] view plain copy class TForm1 : public TForm {

C#復習總結細說委托

protected 希望 百度百科 內存 sting lin baidu 調用約定 multicast 1 前言 前幾天看到博客園一個前輩寫了一篇文章用“五分鐘重溫委托,匿名方法,Lambda,泛型委托,表達式樹”,文章寫的非常好,推薦閱讀一下,正

C#復習總結細說匿名方法

target [] targe left 沒有 如果 連接 program ont 1 前言 本系列會將【委托】 【匿名方法】【Lambda表達式】 【泛型委托】 【表達式樹】 【事件】等基礎知識總結一下。(本人小白一枚,有錯誤的地方希望大佬指正) 系類1:細說委托

C#復習總結細說泛型委托

聲明 sys red 合成 delegate -s 返回 line ron 1 前言 本系列會將【委托】 【匿名方法】【Lambda表達式】 【泛型委托】 【表達式樹】 【事件】等基礎知識總結一下。(本人小白一枚,有錯誤的地方希望大佬指正) 系類1:細說委托 系類2:

C陷阱與缺陷邊界計算與不對稱邊界

前言 本文與為什麼C語言從0開始編號搭配實用更佳。 如有不足還請指正! 正文 如果一個數組有10個元素,那麼這個陣列下表的允許取值範圍是什麼呢? 下面程式碼1,這段程式碼的執行結果是什麼?為什麼? #include <stdio.h&g

C程式編譯連結gcc使用命令介紹 gcc的使用簡介與命令列引數說明

1.gcc或者g++安裝rpm -qa|grep gcc ==>檢查gcc是否安裝gcc -v ==>檢查gcc版本 編譯器會在可執行檔案中植入一些資訊,可執行檔案會變大。一般開發時候使用 -g ,編譯一個 “release 版本” 時不使用 -g 編譯。gcc如果是最新的則不重

C程式編譯連結gcc使用命令介紹 GCC編譯器編譯連結  

1.gcc安裝 rpm -qa|grep gcc ==>檢查gcc是否安裝 gcc -v ==>檢查gcc版本 yum -y install gcc ==>安裝gcc  2.基本語法 gcc最基本的用法是:gcc [options]

C++學習記錄2CC++的升級

C++繼承了所有的C特性; C++在C的基礎上提供了更多的語法和特性; C++的設計目標是執行效率和開發效率(比C語言還高)的統一。 (C++中++的體現)C++是C語言的進化和升級。最重要的是兩個方面,其一是型別的加強(型別檢查更嚴格),其二是天生面向物件的支援(內建天生支援面向物件軟

C++學習記錄1學習C++的意義

UINX誕生之初使用匯編語言編寫的。隨著UINX的發展,組合語言成為了其發展的掣肘。如何提高UINX的開發效率呢? 在1971年,K&D來發出C語言,由解釋型語言(B語言)變成編譯型語言。 C語言天生就是為了開發作業系統而存在的,其目標是高效,最終程式執行效率高。但是起初是用來編寫U

C++程序不輸出到底是什麽造成了程序不輸出

print 原因 文章 spa xxx cpp ora 問題 ext (ubuntu 16.04) 最近做題的時候,經常莫名其妙地,程序寫的明明沒毛病但是就是輸出不了,氣得我呀 然後某一次突然發現了原因,竟然是輸出之後沒有加endl或者空格! 例如: cout<<

c語言入門軟體dev新建工程、執行和除錯

dev新建工程、執行和除錯 上一次安裝中,曾讓你們把安裝路徑記下來,現在我們可以找到安裝路徑,拷貝出裡面的help資料夾,開啟到這裡,我們將比較官方的形式來了解以下dev的使用。 1.Editing 在編輯之前,我們需要新建一個dev的工程和擬寫一個簡單的程式

C++模版之旅神奇的Traits

介紹traits的文章很多,但感覺大部分文章的說明都很晦澀難懂,把一個並不很複雜的C++模板的應用描述的過於複雜。忍不住想把自己的理解跟大家分享一下,或許我也只是掌握了一點traits的皮毛而已,但也希望這些皮毛能略微抓住你的眼球,帶給你一些啟發。 首先,介紹traits前

c++遇坑警告strcpy_s函式的使用

一、函式原型 ACRTIMP errno_t __cdecl strcpy_s( _Out_writes_z_(_SizeInBytes) char* _Destinatio

C++ Builder 6.0設定DBGrid單元顯示格式

1.設定列的展示格式 DBGrid的DataSource設定一個ADOquery。 雙擊ADOquery,在彈出的欄位編輯器中,新增要設定的欄位。 點選新增欄位,設定displayformat屬性。例如展示為,帶有三位小數。0.000 2.設定行高 ((TStringGrid

C#反射-Type類

一.反射的作用: 檢視和遍歷型別和型別的元資料;動態建立型別例項,動態的呼叫所建立的例項方法和欄位,屬性,遲繫結方法和屬性。 二.獲取Type物件例項: 反射的核心類-Type,封裝了關於型別的元資料,是進行反射的入口。當獲得了型別的Type物件後,可以根據Type提供的書