http://msdn.microsoft.com/en-us/library/windows/desktop/aa379010(v=vs.85).aspx

注意:原文版本较老,我更新和改变了部分内容。并提供了完整的程序。编译环境SDK 7.0  WinXP VS2010。

RPC官方教程

此手册可使你从已经存在的单独程序,一步步地创建简单的、单客户端、单服务端的分布式程序。步骤如下:

  • 设计接口和创建程序配制文件。
  • 使用MIDL编译器来创建C语言客户端桩、服务端桩及相关头文件。
  • 写客户端程序。它负责管理链接服务端。
  • 写一个包含远程过程的服务端。
  • 编译并链接RPC动态库来运行这些分布式程序。

客户端程序通过远程过程调用传递一个字符串给服务端,服务端输出字符串“Hello,World”到命令行控制台中。完成的源码在SDK/RPC/Hello目录中。

此文内容分类如下:

  • 单独程序
  • 定义接口
  • 生成UUID
  • IDL文件
  • ACF文件
  • 生成Stub文件
  • 客户端程序
  • 服务端程序

单独程序

此脱单独程序由一个函数组成。此程序是我们的分布式程序的样板。函数HelloProc定义在单独的源文件中,这个它可以单独程序或分布式程序中复用它。

  1. /* file hellop.c */
  2. #include <stdio.h>
  3. #include <windows.h>
  4. void HelloProc(char * pszString)
  5. {
  6. printf("%s\n", pszString);
  7. }
  8. /* file: hello.c, a stand-alone application */
  9. #include "hellop.c"
  10. void main(void)
  11. {
  12. char * pszString = "Hello, World";
  13. HelloProc(pszString);
  14. }

定义接口

接口定义一个标准规范,定义了客户端与服务端是如何通信的。此接口定义了如何识别对方,客户端程序如何远程调用及如何处理远程调用的参数和返回值。它也指定了数据的传输方式。你以MIDL(Microsoft Interface Definition Language)方式定义的接口包含了C风格的参数,并使用关键字定义这些参数的属性。这些属性描述了数据如何在网络上传送。接口定义(IDL)文件包含了类型定义,属性定义和函数原型定义,这些用于描述数据如何在网络中传输。ACF文件包含配置程序的属性,但不影响网络。

生成UUID

第一步是使用uuidgen工作生成一个UUID。使用UUID客户端和服务端就可以彼此辨别。Uuidgen工具(uuidgen.exe)在安装SDK时就已经自动安装了。

下面的命令创建了一个UUID和Hello.idl临时文件。

uuidgen /i /ohello.idl

你的hello.idl文件内容可能是这样(当然UUID会不同)

  1. [
  2. uuid(7a98c250-6808-11cf-b73b-00aa00b677a7),
  3. version(1.0)
  4. ]
  5. interface INTERFACENAME
  6. {
  7. }

DIL文件

IDL文件由一个或多个接口定义组成,每一个接口定义都有一个接口头和一个接口体。接口头包含了使用此接口的信息,如UUID。这此信息封装一对中括号之内。之后是interface关键和接口名。接口体包含了C风格的数据类型和函数原型和带属性的参数。这此参数的属性描述了数据如何在网络上传送。此例中,定义头中包含了UUID和版本号。版本号作兼容之用。客户端、服务端的版本只有兼容了,才可以连接。接口体包含了HelloProc函数原型。函数的参数pszString有两个属性[in]和[string]。[in]函数告诉运行库此参数只能从客户端传送到服务端。[string]属性指定了桩要按C风格的字符串来处理此参数。客户端程序可以关闭服务端程序,所以此接口包含了另一个函数Shutdown。

  1. //file hello.idl
  2. [
  3. uuid(7a98c250-6808-11cf-b73b-00aa00b677a7),
  4. version(1.0)
  5. ]
  6. interface hello
  7. {
  8. void HelloProc([in, string] unsigned char * pszString);
  9. void Shutdown(void);
  10. }

ACF文件

