告别自制 DLL:用 memcpy 直连 WinAPI 传递结构体与指针
MQL5 功能总有不够用的时候:你想连数据库、用通信套接字、调操作系统 API,却被两件事卡住——怎么把复杂结构传给 API 函数?怎么处理 API 返回的指针?很多人的第一反应是写个中间 DLL。本文证明大可不必:用系统自带的 msvcrt.dll 里的 memcpy,配合“内存即字节数组”的思想,就能在纯 MQL5 里完成结构传递与指针读取,彻底甩掉自制 DLL。
一、核心思想:内存就是字节序列
任何变量(含结构、数组)都存在内存里,地址是一个 int 值。只要知道地址和字节数,就能把一段内存当成任意类型解读——这叫“内存即字节序列”。C 库 msvcrt.dll 的 memcpy(dst, src, cnt) 能把从 src 地址开始的 cnt 字节复制到 dst 地址,并返回 dst 地址。这意味着:不管 src 是 char、double、数组还是结构,只要知道地址和大小,就能在内存区域间自由搬运。
二、用 memcpy 取变量地址
memcpy 返回接收缓冲的地址,这个特性可用来“取任意变量的地址”:把同一变量同时作为源和接收、cnt 传 0(不实际复制),返回值就是它的地址。文中示范取 double 变量和 short 数组首地址——拿到地址后,就能把它当作参数传给需要指针的 API 函数,或作为 memcpy 的源/目标。
// 用 memcpy 取变量地址(节选)
#import "msvcrt.dll"
int memcpy(short &dst[], short &src[], int cnt);
int memcpy(double &dst, double &src, int cnt);
#import
void OnStart()
{
short src[5];
int adr = memcpy(src, src, 0); // 取数组首地址
double var;
adr = memcpy(var, var, 0); // 取变量地址
}
三、复制数组与传递结构给 API
复制数组:先 ArrayResize 分配目标,cnt 用 sizeof(类型)*ArraySize 算字节数,再 memcpy。传结构给 API 是更实用的招:MQL5 不能直接传结构指针,但结构可以整体复制进一个同尺寸的 uchar 数组(如 struct str2 { uchar c[28]; }),再把数组传给 API。文中以套接字 connect 为例:构造 sockaddr_in 结构 → 整体赋给同尺寸 ref_sockaddr_in 字节数组 → 把数组作为 connect 的参数提交,结构直达 API,无需 DLL。
// 把结构转字节数组再传给 API(节选)
struct sockaddr_in { short sin_family; u_short sin_port; in_addr sin_addr; char sin_zero[8]; };
struct ref_sockaddr_in { uchar c[2+2+8+8]; }; // 同尺寸字节数组
// 填充 addrin 后:
ref_sockaddr_in ref = addrin; // 结构 -> 字节数组
res = connect(asock, ref.c, sizeof(addrin));
四、处理 API 返回的指针:以内存映射文件为例
API 常返回指向结构/数组的指针,MQL5 不会自动拆。办法是用 memcpy 从指针地址复制指定字节数到本地数组。文中用内存映射文件(MMF)示范:OpenFileMappingW 拿到句柄,MapViewOfFile 返回内存视图指针,再 memcpy(view, src, 10) 读前 10 字节、memcpy(view, num, 4) 写 4 字节 int——读写共享内存全程纯 MQL5,不用任何 DLL。
五、从 MySQL 取回数据:指针的指针
更难的 case 是 API 返回“指针的指针”,比如 mysql_fetch_row 返回字符串数组(每个字段又是一个数组)。文中用 mysql_num_rows / mysql_num_fields / mysql_fetch_lengths 先拿到行数、字段数、各字段长度,再用 memcpy 按长度从返回指针逐个字段拷回本地 uchar 数组。字段可能是二进制,所以不能用 string 数组硬套——这正是“按字节长度精确搬运”的价值。
- 取地址:memcpy(var, var, 0) 借返回值拿变量/数组地址
- 传结构:结构整体赋给同尺寸 uchar 数组,再把数组当指针传 API
- 读指针:memcpy(本地数组, 返回指针, 字节数) 精确搬回数据
- 算字节:始终用 sizeof / ArraySize*元素大小,别手填数字
六、小结
摆脱自制 DLL 的关键在于“内存即字节序列”+ msvcrt 的 memcpy:取地址用 memcpy(var,var,0)、传结构用“结构→同尺寸字节数组”、读 API 返回的指针用 memcpy 按长度搬回。它让你在纯 MQL5 里直连 WinAPI、套接字、共享内存、数据库,省掉维护 DLL 的编译/部署/兼容负担。代价是字节级操作容错低——尺寸算错就崩,所以每个 sizeof 都要核对、每个字段布局都要对齐文档。把它当“高级系统编程”的钥匙,而不是随手乱用的捷径。