Windows RPC 必须使用 MIDL 编译器生成桩代码,配合 rpcrt4.dll 调用;直接手写序列化或调用底层 API 极易因类型、内存、绑定等问题崩溃。

Windows RPC 用什么 API?别碰 rpcrt4.dll 手写序列化
Windows 原生 RPC 不是 gRPC 那种现代框架,而是基于 rpcrt4.dll 的底层机制,必须配合 MIDL 编译器生成桩代码。直接调用 RpcBindingBind 或手写 RpcIfInqId 等函数,99% 的人会在类型序列化、句柄管理、内存生命周期上翻车。
正确路径是:写 IDL 接口定义 → 用 midl.exe 生成客户端/服务端存根(stub)→ 在 C++ 中调用生成的函数。MIDL 不只是语法检查器,它负责把结构体转成 NDR 流、处理指针嵌套、自动插入 [unique]/[ref] 语义——跳过这步等于裸写网络字节序解析。
-
midl.exe必须和目标平台匹配(x64 进程不能用 x86 生成的*_c.c) - IDL 中所有跨进程传递的结构体,字段顺序、对齐、指针修饰(
[string]、[size_is])必须显式声明,否则服务端收到的是乱码或崩溃 - 不要在 IDL 里用 C++ 类、STL 容器、虚函数——MIDL 只认 C 兼容类型
IDL 文件怎么写才不出错?重点看 endpoint 和 protocol_sequence
IDL 不是接口描述语言那么简单,它直接决定通信通道的建立方式。最常被忽略的是 endpoint 绑定配置,比如 TCP 端口或命名管道名,一旦和服务端不一致,RPC_S_SERVER_UNAVAILABLE 就会静默出现,连连接尝试都发不出去。
示例 IDL 片段(注意注释掉的坑):
立即学习“C++免费学习笔记(深入)”;
interface MyService
{
// ✅ 正确:显式指定协议和 endpoint
[endpoint("ncacn_ip_tcp:127.0.0.1[4040]")]
int GetData([in] long id, [out] wchar_t** result);
<pre class='brush:php;toolbar:false;'>// ❌ 错误:没 endpoint,MIDL 默认用 LRPC(本地进程间),跨机器必失败
// int GetRemoteData([in] long id);
// ❌ 错误:TCP 端口没加括号,MIDL 解析失败
// [endpoint("ncacn_ip_tcp:127.0.0.1:4040")]}
-
ncacn_ip_tcp对应 TCP;ncalrpc对应本地 LRPC(仅同机);ncacn_np是命名管道([\pipe\MyPipe]) - Windows Server 默认禁用远程 TCP RPC,需手动启用
RemoteRegistry服务并开放防火墙端口 - 使用
ncalrpc时,endpoint名称不能含反斜杠以外的特殊字符,且长度限制为 50 字符
客户端调用卡死或返回 RPC_S_INVALID_BINDING 怎么排查?
这个错误几乎总是绑定句柄(RPC_BINDING_HANDLE)没正确初始化导致的,不是网络不通。Windows RPC 不像 socket 那样“connect 失败就报错”,它可能在第一次调用时才真正尝试解析地址,此时才暴露问题。
关键检查点:
- 确认
RpcStringBindingCompose的参数顺序:协议、网络地址、endpoint、选项、绑定字符串指针——第 4 个参数(选项)传nullptr比传空字符串安全 - 调用
RpcBindingFromStringBinding后,必须检查返回值,不能假设成功;失败后要调用RpcStringFree清理字符串 - 服务端没启动时,客户端不会立即失败,而是阻塞在
GetData()调用里(默认 30 秒超时),可调用RpcMgmtSetComTimeout主动设短 - 32/64 位不匹配会导致
RPC_S_INVALID_ARG而非绑定错误,因为指针大小影响 NDR 解包
服务端如何避免崩溃?重点盯 RpcServerUseProtseqEp 和内存所有权
服务端崩溃八成出在两件事上:协议序列注册失败后继续调用 RpcServerRegisterIf,或者回调函数里释放了本该由 RPC 运行时管理的内存(比如 [out, string] wchar_t** 返回值)。
典型安全写法:
RPC_STATUS status;
status = RpcServerUseProtseqEp(L"ncacn_ip_tcp", 20, L"4040", nullptr);
if (status != RPC_S_OK) { /* 记录错误并退出 */ }
<p>status = RpcServerRegisterIf(MyService_v1_0_s_ifspec, nullptr, nullptr);
if (status != RPC_S_OK) { /<em> 同上 </em>/ }</p>-
RpcServerUseProtseqEp第二个参数是最大并发连接数,设 0 表示不限制,但 Windows 默认只允许 10 个 TCP 连接,需改注册表HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesRpcSsParametersMaxCalls - IDL 中标记
[out, string]的缓冲区,必须用RpcSmAllocate分配(不能用new或malloc),否则服务端释放时会触发堆损坏 - 服务端主循环必须调用
RpcServerListen,且不能在监听线程里做耗时操作——RPC 运行时不提供线程池,自己得 fork 或用 I/O 完成端口
IDL + MIDL + 绑定字符串 + 内存分配规则,这四点漏掉任何一环,RPC 就不是“通信问题”,而是“不可预测的崩溃”。没有捷径,也没有轻量封装能绕开这些约束。











