Windows RPC 初探

开始

IDL(接口定义语言)文件:

import "oaidl.idl";
[
    uuid(fa63b4aa-cde1-461b-a201-54eb3ea1808b), //定义uuid
    version(1.0) //定义版本号
]

interface myrpc //定义rpc接口 interface
{
    void input(char* string);
}

ACF文件:

[
	implicit_handle(handle_t rpcBinding)
]

interface myrpc{

}

implicit_handle 表示是隐式句柄

image-20250921215508572

随后在Native Tools 内使用midl工具生成服务端和客户端所用的.c和.h文件

这里注意 如果要x64的RPC程序需要使用X64的Native Tools!!!

image-20250921215526737

myrpc.h为客户端与服务端公用

_c文件为Client所用

_s文件为Server所用

服务端

​ 服务端必须包含两个内存分配函数。这两个函数被服务端程序调用:midl_user_allocate和midl _user_free。这两个函数在服务端分配和释放内存,一般情况下midl _user_allocate和midl_user_free都会简单地封装C库函数malloc和free。

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include "../IDL/myrpc.h"

#define RPC_PORT L"12345"

void input(char * string) {
	printf("[RPC]%s\n", string);
	return;
}


int wmain(int argc, wchar_t* argv[])
{
	// 采用tcp协议,13521端口
	RpcServerUseProtseqEp((RPC_WSTR)L"ncacn_ip_tcp", RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
		(RPC_WSTR)RPC_PORT, NULL);
	// 注意:从Windows XP SP2开始,增强了安全性的要求,如果用RpcServerRegisterIf()注册接口,客户端调用时会出现
	// RpcExceptionCode() == 5,即Access Denied的错误,因此,必须用RpcServerRegisterIfEx带RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH标志
	// 允许客户端直接调用
	printf("[RPC]Server Running in port %ls\n", RPC_PORT);
	RpcServerRegisterIfEx(myrpc_v1_0_s_ifspec, NULL, NULL, RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH, 0, NULL);
	// 开始监听,本函数将一直阻塞
	RPC_STATUS result = RpcServerListen(1, 20, FALSE);


	return 0;
}


void __RPC_FAR* __RPC_USER midl_user_allocate(size_t len)
{
	return(malloc(len));
}
void __RPC_USER midl_user_free(void __RPC_FAR* ptr)
{
	free(ptr);
}

image-20250921222537530

客户端

#include <stdio.h>
#include "../IDL/myrpc.h"

int main() {
	RPC_WSTR pszStringBinding = NULL;

	RpcStringBindingCompose(
		NULL,
		(RPC_WSTR)L"ncacn_ip_tcp",
		(RPC_WSTR)L"127.0.0.1",
		(RPC_WSTR)L"12345",
		NULL,
		&pszStringBinding
	);

	// 绑定接口,这里要和 test.acf 的配置一致,那么就是test_Binding
	RpcBindingFromStringBinding(pszStringBinding, &rpcBinding);

	// 下面是调用服务端的函数了
	RpcTryExcept
	{
		while (1)
		{
			input(L"Hello World!");
			Sleep(1000);
		}

	}
		RpcExcept(1)
	{
		printf("RPC Exception %d\n", RpcExceptionCode());
		Sleep(2000);
	}
	RpcEndExcept


		// 释放资源
	RpcStringFree(&pszStringBinding);
	RpcBindingFree(&rpcBinding);

	return 0;
}





// 下面的函数是为了满足链接需要而写的,没有的话会出现链接错误
void __RPC_FAR* __RPC_USER midl_user_allocate(size_t len)
{
	return(malloc(len));
}
void __RPC_USER midl_user_free(void __RPC_FAR* ptr)
{
	free(ptr);
}

image-20250921223201311

由于宽字符串的问题 所以只显示第一个字符 已经成功建立RPC远程通信

image-20250921223238178

客户端和服务端都需要引入rpcrt4.lib

Credit:

https://bbs.kanxue.com/thread-262291.htm

https://blog.csdn.net/herojuice/article/details/81015325