将代码 c++ 转换为 c#,读取到类

Convert code c++ to c# , read to a class

本文关键字:读取 代码 c++ 转换      更新时间:2023-10-16

我有一个类

class DataqFmt 
{
public:
    unsigned short  SR_number;
    unsigned short  SR_numerator;
    unsigned char   offset;
    unsigned char   nbytes;
    short       hdr_bytes;
    unsigned long   dat_bytes;
        char        dummy[1144];
};

代码 C++ :

afl = fopen(path, "rb");
DataqFmt dataqstr;
fread ((char*) &dataqstr, sizeof (dataqstr), 1, afl);

如何将此代码 c++ 转换为 c# .请帮助我

我将假设编写此文件的C++应用程序是为 Win32 x86 编译并在 Win32 x86 上运行的。这使我能够对DataqFmt安排的方式做出假设。如果这些假设是错误的,您可以使用StructLayout和相关属性的各种选项(如 MarshalAsFieldOffset )来调整它们。

首先,我们将使用与 DataqFmt 布局相同的 C# 结构:

[StructLayout(LayoutKind.Sequential)]
struct DataqFmtCs
{
    public ushort SR_number; // assuming the C++ type unsigned short is 2 bytes
    public ushort SR_numerator;
    public byte offset; // assuming the C++ type unsigned char was an unsigned and 1 byte
    public byte nbytes;
    public short hdr_bytes; // assuming the C++ type short was 2 bytes
    public uint dat_bytes; // assuming the C++ type unsigned long was 4 bytes
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1144)]
    public sbyte[] dummy; // assuming the C++ type char was signed and 1 byte
}

有了这种结构,我们现在需要执行文件 I/O 来获取原始字节。

int dataqfmtSize = Marshal.SizeOf(typeof(DataqFmtCs));
var buffer = new byte[dataqfmtSize];
using (var fs = File.OpenRead("path\to\the\file")) {
    int bytesRead = fs.Read(buffer, 0, buffer.Length);
    if (bytesRead != buffer.Length) {
        // handle this
    }
    DataqFmtCs someThing = GetDataqFmtFromBuffer(buffer);
}

最后,我们有转换例程GetDataqFmtFromBuffer

private static DataqFmtCs GetDataqFmtFromBuffer(byte[] buffer) {
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    try {
        return (DataFmtCs)Marshal.PtrToStructure(
            handle.AddrOfPinnedObject(),
            typeof(DataFmtCs));
    } finally {
        handle.Free();
    }
}

Marshal.PtrToStrucutre正在做绝大多数的转换工作。代码的其余部分只是定义结构并执行基本的 .NET 文件 I/O。

另一种选择(如果您使用的是与平台无关的文档齐全的协议,这通常是一个很好的解决方案,因为您通常需要执行字节序转换)是编写一个反序列化例程,该例程使用 BinaryReaderStream上运行。我把这个留给你做一个练习。

如果您使用的是Visual Studio,则还可以导入C++代码并将其编译为CLR库,以供任何其他.net程序集引用。 如果您需要C++代码快速运行,这尤其是一个好主意。

如果你愿意承认 fread() C++本质上是脆弱且不可移植的,那么最好编写一个抽象层来读取(和写入)代码数据类型,例如,这样的格式很有用:

// returns true on success, false on EOF
public static bool Read(Stream stm, out int val)
{ /* ... */ }
public static bool Read(Stream stm, out short val)
{ /* ... */ }

然后,您可以将代码编写为如下所示:

if (!Read(stm, out _someMember))
    throw SomeException(); // or return a fail code
if (!Read(stm, out _someOtherMember))
    throw SomeException();

或者在我的一些代码的情况下,我编写了一个方法,该方法给定一个对象、一个流和一个字段或属性的名称,读取适当的大小值并设置成员(如果找不到它,则引发异常),然后是一个类似的方法,它适用于一个对象和一个名称集合(或者实际上是可变数量的字符串参数, 所以初始化可以看起来像这样:

    public static TTHorizontalHeader FromStream(Stream stm)
    {
        TTHorizontalHeader header = new TTHorizontalHeader();
        if (!Reader.ReadType(stm, header,
            "TableVersion", "Ascender", "Descender", "LineGap", "AdvanceWidthMax", "MinLeftSideBearing",
            "MinRightSideBearing", "XMaxExtent", "CaretSlopeRise", "CaretSlopeRun", "CaretOffset", "Reserved0",
            "Reserved1", "Reserved2", "Reserved3", "MetricDataFormat", "NumberOfHMetrics"))
            return null;
        return header;
    }

但是你说,"C++不会跑得更快,因为它只做一个fread()?我回答说:"Endianess、数据类型大小、结构填充和对象布局的变化足以让我们不以性能为幌子继续传播相同的脆弱代码。 无论如何,任何像样的缓冲流都只会进行一次实际读取,并且读取工作是由公正的机器人完成的。 除了 I/O 的成本,无论是否缓冲,都将使反射和字符串迭代的成本相形见绌。