前言

该篇博文不是讲Windows rpc入门的。是笔者在实际使用Windows RPC时 所遇到的问题,以及解决方法。

笔者有这样的需求,需要从RPC Server获取大量数据,而且该数据是动态分配的。故此RPC Client在调用RPC Server方法是 需要动态获取空间。笔者在中文社区没有找到相关资料,最后只有去看官方文档,下面几个链接是官方文档。

https://docs.microsoft.com/en-us/windows/desktop/Midl/string 字符串的使用

https://docs.microsoft.com/en-us/windows/desktop/Midl/midl-arrays 数组的使用

https://docs.microsoft.com/en-us/windows/desktop/rpc/multiple-levels-of-pointers 多级指针使用

场景如下:

笔者需要从RPC Server获取学生列表信息,个数当然是不确定的,这肯定是动态分配的,学生信息中含有学生姓名和地址,这个是字符串,字符串的长度同样是不确定了,这个需要动态分配。所以这里会在RPC中用到 自定义数据类型、自定义数据类型数组、指针数组。看笔者是怎样处理的。

// 在C语言中笔者想接受的自定义数据类型是
typedef struct _STUDENT
{char *m_name; // 或者std::string类型char *m_address; // 或者std::string类型short m_age;short m_score;
}STUDENT,*PSTUDENT;// 按照MIDL数据类型的写法,应该为下面这样,但这样MIDL就无法编译过!
// 是因为 RPC对应可变长的字符串(运行期间进行分配空间) 是采用的柔性数组,从生成的头文件我们也能看出来。
// 柔性数组成员只能放在结构体最后且只能放置一个柔性数组成员!
typedef struct _STUDENT
{[string]char m_name[*];[string]char m_address[*];short m_age;short m_score;
}STUDENT,*PSTUDENT;

编译错误的图片,错误关键字 error MIDL2055 : field deriving from a conformant array must be the last member of the structure

因为在rpc调用是没有std::string类型的,需要使用官方推荐的字符串类型写法或者使用自定义的字符串

// 官方文档推荐的自定义字符串写法(带长度和大小)
typedef struct _MYSTRING
{ unsigned short size; unsigned short length; [ptr,size_is(size), length_is(length)] char string[*];
} MYSTRING;
typedef [ptr] MYSTRING** PPMYSTRING;
typedef [ptr] MYSTRING* PMYSTRING;

最终编译器生成的字符串类型如下:

typedef struct _MYSTRING{unsigned short size;unsigned short length;unsigned char string[ 1 ];}   MYSTRING;

idl文件

说了这么多,下面看下笔者的idl文件。

[
uuid(55ae06b7-1011-4654-a4eb-f3347325203f),
version(1.0),
pointer_default(unique)
]
interface RPCTest
{// 在C语言中我们想接受的自定数据类型数据//typedef struct _STUDENT//{//   char *m_name; // 或者std::string类型//  char *m_address; // 或者std::string类型//   short m_age;//  short m_score;//}STUDENT,*PSTUDENT;// 按照MIDL数据类型的写法,应该为下面这样,但这样MIDL就无法编译过!// 是因为 RPC对应可变长的字符串(运行期间进行分配空间) 是采用的柔性数组,从生成的头文件我们也能看出来。// 柔性数组成员只能放在结构体最后且只能放置一个柔性数组成员!//typedef struct _STUDENT//{// [string]char m_name[*];//   [string]char m_address[*];//    short m_age;//  short m_score;//}STUDENT,*PSTUDENT;// 官方文档推荐的自定义字符串写法(带长度和大小)typedef struct _MYSTRING{ unsigned short size; unsigned short length; [ptr,size_is(size), length_is(length)] char string[*]; } MYSTRING;typedef [ptr] MYSTRING** PPMYSTRING;typedef [ptr] MYSTRING* PMYSTRING;typedef struct _BASETYPEDATA{short m_age;short m_score;}BASETYPEDATA,*PBASETYPEDATA;// 这样我们把基础数据类型和指针数据类型分开,// 由于基础数据类型的长度已经固定了,所以我们只需要在RPCServer动态开辟一个数组空间即可,并个数返回给RPCClient// 那么我们的字符串呢?当然字符串也是动态开辟的,RPCServer返回给RPCClient的是一个[str1的地址,str2的地址,str3的地址...]这是一个指针数组,同样也将个数返回// 这个指针数组的空间 也是有RPCServer开辟的,而且指针数组的成员 也就是最终的字符串的空间也是由RPCServer开辟的,所以我们参数是3级指针// 获取信息列表void rpc_GetMsgList([out]int* pNum,[out,size_is(, *pNum)]PBASETYPEDATA *pBaseTypeData,[out,size_is(, *pNum)]PPMYSTRING *pNameList,[out,size_is(, *pNum)]PPMYSTRING *pAddressList);
}

