【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++學習記錄2】C到C++的升級
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提供的書