ACF文件使你自定义你的客户端或服务端的RPC接口,但不影响网络特征。例如,如果你的客户端程序包含了一个复杂的数据结构,此数据结构只在本地机上有意义,那么你就可以在ACF文件中指定如何描述独立与机器的数据结构,使用数据结构用于远程过程调用。此教程演示了一个ACF文件 ---指定一个绑定的handle类型,用来代表客户端与服务端的连接。[implicit_handle]属性允许客户端程序为它的远程过程调用选择一个服务端。ACF定义了此句柄为handle_t类型(MIDL基本数据类型)。MIDL编译器将绑定ACF文件指定的句柄名字hello_IfHandle,放在生成的头文件中。注意此ACF文件的体为空。

  1. //file: hello.acf
  2. [
  3. implicit_handle (handle_t hello_IfHandle)
  4. ]
  5. interface hello
  6. {
  7. }

MIDL编译器有一个选项 /app_config。此选项可使你包含某一个ACF属性,如implicit_handle。在IDL文件中,而不是创建一个ACF文件。如果你的程序不需要指定大量配置和不考虑OSF兼容,则可以考虑使用此选项。

生成Stub文件

在定义了客户端/服务端接口之后,你将编写客户端和服务端源码。之后使用一个makefile文件生成桩和头文件。再编译链接客户端/服务端程序。 但是,如果你第一次探究分布式计算环境。你可能想调用MIDL编译器,来查看MIDL产生了什么文件。 MIDL编译器(Midl.exe)在安装SDK时已自动安装。当你编译这些文件时,确保Hello.idl 和 Hello.acf在同一文件夹中。下面的命令将生成Hello.h头文件和客户端、服务端桩: Hello_c.c and Hello_s.c.

midl hello.idl

你将在服务端程序中提供这两个函数。如果数据从服务端传送到客户端(要使用一个[out]参数),你也要在客户端提供两个内存管理函数。全局变量,hello_IfHandle,和客户端、服务端接口名字:hello_v1_0_c_ifspec 和 hello_v1_0_s_ifspec. 客户端和服务端程序将在运行时使用这些接口句柄。你仅使用桩文件Hello_c.c and hello_s.c就可以了。

  1. /* this ALWAYS GENERATED file contains the definitions for the interfaces */
  2. /* File created by MIDL compiler version 7.00.0555 */
  3. /* at Sun Dec 01 09:32:11 2013
  4. */
  5. /* Compiler settings for hello.idl:
  6. Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555
  7. protocol : dce , ms_ext, c_ext, robust
  8. error checks: allocation ref bounds_check enum stub_data
  9. VC __declspec() decoration level:
  10. __declspec(uuid()), __declspec(selectany), __declspec(novtable)
  11. DECLSPEC_UUID(), MIDL_INTERFACE()
  12. */
  13. /* @@MIDL_FILE_HEADING(  ) */
  14. #pragma warning( disable: 4049 )  /* more than 64k source lines */
  15. /* verify that the <rpcndr.h> version is high enough to compile this file*/
  16. #ifndef __REQUIRED_RPCNDR_H_VERSION__
  17. #define __REQUIRED_RPCNDR_H_VERSION__ 475
  18. #endif
  19. #include "rpc.h"
  20. #include "rpcndr.h"
  21. #ifndef __RPCNDR_H_VERSION__
  22. #error this stub requires an updated version of <rpcndr.h>
  23. #endif // __RPCNDR_H_VERSION__
  24. #ifndef __hello_h__
  25. #define __hello_h__
  26. #if defined(_MSC_VER) && (_MSC_VER >= 1020)
  27. #pragma once
  28. #endif
  29. /* Forward Declarations */
  30. #ifdef __cplusplus
  31. extern "C"{
  32. #endif
  33. #ifndef __hello_INTERFACE_DEFINED__
  34. #define __hello_INTERFACE_DEFINED__
  35. /* interface hello */
  36. /* [implicit_handle][version][uuid] */
  37. void HelloProc(
  38. /* [string][in] */ unsigned char *pszString);
  39. void Shutdown( void);
  40. extern handle_t hello_IfHandle;
  41. extern RPC_IF_HANDLE hello_v1_0_c_ifspec;
  42. extern RPC_IF_HANDLE hello_v1_0_s_ifspec;
  43. #endif /* __hello_INTERFACE_DEFINED__ */
  44. /* Additional Prototypes for ALL interfaces */
  45. /* end of Additional Prototypes */
  46. #ifdef __cplusplus
  47. }
  48. #endif
  49. #endif

