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. MonkeyTalk使用方法

    1.简单介绍 MonkeyTalk软件测试工具由两部分构成:MonkeyTalk IDE 和 MonkeyTalk Agents MonkeyTalk IDE是Eclipse平台的工具,工能是:对iO ...

  2. 笔记44 Hibernate快速入门(一)

    一.Hibernate简介 Hibernate 是传统 Java 对象和数据库服务器之间的桥梁,用来处理基于 O/R 映射机制和模式的那些对象. Hibernate 架构是分层的,作为数据访问层,你不 ...

  3. 记一次Mysql占用内存过高的优化过程

    一.环境说明: 操作系统:CentOS 6.5 x86_64 数据库:Mysql 5.6.22 服务器:阿里云VPS,32G Mem,0 swap 二.问题情况: 1.某日发现公司线上系统的Mysql ...

  4. MySQL日期格式化 利用Mysql的DATE_FORMAT()进行日期格式转换

    碰到一个MYSQL的问题,表logstatb中moment字段的内容是"年-月-日 时:分:秒",需要查询匹配“年月日”或“时:分:秒”即可的数据条目,这个时候就可以通过下面的SQ ...

  5. TopCoder[TCO2016 Round 1A]:EllysTree(1000)

    Problem Statement      Elly has a graph with N+1 vertices, conveniently numbered from 0 to N. The gr ...

  6. NX二次开发-UFUN写入本地文本文档uc4524

    1 NX9+VS2012 2 3 #include <uf.h> 4 #include <uf_cfi.h> 5 #include <uf_ui.h> 6 7 us ...

  7. MFC-CString与int互相转化

    1. CString转int ; CString str = _T("123"); n = _ttoi(str); 2. int转CString ; CString str; st ...

  8. JVM内核-原理、诊断与优化学习笔记(三):常用JVM配置参数

    文章目录 Trace跟踪参数 -verbose:gc (打开gc的跟踪情况) -XX:+printGC(打开gc的log开关,如果在运行的过程中出现了gc,就会打印出相关的信息.) -XX:+Prin ...

  9. CodeForces 1152F1 Neko Rules the Catniverse (Small Version)

    题目链接:http://codeforces.com/problemset/problem/1152/F1 题目大意 有 n 个星球,给定限制 m,从 x 星球走到 y 星球的条件是,$1 \leq ...

  10. 升级MySQL5.7.22版本_总结记录

    目录 一. mysql5.7安装 0. 背景 1. 准备:下载安装包 2. 安装流程小结 3. 具体步骤 二. mysql5.7的一些变化 一. mysql5.7安装 0. 背景 之前用的5.6版本, ...