Files
XYParser/CSharp调用说明.md
2026-06-06 10:07:53 +08:00

6.2 KiB

XYParser 的 C# 调用说明

1. 项目现状

当前工程已经是一个 C++ 原生 DLL,不是 .NET DLL。
C# 调用它的推荐方式是使用 P/Invoke

相关接口定义见:

当前主要导出函数:

  • XYParser_CreateParser
  • XYParser_DestroyParser
  • XYParser_SetAdcParams
  • XYParser_SetBypassChecksum
  • XYParser_Feed
  • XYParser_GetLastError

2. 使用方式

2.1 先编译 DLL

在 Visual Studio 中打开:

建议编译配置:

  • Release | x64

编译后得到:

  • XYParser.dll

运行 C# 程序时,需要把 XYParser.dll 放到:

  • C# 可执行程序同目录
  • 或系统可搜索到的 DLL 路径中

3. C# P/Invoke 声明

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++ 中的结构体定义见:

对应的 C# 写法:

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. 调用示例

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++ 中:

double channel_values_uv[5][64];

C# 中按一维数组接收后,索引方式为:

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 当前只支持 864

8. 推荐封装方式

如果后续要长期在 C# 中使用,建议再包一层托管类,例如:

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 即可调用
  • 最关键的是函数声明匹配、结构体布局匹配、位数一致