这里顺带贴一下 笔者的编译命令,使用Visual Studio命令提示工具编译

midl E:\Learn_Note\Code\RPCTest\MIDL\RPCTest.idl /acf E:\Learn_Note\Code\RPCTest\MIDL\RPCTest.ACF /out E:\Learn_Note\Code\RPCTest\MIDL

代码

RPC Server 代码

main.cpp

#include <iostream>
#include <Windows.h>
#include "RPCTest.h"
#include "student.h"
#include <list>#define ENDPOINT_ADDRESS_NAME TEXT("\\pipe\\RPCTest")
using namespace std;void * __RPC_USER MIDL_user_allocate(size_t size)
{return(HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, size));
}void __RPC_USER MIDL_user_free( void *pointer)
{HeapFree(GetProcessHeap(), 0, pointer);
}void rpc_GetMsgList( /* [out] */ int *pNum,/* [size_is][size_is][out] */ PBASETYPEDATA *pBaseTypeData,/* [size_is][size_is][out] */ PPMYSTRING *pNameList,/* [size_is][size_is][out] */ PPMYSTRING *pAddressList)
{// 具体业务逻辑,根据实际情况申请内存空间// 这里模拟10个业务数据,具体业务参数的数据,需要通过rpc将数据返回给客户端list<STUDENT> students;STUDENT s;char tmp[50] = {0};for(int i = 0; i < 10 ; i++){memset(tmp,0,50);sprintf(tmp,"李四-%d",i);s.m_name = tmp;memset(tmp,0,50);sprintf(tmp,"北京街%d号",i);s.m_address = tmp;s.m_age = i * 10;s.m_score = i*10 + 9;students.push_back(s);}// RPC服务器 开辟空间,将数据返回给RPC客户端*pNum = students.size();size_t baseLen = *pNum * sizeof(BASETYPEDATA);size_t strLen = *pNum * sizeof(PMYSTRING);*pBaseTypeData = (PBASETYPEDATA)MIDL_user_allocate( baseLen );*pNameList = (PPMYSTRING)MIDL_user_allocate( strLen );*pAddressList = (PPMYSTRING)MIDL_user_allocate( strLen );list<STUDENT>::iterator it = students.begin();for(int i = 0; it != students.end() ; it++,i++){(*pBaseTypeData)[i].m_age = (*it).m_age;(*pBaseTypeData)[i].m_score = (*it).m_score;const char* str = (*it).m_name.c_str();int len = (*it).m_name.length();int allocateLen = ( len>=2 ? len+1 : 2 ) + 4; // sizeof(PMYSTRING) 6个字节,字符串size至少2个字节PMYSTRING ptr = NULL;(*pNameList)[i] = (PMYSTRING)MIDL_user_allocate(allocateLen);ptr = (*pNameList)[i];ptr->length = len;ptr->size = len >=2 ? len+1 :2;strncpy( (char*)ptr->string, str , len );str = (*it).m_address.c_str();len = (*it).m_address.length();allocateLen = ( len>=2 ? len+1 : 2 ) + 4;(*pAddressList)[i] = (PMYSTRING)MIDL_user_allocate(allocateLen);ptr = (*pAddressList)[i];ptr->length = len;ptr->size = len >=2 ? len+1 :2;strncpy( (char*)ptr->string, str , len );}
}int main()
{RPC_STATUS status;unsigned char*   pszSecurity         = NULL;unsigned int    cMinCalls           = 120;unsigned int    cMaxCalls           = 120;status = RpcServerUseProtseqEp(reinterpret_cast<unsigned short*>(TEXT("ncacn_np")),RPC_C_PROTSEQ_MAX_REQS_DEFAULT,reinterpret_cast<unsigned short*>(ENDPOINT_ADDRESS_NAME),pszSecurity);if (status != RPC_S_OK){printf("RpcServerUseProtseqEp failed!\n");}// Register the services interface(s).status = RpcServerRegisterIf(RPCTest_v1_0_s_ifspec,       // Interface to register.NULL,                          // Use the MIDL generated entry-point vector.NULL);                     // Use the MIDL generated entry-point vector.if (status != RPC_S_OK){cout << "RpcServerRegisterIf failed!" << endl;return 0;}cout << "RpcServerRegisterIf suc!" << endl;status = RpcServerListen(cMinCalls,cMaxCalls,TRUE);if (status != RPC_S_OK){cout << "RpcServerListen failed!" << endl;return 0;}cout << "RpcServerListen suc!" << endl;status = RpcMgmtWaitServerListen();cout << "RpcMgmtWaitServerListen over!"<< endl;return 0;
}

