600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > C/C++:Windows编程—Windows RPC 传递自定义数据类型 自定义数据类型数组 指针数组

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

时间:2024-01-20 05:11:31

相关推荐

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

前言

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

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

/en-us/windows/desktop/Midl/string 字符串的使用

/en-us/windows/desktop/Midl/midl-arrays 数组的使用

/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文件打包下载地址 这里下载

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。