客户端程序

此例在SDK下RPC目录下Hello目录中。 Hello.c源文件包含了MIDL生成头文件Hello.h。在Hello.h中包含了Rpc.h和rpcndr.h,还有HelloProc和Shutdown,及客户端、服务端使用的数据类型。在此例中必须使用MIDL。.因为此客户端管理与服务端的连接。客户端程序调用运行时函数来建立一个指向服务端的句柄,在远程过程调用完成后,释放这个句柄。函数RpcStringBindingCompose组合此函数的参数生成一个以字符串表示的绑定句柄(binding handle)并为此字符串分配内存。

函数RpcBindingFromStringBinding从字符串表示的绑定句柄,创建了一个服务绑定句柄,hello_ClientIfHandle。调用函数RpcStringBindingCompose,它的参数并不是UUID。因为此教程假定只有一个接口”hello”的实现。另外,此调用没有指定网络地址。因为此程序使用默认机制:本地调用机制。Protocol sequence是一个字符串,表示了具体网络传送。

Endpoint指定了具体的protocol sequence名称。此例使用了命名管道为网络传输方式,所以protocol sequence为”ncacn_np”。Endpoint名字为”\pipe\hello”。

远程过程调用:HelloProc和Shutdown,包含在RPC的异常处理函数中。RPC的异常处理函数是一系列宏,可使程序发生异常时,在代码外处理异常。如果RPC运行模块报告了一个异常,程序就会跳转到RpcExcept代码块中。

