1、创建IDL文件,定义接口。

IDL文件可以由uuidgen.exe创建。

首先找到系统中uuidgen.exe的位置,如:C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools。在此目录下运行命令"uuidgen

/i /ohello.idl",即可在该位置生成一个IDL文件:hello.idl。文件内容如下:

//hello.idl

[

uuid(b2617491-ba5a-48a9-b388-9f0cee8ec882),

version(1.0)

]

interface INTERFACENAME

{

}

然后,增加接口。如下:

//hello.idl

[

uuid(b2617491-ba5a-48a9-b388-9f0cee8ec882),

version(1.0)

]

interface INTERFACENAME

{

void HelloProc([in,string]unsigned char* szhello);

void ShutDown(void);

}

2、创建acf文件。

hello.acf文件内容如下:

//hello.acf

[

implicit_handle (handle_t hello_IfHandle)

]

interface INTERFACENAME

{

}

注意: 1)hello.idl文件与hello.acf文件中的接口名称(INTERFACENAME)应一致,否则接下来编译的时候会报错。

 2)hello.idl文件与hello.acf文件应放在同一目录下。

3、编译IDL文件。

有资料说可以用"midl hello.idl"命令直接进行编译,但是我试过之后,总是提示MIDL1005 的错误,没办法,用vs2008进行编译的,步骤如下

首先,创建一个空的项目,如RpcTest将编辑好的hello.idl文件添加至RpcTest项目中。

然后,直接进行编译。

这时就可以看到RpcTest项目的生成目录下有了hello_h.h, hello_c.c, hello_s.c三个文件。其中,hello_h.h文件是客户端和服务器端程序共

同要用到的,hello_c.c是客户端程序需要的,hello_s.c是服务器程序所需要的。

在hello_h.h文件中可以看到hello.idl中所定义的接口实体,一个全局句柄变量(handle_t)以及客户端与服务端的接口句柄名

INTERFACENAME_v1_0_c_ifspec和INTERFACENAME_v1_0_s_ifspec。客户端、服务端应用程序在实时调用将使用接口句柄名。



/* interface INTERFACENAME */

/* [implicit_handle][version][uuid] */ 

void HelloProc(/* [string][in] */ unsigned char *szhello);

void ShutDown( void);

extern handle_t hello_IfHandle;

extern RPC_IF_HANDLE INTERFACENAME_v1_0_c_ifspec;

extern RPC_IF_HANDLE INTERFACENAME_v1_0_s_ifspec;

4、编写服务器程序。

服务端通过调用RPC实现函数RpcServerUseProtseqEp 与RpcServrRegisterIf捆绑信息并提供给客户端,例子程序传递接口句柄名给

RpcServerRegisterIf,其它的参数被置为空,客户端然后调用RpcServerListen函数等待客户端的请求。

服务端应用程序必须包含两个内存管理函数midl_user_allocate与midl_user_free。当远端过程调用向服务端传递参数时,调用这两个函数分

配及释放内存。

除此之外,服务端还应实现具体的接口函数功能。详细代码如下。

//server.cpp

#include <iostream>

using namespace std;

#include "hello_h.h"

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(

  INTERFACENAME_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;

}

/************************************************************************/

/*                        MIDL malloc & free                            */

/************************************************************************/

void * __RPC_USER MIDL_user_allocate(size_t len)

{

 return (malloc(len));

}

void __RPC_USER MIDL_user_free(void*ptr)

{

 free(ptr);

}

/************************************************************************/

/*                       Interfaces                                     */

/************************************************************************/

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;

 }

}

5、编译服务端程序。

再次利用刚才的空项目RpcTest。

1)首先将刚刚加入的hello.idl文件从项目中移除。

2)然后加入hello_h.h, hello_s.c, server.cpp三个文件。

3)为项目加入rpc库文件:rpcrt4.lib。

4)编译生成RpcTest.exe,更名为server.exe。

6、编写客户端程序。

hello_c.c 源文件中定义了hello_h.h,它由MIDL生成,在它内部又预定义了rpc.h与rncndr.h它们包含了客户端、服务端应用程序所使用的实时

程序及数据类型,客户端管理着它到服务端的连接,客户端应用程序调用实时函数建立用来连接服务端的句柄,当远端过程调用完成时再释放

它。RpcStringBindingCompose 把代表句柄和为字符串绑定而配置内存的成份组装成字符串。RpcBindingFromStringBinding 根据上一个字符

串为客户端应用程序创建一个服务端绑定句柄。接口端点的指定,方法很多,最终方式取决于使用的协议,例子中使用的是Named pipes,它使

用的IDL字符串是“ncacn_np”,则终点名称就填写”\\pipes\\idlfilename”。

RPC异常处理通过一整套宏处理可以使你控制外部应用程序代码出错引起的异常现象,如有发生,将会调用RpcExcept模块,在这里你需要清除

内存并安全退出。远端过程调用结束后,客户端首先调用RpcStringFree函数,释放设置字符串捆绑的内存,然后调用RpcBindgFree()去释放句

柄。

详细代码如下。

//client.cpp

#include <iostream>