RPC Client 代码

main.cpp

#include <iostream>
#include <Windows.h>
#include "RPCTest.h"
#include "student.h"
#include <list>#define ENDPOINT_ADDRESS_NAME TEXT("\\pipe\\RPCTest")
using namespace std;void  __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{void* ret = malloc(len);memset(ret,0,len);return ret;
}void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{free(ptr);
}RPC_WSTR g_pszStringBinding;bool ClearClient()
{if(RPCTest_IFHandle){RpcBindingFree(&RPCTest_IFHandle);RPCTest_IFHandle = NULL;}if(g_pszStringBinding){RpcStringFree(&g_pszStringBinding);g_pszStringBinding = NULL;}return true;
}bool PrepareClient()
{RPC_STATUS status;RPC_WSTR pszNetworkAddress   = NULL;// Creates a string binding handle.// This function is nothing more than a printf.// Connection is not done here.status = RpcStringBindingCompose(NULL, // UUID to bind to.reinterpret_cast<unsigned short*>(TEXT("ncacn_np")),pszNetworkAddress,reinterpret_cast<unsigned short*>(ENDPOINT_ADDRESS_NAME),NULL,&g_pszStringBinding);if (status != RPC_S_OK){return false;}/* Set the binding handle that will be used to bind to the server. */status = RpcBindingFromStringBinding(g_pszStringBinding,&RPCTest_IFHandle);if (status != RPC_S_OK) {if(g_pszStringBinding){RpcStringFree(&g_pszStringBinding);g_pszStringBinding = NULL;}return false;}return true;
}void GetMsgList(list<STUDENT>& result)
{if(!PrepareClient()){return ;}int num = 0;PBASETYPEDATA baseData = NULL;PPMYSTRING nameList = NULL;PPMYSTRING addressList = NULL;rpc_GetMsgList(&num,&baseData,&nameList,&addressList);// 将从RPCServer获取的数据进行组装for(int i = 0 ; i < num; i++){STUDENT s;s.m_age = baseData[i].m_age;s.m_score = baseData[i].m_score;s.m_name = (char*)nameList[i]->string;s.m_address = (char*)addressList[i]->string;result.push_back(s);// 拷贝完一个单元数据,释放一个单元数据midl_user_free(nameList[i]);midl_user_free(addressList[i]);}// 释放基础数据数组midl_user_free(baseData);// 释放指针数组内存midl_user_free(nameList);midl_user_free(addressList);ClearClient();return ;
}int main()
{list<STUDENT> students;GetMsgList(students);list<STUDENT>::iterator it = students.begin();for(; it != students.end() ; it++ ){cout << (*it).m_name << " "<< (*it).m_address << " " << (*it).m_age << " "<< (*it).m_score << endl;}system("pause");return 0;
}

运行结果

完整工程

RPCServer、RPCClient、idl文件打包下载地址 这里下载

