猿问

将复杂的数据结构从 C# 传递到本机 dll

我正在从 NetCore 应用程序调用用 C 编写的第三方库。问题是,为了使用这个库,我首先需要进行调用并配置一个复杂的结构,该结构稍后必须传递给所有后续调用。


void createCtx(modbus_t ** ctx)

{

    *ctx = modbus_new_tcp("192.168.1.175", 502);


    //configure the context here ....


    int res = modbus_connect(*ctx);

}


int pollData(modbus_t * ctx)

{

    //....

    modbus_read_bits(ctx, addr, 1, tab_rp_bits);

    //....

}

我的方法是在调用者应用程序 (C#) 上创建 modbus_t 对象,通过调用 createCtx 对其进行配置,然后定期将其传递给 pollData。我已经阅读了有关 StructLayout 的内容,但由于我不需要访问 modbusContext 对象中的数据,我只想为上下文保留一块内存,让 C# 忘记里面的内容。


这就是我想出的


static IntPtr modbusContext;


static class ModbusDriver

{

            [DllImport("modbusdriver",EntryPoint = "createCtx")]


            public static extern void CreateCtx(ref IntPtr modbusContext);


            [DllImport("modbusdriver",EntryPoint = "pollData")]


            public static extern uint PollData(IntPtr modbusContext)


        }



        static void Main(string[] args)

        {

            int ctxSize = ModbusDriver.GetCtxSize();


            modbusContext = Marshal.AllocHGlobal(80 * Marshal.SizeOf(typeof(byte))); //<--- 80 is the result of sizeof(modbus_t)

            ModbusDriver.CreateCtx(ref modbusContext);


            while(true)

            {

                ModbusDriver.PollData(modbusContext);

                Thread.Sleep(1000);

            }

        }

    }

这一切似乎都行得通,但感觉不太对劲,尤其是因为 modbus_t 结构相当复杂


struct modbus_t {

    /* Slave address */

    int slave;

    /* Socket or file descriptor */

    int s;

    int debug;

    int error_recovery;

    struct timeval response_timeout;

    struct timeval byte_timeout;

    struct timeval indication_timeout;

    const modbus_backend_t *backend;

    void *backend_data;

};


所以我的问题是,我的方法正确吗?具体来说, modbus_t 包含指针。我设法在 C# 中保留了 modbus_t 结构,它似乎可以工作,但是假设结构中包含的指针引用的内存在调用之间不会被破坏真的安全吗?感觉不对。


HUWWW
浏览 206回答 1
1回答

元芳怎么了

只要您不想修改数据,就可以安全地将数据包装为 void * 或 IntPtr。您通过 AllocHGlobal 分配数据,后者通过 LocalAlloc 从本地进程堆返回数据,最终调用 RtlAllocateHeap。对于 C#,该指针是一个黑盒子,永远不会写入或修改它。只要您不提前释放数据,一切都会好起来的。C 编程规则适用:您需要手动管理内存,并注意谁拥有数据以及谁负责删除它。只有当您尝试将该指针映射到部分尝试授予对某些字段的访问权限的托管类时,才会出现问题。然后您需要注意结构成员对齐方式与 C 头文件中的对齐方式相同,并且您需要为要跳过的数据获取正确的偏移量。然后,您可以将 IntPtr 转换为 C# 结构,作为带有不安全代码的指针,如果您得到正确的偏移量和对齐,它应该可以工作。如果 C++ 类是包含 STL 数据类型的头文件的一部分,情况就完全不同了。这些东西根本不是可包装的,因为成员对齐取决于您当前编译器的随附 STL 版本,这在私有成员字段之间强加了一个紧密的合同,可以在 C++/STL 版本之间更改。为此,您需要一个 C 包装器,它将辅助方法包装为普通的 C 方法,具有内部调用 C++ 方法的常用结构。托管 C++ 是一种相当过时的技术,不应再使用。总结一下:您目前的方法很好,并且会奏效。如果您想从字节 blob 访问修改数据,这将变得更加工作,但是一旦您知道如何在 C# 中声明仅包含原始类型(没有字符串、字典或指向托管堆结构的指针)的包装器结构,这也是可行的。
随时随地看视频慕课网APP
我要回答