#include <string>

using namespace std;

#include "hello_h.h"

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);   

}

7、编译客户端程序。

再次利用刚才的空项目RpcTest。

1)首先将刚刚加入的hello_h.h等文件从项目中全部移除。

2)然后加入hello_h.h, hello_c.c, client.cpp三个文件。

3)为项目加入rpc库文件:rpcrt4.lib。

4)编译生成RpcTest.exe,更名为client.exe。

8、大功告成。

OK,到现在,已经有了客户端、服务端应用程序的可执行文件。

1)首先运行server.exe。

2)而后,在client.exe所在的目录下用命令行"client.exe -ip 192.168.1.146"来启动客户端程序并与服务器端相连。

3)在client的窗口内输入任意字符串,回车后可看到server窗口上有显示。

4)在client窗口内输入exit或quit,server窗口关闭。

RPC远程过程调用实例详解的更多相关文章

  1. RPC框架调用过程详解

    RPC框架调用过程详解 2017年09月16日 21:14:08 荷叶清泉 阅读数 6275   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. ...

  2. linux基础-磁盘阵列(RAID)实例详解

    磁盘阵列(RAID)实例详解 raid技术分类 软raid技术 硬raid技术 Raid和lvm的区别 为什么选择用raid RAID详解 RAID-0 RAID-1 RAID-5 Raid-10 R ...

  3. Cocos2d-x 3.X手游开发实例详解

    Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...

  4. JavaScript学习笔记-实例详解-类(二)

    实例详解-类(二)   //===给Object.prototype添加只读\不可枚举\不可配置的属性objectId(function(){ Object.defineProperty(Object ...

  5. JavaScript学习笔记-实例详解-类(一)

    实例详解-类(一): //每个javascript函数(除了bind())都自动拥有一个prototype对象// 在未添加属性或重写prototype对象之前,它只包含唯一一个不可枚举属性const ...

  6. Entity Framework实例详解

    Entity Framework Code First的默认行为是使用一系列约定将POCO类映射到表.然而,有时候,不能也不想遵循这些约定,那就需要重写它们.重写默认约定有两种方式:Data Anno ...

  7. 免费的HTML5连载来了《HTML5网页开发实例详解》连载(二)

    最近新浪.百度.腾讯.京东.大众点评.淘宝等流行的网站都加大了招聘HTML5的力度,HTML5开发人员成了抢手货,本次连载的是由大众点评前端工程师和一淘网前端工程师基情奉献的<HTML5网页开发 ...

  8. Linux下rz命令使用的实例详解

    Linux中rz命令和sz命令都可用于文件传输,而rz命令主要用于文件的上传,下面将通过几个实例来给大家详细介绍下Linux下rz命令的用法,一起来学习下吧. rz命令可以批量上传文件,当然也可上传单 ...

  9. 实例详解 DB2 排序监控和调优

    实例详解 DB2 排序监控和调优http://automationqa.com/forum.php?mod=viewthread&tid=2882&fromuid=2

随机推荐

  1. centos7 sshd 安全设置

    ssh 的安全机制 1.SSH之所以能够保证安全,原因在于它采用了非对称加密技术(RSA)加密了所有传输的数据.   2.传统的网络服务程序,如FTP等在网络上用明文传送数据.用户帐号和用户口令,很容 ...

  2. 泛型(Generic)接口

    泛型接口例子:一个学生有一个独一无二的ID,但是每个学生的姓名不一定是唯一的. class Program { static void Main(string[] args) { Student< ...

  3. Batch - 忽略FORFILES “no files found” error

    ref:https://stackoverflow.com/questions/16820681/suppress-forfiles-no-files-found-error Solution: Th ...

  4. vue中使用axios与axios的请求响应拦截

    VUE中使用Axios axios的安装 npm install axios vue-axios axios在vue的配置与使用 在main.js中引入axios和vue-axios import a ...

  5. 【Http】keepalive

    http是现在web领域极其普遍的应用层传输协议, 目前常见的使用版本则是http1.1, 当然最先版本是http2.0. 传统的Http应用里都是一次TCP连接一次request.   image ...

  6. css 布局,过渡

    做了一个小案例,关于我们内边距的处理的,然后再加上一些过渡效果 效果: 具体实现重点加上这里: 具体代码实现: CSS部分: <style> #container{ border:2px ...

  7. delphi 获取文件图标

    {根据文件的名字得到此文件在系统中对应大小的图标large=true(64*64) false(32*32)}procedure GetFileIcon(TypeName: Widestring; I ...

  8. NX二次开发-UFUN多选菜单对话框uc1605

    NX11+VS2013 #include <uf.h> #include <uf_ui.h> UF_initialize(); //多选菜单对话框 char sPromptSt ...

  9. Dll注入技术之ComRes注入

    DLL注入技术之ComRes注入 ComRes注入的原理是利用Windows 系统中C:\WINDOWS\system32目录下的ComRes.dll这个文件,当待注入EXE如果使用CoCreateI ...

  10. 用注册表创建无法删除的IE快捷方式

    代码如下: Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID/{98745625-1234 ...