# XYParser 的 C# 调用说明 ## 1. 项目现状 当前工程已经是一个 **C++ 原生 DLL**,不是 .NET DLL。 C# 调用它的推荐方式是使用 **P/Invoke**。 相关接口定义见: - [XYParserApi.h](file:///c:/Users/xyyl666/Desktop/XYParser/XYParser/XYParserApi.h#L12-L40) - [XYParserApi.cpp](file:///c:/Users/xyyl666/Desktop/XYParser/XYParser/XYParserApi.cpp#L59-L154) 当前主要导出函数: - `XYParser_CreateParser` - `XYParser_DestroyParser` - `XYParser_SetAdcParams` - `XYParser_SetBypassChecksum` - `XYParser_Feed` - `XYParser_GetLastError` ## 2. 使用方式 ### 2.1 先编译 DLL 在 Visual Studio 中打开: - [XYParser.sln](file:///c:/Users/xyyl666/Desktop/XYParser/XYParser/XYParser.sln) 建议编译配置: - `Release | x64` 编译后得到: - `XYParser.dll` 运行 C# 程序时,需要把 `XYParser.dll` 放到: - C# 可执行程序同目录 - 或系统可搜索到的 DLL 路径中 ## 3. C# P/Invoke 声明 ```csharp using System; using System.Runtime.InteropServices; internal static class NativeMethods { private const string DllName = "XYParser.dll"; [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr XYParser_CreateParser(byte channel_count); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void XYParser_DestroyParser(IntPtr handle); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void XYParser_SetAdcParams(IntPtr handle, double vref, double gain); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void XYParser_SetBypassChecksum(IntPtr handle, int bypass); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern int XYParser_Feed( IntPtr handle, byte[] data, UIntPtr size, [Out] XYParserFrameSummary[] outSummaries, int maxSummaries); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr XYParser_GetLastError(IntPtr handle); } ``` ## 4. 结构体定义 C++ 中的结构体定义见: - [XYParserFrameSummary](file:///c:/Users/xyyl666/Desktop/XYParser/XYParser/XYParserApi.h#L21-L29) 对应的 C# 写法: ```csharp using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] public struct XYParserFrameSummary { public uint frame_index; public byte channel_count; public byte battery; public byte sample_count; // C++: double channel_values_uv[5][64] // C#: 按一维数组接收,总长度 = 5 * 64 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5 * 64)] public double[] channel_values_uv; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public byte[] trigger_types; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public byte[] trigger_indices; } ``` ## 5. 调用示例 ```csharp using System; using System.IO; using System.Runtime.InteropServices; class Program { static void Main() { IntPtr handle = NativeMethods.XYParser_CreateParser(8); if (handle == IntPtr.Zero) { throw new Exception("创建解析器失败"); } try { NativeMethods.XYParser_SetAdcParams(handle, 4.5, 6.0); NativeMethods.XYParser_SetBypassChecksum(handle, 1); byte[] input = File.ReadAllBytes("test.bin"); XYParserFrameSummary[] summaries = new XYParserFrameSummary[8]; for (int i = 0; i < summaries.Length; i++) { summaries[i].channel_values_uv = new double[5 * 64]; summaries[i].trigger_types = new byte[5]; summaries[i].trigger_indices = new byte[5]; } int count = NativeMethods.XYParser_Feed( handle, input, (UIntPtr)input.Length, summaries, summaries.Length); string lastError = Marshal.PtrToStringAnsi( NativeMethods.XYParser_GetLastError(handle)) ?? ""; Console.WriteLine($"解析出的帧数: {count}"); Console.WriteLine($"最后错误: {lastError}"); if (count > 0) { double firstValue = summaries[0].channel_values_uv[0]; Console.WriteLine($"第一帧第一个采样点第一个通道值: {firstValue}"); } } finally { NativeMethods.XYParser_DestroyParser(handle); } } } ``` ## 6. 二维数组取值说明 C++ 中: ```cpp double channel_values_uv[5][64]; ``` C# 中按一维数组接收后,索引方式为: ```csharp double value = summary.channel_values_uv[sampleIndex * 64 + channelIndex]; ``` 例如: - 第 `0` 个采样点,第 `0` 个通道:`[0 * 64 + 0]` - 第 `1` 个采样点,第 `3` 个通道:`[1 * 64 + 3]` ## 7. 注意事项 - `CallingConvention` 要使用 `Cdecl` - `XYParser_GetLastError` 返回的是 `const char*`,需用 `Marshal.PtrToStringAnsi` - C# 程序位数必须和 DLL 一致 - 你当前更适合使用 `x64` - `channel_count` 当前只支持 `8` 和 `64` ## 8. 推荐封装方式 如果后续要长期在 C# 中使用,建议再包一层托管类,例如: ```csharp public sealed class XYParser : IDisposable { private IntPtr _handle; public XYParser(byte channelCount) { _handle = NativeMethods.XYParser_CreateParser(channelCount); if (_handle == IntPtr.Zero) throw new InvalidOperationException("创建解析器失败"); } public void SetAdcParams(double vref, double gain) { NativeMethods.XYParser_SetAdcParams(_handle, vref, gain); } public void SetBypassChecksum(bool enabled) { NativeMethods.XYParser_SetBypassChecksum(_handle, enabled ? 1 : 0); } public void Dispose() { if (_handle != IntPtr.Zero) { NativeMethods.XYParser_DestroyParser(_handle); _handle = IntPtr.Zero; } } } ``` ## 9. 总结 - 当前工程已经能生成原生 `XYParser.dll` - C# 不需要重写解析逻辑,直接用 `DllImport` 即可调用 - 最关键的是函数声明匹配、结构体布局匹配、位数一致