在此代码块中,你可以插入代码,来做一些清理工作,然后退出程序。.此例中程序仅简单地知道用户发生了一个异常。如果你不想使用异常,你可以使用ACF属性comm_status和fault_status来报告错误。在远程过程调用完成后,客户端程序首先调用RpcStringFree来释放先前字符串绑定申请的内存。

  1. /* file: helloc.c */
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <ctype.h>
  5. #include "hello.h"
  6. #include <windows.h>
  7. void main()
  8. {
  9. RPC_STATUS status;
  10. unsigned char * pszUuid             = NULL;
  11. unsigned char * pszProtocolSequence = "ncacn_np";
  12. unsigned char * pszNetworkAddress   = NULL;
  13. unsigned char * pszEndpoint         = "\\pipe\\hello";
  14. unsigned char * pszOptions          = NULL;
  15. unsigned char * pszStringBinding    = NULL;
  16. unsigned char * pszString           = "hello, server";
  17. unsigned long ulCode;
  18. status = RpcStringBindingCompose(pszUuid,
  19. pszProtocolSequence,
  20. pszNetworkAddress,
  21. pszEndpoint,
  22. pszOptions,
  23. &pszStringBinding);
  24. if (status) exit(status);
  25. //status = RpcBindingFromStringBinding(pszStringBinding, &hello_ClientIfHandle);
  26. status = RpcBindingFromStringBinding(pszStringBinding, &hello_IfHandle);
  27. if (status) exit(status);
  28. RpcTryExcept
  29. {
  30. HelloProc(pszString);
  31. Shutdown();
  32. }
  33. RpcExcept(1)
  34. {
  35. ulCode = RpcExceptionCode();
  36. printf("Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
  37. }
  38. RpcEndExcept
  39. status = RpcStringFree(&pszStringBinding);
  40. if (status) exit(status);
  41. status = RpcBindingFree(&hello_IfHandle);
  42. if (status) exit(status);
  43. exit(0);
  44. }
  45. /******************************************************/
  46. /*         MIDL allocate and free                     */
  47. /******************************************************/
  48. void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
  49. {
  50. return(malloc(len));
  51. }
  52. void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
  53. {
  54. free(ptr);
  55. }

服务端程序

此例中必须 使用MIDL。根据你自己的编码风格,你可以一个或多个独立的文件中实现这些远程过程。此教程的程序,Hellos.c源文件包含了主要的服务端框架代码。Hellop.c包含了远程过程。将远程过程单独放在一个单独的文件中有一个好处:在转换成分布程序之前,可以链接它到非分布式程序上调试。在非分布式程序上调通之后,你可以编译链接此文件到分布式服务端。Hello.h头文件为客户端源文件,服务端源码也要包含此头文件。服务端调用RPC运行库函数RpcServerUseProtseqEp和RpcServerRegisterIf来建立有效的客户端。此例中,程序向函数RpcServerRegisterIf传递接口句柄。另一个参数为NULL。之后,服务端调用函数RpcServerListen来等待客户端的请求。

服务端必须包含两个内存分配函数。这两个函数被服务桩程序调用:midl_user_allocate和midl_user_free。这两个函数在服务端分配和释放内存,当远程过程传送参数给服务端时。此例中,midl_user_allocate和midl_user_free简单地封装了C库函数malloc和free。(注意,在MIDL编译器生成的前置声明中,”MIDL”是大小的。头文件Rpcndr.h定义了midl_user_free和midl_user_allocate。这两个函数指向MIDL_user_free 和MIDL_user_allocate。

  1. /* file: hellos.c */
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <ctype.h>
  5. #include "hello.h"
  6. #include <windows.h>
  7. handle_t hello_IfHandle;
  8. void main()
  9. {
  10. RPC_STATUS status;
  11. unsigned char * pszProtocolSequence = "ncacn_np";
  12. unsigned char * pszSecurity         = NULL;
  13. unsigned char * pszEndpoint         = "\\pipe\\hello";
  14. unsigned int    cMinCalls = 1;
  15. unsigned int    fDontWait = FALSE;
  16. status = RpcServerUseProtseqEp(pszProtocolSequence,
  17. RPC_C_LISTEN_MAX_CALLS_DEFAULT,
  18. pszEndpoint,
  19. pszSecurity);
  20. if (status) exit(status);
  21. //RPC_S_INVALID_ENDPOINT_FORMAT
  22. //status = RpcServerRegisterIf(hello_ServerIfHandle, NULL, NULL);
  23. //status = RpcServerRegisterIf(hello_IfHandle, NULL, NULL);
  24. status = RpcServerRegisterIf(hello_v1_0_s_ifspec, NULL, NULL);
  25. if (status) exit(status);
  26. status = RpcServerListen(cMinCalls,
  27. RPC_C_LISTEN_MAX_CALLS_DEFAULT,
  28. fDontWait);
  29. if (status) exit(status);
  30. }
  31. /******************************************************/
  32. /*         MIDL allocate and free                     */
  33. /******************************************************/
  34. void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
  35. {
  36. return(malloc(len));
  37. }
  38. void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
  39. {
  40. free(ptr);
  41. }
  42. e(ptr);
  43. }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

参考

  1. RpcStringBindingCompose function
  2. The RpcStringBindingCompose function creates a string binding handle.
  3. Syntax
  4. C++
  5. RPC_STATUS RPC_ENTRY RpcStringBindingCompose(
  6. TCHAR *ObjUuid,
  7. TCHAR *ProtSeq,
  8. TCHAR *NetworkAddr,
  9. TCHAR *EndPoint,
  10. TCHAR *Options,
  11. TCHAR **StringBinding
  12. );

String Binding

The string binding is an unsigned character string composed of strings that represent the binding object UUID, the RPC protocol sequence, the network address, and the endpoint and endpoint options.

ObjectUUID@ProtocolSequence:NetworkAddress[Endpoint,Option]

源码下载

微软RPC官方教程的更多相关文章

  1. Windows XP SP3 Professional 微软(MSDN)官方原版系统

    Windows XP SP3 Professional 微软(MSDN)官方原版系统 Windows XP(版本号:5.1,开发代号:Whistler)是微软公司推出供个人电脑使用的操作系统,其RTM ...

  2. MVC4学习之官方教程中迁移版本库报错

    因工作需要,学习MVC4,但是微软官方教程中迁移版本库步骤在本地测试报错 官方教程地址:http://www.asp.net/mvc/overview/older-versions/getting-s ...

  3. Unity性能优化(3)-官方教程Optimizing garbage collection in Unity games翻译

    本文是Unity官方教程,性能优化系列的第三篇<Optimizing garbage collection in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...

  4. Unity性能优化(4)-官方教程Optimizing graphics rendering in Unity games翻译

    本文是Unity官方教程,性能优化系列的第四篇<Optimizing graphics rendering in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...

  5. Unity性能优化(2)-官方教程Diagnosing performance problems using the Profiler window翻译

    本文是Unity官方教程,性能优化系列的第二篇<Diagnosing performance problems using the Profiler window>的简单翻译. 相关文章: ...

  6. Unity性能优化(1)-官方教程The Profiler window翻译

    本文是Unity官方教程,性能优化系列的第一篇<The Profiler window>的简单翻译. 相关文章: Unity性能优化(1)-官方教程The Profiler window翻 ...

  7. jeecg表单页面控件权限设置(请先看官方教程,如果能看懂就不用看这里了)

    只是把看了官方教程后,觉得不清楚地方补充说明一下: 1. 2. 3. 4.用"jeecgDemoController.do?addorupdate"这个路径测试,不出意外现在应该可 ...

  8. [转]Google Guava官方教程(中文版)

    Google Guava官方教程(中文版) http://ifeve.com/google-guava/

  9. Google Guava官方教程(中文版)

    Google Guava官方教程(中文版) 原文链接  译文链接 译者: 沈义扬,罗立树,何一昕,武祖  校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, ...

随机推荐

  1. 文本聚合函数(wm_concat, listagg, group_concat, string_agg)

    实现目标   1.聚合文本   2.聚合文本(去重)   3.聚合文本(去重),按照指定字段排序   4.聚合文本(去重),按照指定字段排序,替换默认逗号分隔符 MySQL: group_concat ...

  2. 清除浏览器默认样式——css reset & normalize.css

    css reset 自己挨个清除很麻烦 可以使用网上一些css库——css reset 把模板复制到css文件最上方,其他的样式我们自己编写来覆盖它们 但是这个也有一些弊端,会把一些本来需要的样式给清 ...

  3. [DataContract]引用

    项目->右键->添加引用->找到System.Runtime.Serialization 添加之

  4. fastText一个库用于词表示的高效学习和句子分类

    fastText fastText 是 Facebook 开发的一个用于高效学习单词呈现以及语句分类的开源库. 要求 fastText 使用 C++11 特性,因此需要一个对 C++11 支持良好的编 ...

  5. KdPrint/DbgPrint and UNICODE_STRING/ANSI_STRING

    typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING ...

  6. 关于Mysql分区和分表

    [分区概念]分区就是把一张表的数据按照一定的规则分成多个区块,这些区块可以在同一个磁盘上,也可以在不同的磁盘上.分区后表还是一张表.分区根据一定的规则把数据文件和索引文件进行了分割,还多出了一个.pa ...

  7. POJ 3761 Bubble Sort

    题目链接:https://vjudge.net/problem/POJ-3761 转自:https://blog.csdn.net/cscj2010/article/details/7820906 题 ...

  8. sshpass批量分发ssh秘钥

    首先安装sshpass: yum -y install sshpass 单条命令: sshpass -p“password” ssh-copy-id -i /root/.ssh/id_rsa.pub ...

  9. sklearn提供的自带数据集

    sklearn 的数据集有好多个种 自带的小数据集(packaged dataset):sklearn.datasets.load_<name> 可在线下载的数据集(Downloaded ...

  10. uoj118 【UR #8】赴京赶考

    题目 不难发现我们直接走过去就行了 考虑到第\(i\)行的构造方法就是把\(b\)数组作为模板,每个数和\(a_i\)异或一下就可以了 于是不难发现对于一段连续相等的\(a\),它们在矩阵上就形成了完 ...