C/C++:Windows编程—Windows RPC 传递自定义数据类型、自定义数据类型数组、指针数组相关推荐

  1. Windows编程—Windows驱动中定时器的使用

    文章目录 Windows编程-Windows驱动中定时器的使用 前言 代码 简单版 升级版 程序效果 Windows编程-Windows驱动中定时器的使用 前言 定时器操作是应用编程中非常常见的操作, ...

  2. Windows编程—Windows驱动开发环境搭建

    文章目录 前言 步骤 步骤一 步骤二 步骤三 连接测试 步骤四 步骤五 总结 前言 作为一个编写Windows程序的开发人员,对Windows驱动开发 并非必需要掌握,但是掌握 Windows驱动开发 ...

  3. Windows编程 Windows程序的生与死(下)

    再谈程序之"死" 记得在第二回中我对程序的"死"只是一句话带过,因为我还没有铺垫好,好了现在我们可以详细的分析一下这个过程了. 这还要从while消息循环说起, ...

  4. Windows编程(2)

    文章目录 windows有关的重要概念 句柄 窗口 Windows的消息机制 消息结构体 宽字符与Unicode 宽字符串的长度 ASCII 和 Unicode 兼容性问题 Windows数据类型 w ...

  5. 【C 语言】二级指针内存模型 ( 指针数组 | 二维数组 | 自定义二级指针 | 将 一、二 模型数据拷贝到 三 模型中 并 排序 )

    文章目录 一.指针数组 和 二维数组 数据 拷贝到 自定义二级指针 中 1.函数形参 设计规则 2.三种内存模型 对应 函数形参 指针退化规则 二.完整代码示例 一.指针数组 和 二维数组 数据 拷贝 ...

  6. Windows 编程

    Delphi 利用Object Pascal 和可视控件库(VCL)对底层的Windows API 进行了完美的封装,所以很少需要使用基础Pascal 语言来建立Windows应用程序,也无需直接调用 ...

  7. Windows编程之调用Matlab

    一.选择matlab: 注意.Matlab的安装需要较长时间,建议本拓展在同学们自己的电脑上运行. Matlab是大多数工科学生必修的科目,是一个口碑极佳的数学计算工具,可以支持立即运算和程序设计两种 ...

  8. Windows编程-001

    如果建立的是Win32控制台工程(入口函数是main函数)的话,WinMain函数不能作为入口函数,如果想要解决这个问题的话,可以打开项目属性->链接器->系统->子系统,把子系统对 ...

  9. windows程序设计 c语言,【教程】基于C语言的Windows编程入门

    (三) 窗口的创建与消息处理 原来那个程序是不是感觉有点简单?这里我们要制作一个复杂一点的Windows程序 - 显示一个空白窗口. 出于稳定性考量,Visual C++编译器中int.long等基本 ...

最新文章

  1. [转]安装和使用JD-Eclipse插件
  2. Quartz之入门实例
  3. Django 练习班级管理系统五 -- 查看老师列表
  4. Pandas重复数据的查看和去重
  5. 计算机专业简历推荐信范文,个人简历自我推荐信范文【三篇】
  6. Python版归并排序算法(附Python程序__name__属性用法演示视频)
  7. 【转】C#获取用户桌面等特殊系统路径
  8. 第二章 传送与交换作业
  9. 轻松搭建Windows8云平台开发环境
  10. node使用ffmpeg拼接音频
  11. intel AVX / AVX2指令学习资源
  12. FFMpeg的码率控制 - CBR or VBR
  13. imitate wechat - 5
  14. [Klipper从入门到放弃]香橙派zero2设置2.4g无线热点
  15. 计算机科学技术的想象作文600,关于科学的想象作文
  16. vuex中subscribe的使用
  17. 谈谈对Android音视频开发的探究
  18. Eclips 安装教程
  19. 小白的编程经验(二维数组迷宫游戏)
  20. 三菱FX系列PLC以太网连接kepwareopc软件

热门文章

  1. python代码进去docker容器内_python脚本监控docker容器
  2. seo全攻略_SaaS 企业推广获客全攻略(2):如何做好企业官网?
  3. python0b1011_1011 A+B 和 C (15分)Python参考答案
  4. linux 守护进程_网络工程师之linux守护进程
  5. php截取字符串几位数,php按照指定长度截取字符串的代码
  6. VMWare笔记-解决虚拟机能ping通主机能上网,但主机ping不通虚拟机(含思路)
  7. Spring Boot中Thymeleaf的初步使用
  8. Qt工作笔记-moc的认识
  9. Java基础入门笔记-多态
  10. Qt5.7+Opencv2.4.9人脸识别(二)人脸采集