同一系统中如何通过命名管道将C语言结构体数据传递到C#应用程序
命名管道传输C结构体到C#实现方案
核心要求为两端保持结构体内存布局、字段类型、字节序完全一致,直接传输二进制字节流即可完成传递。
C端实现修改
原有代码基于字节模式的命名管道,只需直接写入结构体的二进制内存内容即可:
#include <windows.h> #include <stdio.h> HANDLE fileHandle; // 定义待传输的结构体 struct data { unsigned int count; unsigned int total; }; void ReadString(char* output) { ULONG read = 0; int index = 0; do { ReadFile(fileHandle, output + index++, 1, &read, NULL); } while (read > 0 && *(output + index - 1) != 0); } int main() { // 打开管道 fileHandle = CreateFileW(TEXT("\\\\.\\pipe\\my-very-cool-pipe-example"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { printf("打开管道失败"); return 1; } // 读取C#端发送的字符串(原有逻辑保留) char* buffer = new char[100]; memset(buffer, 0, 100); ReadString(buffer); printf("read from pipe server: %s\r\n", buffer); // 发送字符串到C#端(原有逻辑保留) const char* msg = "hello from C\r\n"; DWORD written = 0; WriteFile(fileHandle, msg, strlen(msg), &written, NULL); // 新增:发送结构体数据 struct data sendData = {123, 4567}; WriteFile(fileHandle, &sendData, sizeof(struct data), &written, NULL); CloseHandle(fileHandle); return 0; }
C#端实现修改
原有代码使用StreamReader/StreamWriter处理文本数据,传输二进制结构体需直接操作管道字节流,同时定义内存布局匹配的C#结构体:
using System; using System.IO.Pipes; using System.Runtime.InteropServices; using System.Threading.Tasks; namespace PipeServerCsharp { // 定义和C端内存布局完全一致的结构体 [StructLayout(LayoutKind.Sequential)] public struct Data { public uint count; // 对应C端unsigned int public uint total; } class Program { static void Main(string[] args) { var instances = new Task[5]; for (int i = 0; i < instances.Length; i++) { instances[i] = Task.Run(() => { using var namedPipeServer = new NamedPipeServerStream( "my-very-cool-pipe-example", PipeDirection.InOut, 5, PipeTransmissionMode.Byte); namedPipeServer.WaitForConnection(); // 发送字符串到C端(原有逻辑调整,直接写字节避免编码问题) byte[] strBuf = System.Text.Encoding.UTF8.GetBytes("Hello from c#\0"); namedPipeServer.Write(strBuf, 0, strBuf.Length); namedPipeServer.Flush(); namedPipeServer.WaitForPipeDrain(); // 读取C端发送的字符串(原有逻辑) byte[] readBuf = new byte[1024]; int readLen = namedPipeServer.Read(readBuf, 0, readBuf.Length); string cStr = System.Text.Encoding.UTF8.GetString(readBuf, 0, readLen).TrimEnd('\r', '\n'); Console.WriteLine($"read from pipe client: {cStr}"); // 新增:读取结构体数据 byte[] structBuf = new byte[Marshal.SizeOf<Data>()]; readLen = namedPipeServer.Read(structBuf, 0, structBuf.Length); // 字节数组转结构体(非安全代码版本,也可使用Marshal实现) Data receivedData; unsafe { fixed (byte* p = structBuf) { receivedData = *(Data*)p; } } Console.WriteLine($"收到结构体:count={receivedData.count}, total={receivedData.total}"); }); } Task.WaitAll(instances); } } }
如果不想使用非安全代码,可替换为以下转换逻辑:
GCHandle handle = GCHandle.Alloc(structBuf, GCHandleType.Pinned); try { receivedData = (Data)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(Data)); } finally { handle.Free(); }
注意事项
- 禁止使用
StreamReader/StreamWriter处理二进制数据,会触发隐式编码转换导致字节错乱 - 两端结构体字段顺序、类型必须严格对应,Windows平台下C的
unsigned int为32位无符号整数,对应C#的uint,不要误写为int - Windows平台下C和C#默认均为小端字节序,无需额外转换,跨平台场景需统一字节序
- 如果C端使用
#pragma pack修改了结构体对齐参数,C#端需要在StructLayout中指定对应Pack参数,例如[StructLayout(LayoutKind.Sequential, Pack = 1)]对应1字节对齐
内容的提问来源于stack exchange,提问作者Ganesh Kamath - 'Code Frenzy'
相关产品推荐
相关产品推荐

