如何在C#(winfrom)代码中调用C++风格导出的DLL?

问题描述 投票:0回答:1

DLL 以 C++ 风格导出,并添加了函数修饰符。动态库导出的接口名称如下:

实际函数名称:

RegPOC_CB

动态库中接口名称:

?RegPOC_CB@@YAHPAUPOC_CB_t@@@Z

RegPOC_CB
函数定义如下,接收存储回调函数的
POC_CB_t
结构体指针。

define EXPORT __declspec(dllexport)

typedef void (*CmdOutCallBack)(char* pAtStrCmd);
typedef void (*RADataCallBack)(const char *src , int len,int type) ;
typedef void (*RVDataCallBack)(const char *src , int len,int type, int width,int high,int rota);

typedef struct
{
    CmdOutCallBack cmdOutCB;
    RADataCallBack rADataCB;
    RVDataCallBack rVDataCB;
    CmdOutCallBack LOCB;
}POC_CB_t;

extern int EXPORT RegPOC_CB(POC_CB_t *pCbFunc);

我使用以下代码在C#中注册了回调函数,但是调试时回调函数实现中的所有打印都没有效果。单步调试时,显示调用

RegPOC_CB
函数成功,没有报错。你知道为什么吗?

public class Sender(int id)
{
    public int Id { get; set; } = id;
    
    [DllImport("say.dll", EntryPoint = "?RegPOC_CB@@YAHPAUPOC_CB_t@@@Z", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern int RegPOC_CB(ref POC_CB_t pCbFunc);
    // Using IntPtr has no effect either
    // public static extern int RegPOC_CB(IntPtr pCbFunc);

    // Define a C# delegate that matches the C++ callback function
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void CmdOutCallBack(IntPtr pAtStrCmd);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void RADataCallBack(IntPtr src, int len, int type);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void RVDataCallBack(IntPtr src, int len, int type, int width, int height, int rota);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void LOCallBack(IntPtr message);

    // POC_CB_t struct
    [StructLayout(LayoutKind.Sequential)]
    public struct POC_CB_t
    {
        public CmdOutCallBack cmdOutCB;
        public RADataCallBack rADataCB;
        public RVDataCallBack rVDataCB;
        public LOCallBack LOCB;
    }

    // Callback function implementation
    public void cmdOut(IntPtr pAtStrCmd)
    {
        System.Diagnostics.Debug.WriteLine("call cmdOut");
        var at = Marshal.PtrToStringAnsi(pAtStrCmd);
        if (string.IsNullOrWhiteSpace(at)) { return; }
        System.Diagnostics.Debug.WriteLine($"[{id}] cmdOut Callback: {at}");
    }

    public void rAData(IntPtr src, int len, int type)
    {
        System.Diagnostics.Debug.WriteLine("call rADataCB");
        byte[] buffer = new byte[len];
        Marshal.Copy(src, buffer, 0, len);
        var data = buffer.ToString();
        System.Diagnostics.Debug.WriteLine($"[{id}] Data: {data}, Length: {len}, Type: {type}");
    }

    public void rVData(IntPtr src, int len, int type, int width, int height, int rota)
    {
        System.Diagnostics.Debug.WriteLine("call RecvVideoData");
        byte[] buffer = new byte[len];
        Marshal.Copy(src, buffer, 0, len);
        var data = buffer.ToString();
        System.Diagnostics.Debug.WriteLine($"[{id}] Data: {data}, Length: {len}, Type: {type}, Width: {width}, Height: {height}, Rotation: {rota}");
    }

    public void LO(IntPtr message)
    {
        System.Diagnostics.Debug.WriteLine("call LogOut");
        var LogStr = Marshal.PtrToStringAnsi(message);
        if (string.IsNullOrWhiteSpace(LogStr)) { return; }
        System.Diagnostics.Debug.WriteLine($"[{id}] Log: {LogStr}");
    }

    // call RegPOC_CB
    public void RegisterCallbacks()
    {
        callbacks = new POC_CB_t
        {
            cmdOutCB = cmdOut,
            rADataCB = rAData,
            rVDataCB = rVData,
            LOCB = LO
        };

        RegPOC_CB(ref callbacks);
        // Using IntPtr has no effect either
        //IntPtr pCallbacks = Marshal.AllocHGlobal(Marshal.SizeOf(callbacks));
        //Marshal.StructureToPtr(callbacks, pCallbacks, false);
        //RegPOC_CB(pCallbacks);
        InitPoc(0);
    }
}

我尝试过使用

ref
IntPtr
将结构体指针传递到DLL中,但结果是一样的。

c# c++ dll
1个回答
0
投票

您似乎正在尝试将回调函数从 C# 应用程序注册到 C++ 动态链接库 (DLL)。 C++ DLL 导出一个名为“RegPOC_CB”的函数,该函数采用指向包含回调函数的结构的指针。 C# 代码定义回调函数并尝试使用“RegPOC_CB”函数注册它们。 您提供的 C++ 代码看起来大部分是正确的,但有一些潜在的问题可能会导致您遇到的问题。以下是一些有助于排除故障并可能解决问题的建议:

  1. 调用约定:确保在调用约定中使用 C# 代码与 C++ DLL 中使用的调用约定相匹配。在你的 在这种情况下,C# 和 C++ 代码都使用 CallingConvention.Cdecl, 哪个是对的。结构对齐:检查结构对齐 在 C# 代码中。 C++ 中的 POC_CB_t 结构应与 C++ 代码中使用的结构对齐。你用过 LayoutKind.Sequential,这是正确的选择。
  2. 内存管理:验证为 结构体和函数指针没有被释放 过早地。在您的 RegisterCallbacks 方法中,您正在分配 使用 new 的 POC_CB_t 结构的内存,但您注释掉了 使用 IntPtr 和 Marshal.AllocHGlobal 的代码。确保 结构体和函数指针的内存得到正确管理。
  3. 错误处理:检查返回的任何潜在错误 RegPOC_CB 函数。处理任何潜在的错误或 从函数调用返回值。
  4. 调试:使用日志记录或调试语句来验证 回调函数是从 C++ 端调用的。你已经 已经在您的中添加了 System.Diagnostics.Debug.WriteLine 语句 回调函数,这是一个很好的做法。
  5. 函数名称修饰:确保函数名称修饰 C# 代码中的函数修饰名与 C++ 中的函数修饰名相匹配 DLL。 C++ DLL 中的修饰名是 ?RegPOC_CB@@YAHPAUPOC_CB_t@@@Z,它应该与 EntryPoint 匹配 在 DllImport 属性中。
  6. 函数签名: 仔细检查函数签名 C# 中的回调函数与 POC_CB_t 中的函数指针匹配 结构。

通过仔细检查这些方面并可能进行调整,您也许能够解决问题。如果问题仍然存在,请考虑使用 Dependency Walker 等工具或调试器来检查 C++ DLL 中的导出函数及其签名。此外,捕获 RegPOC_CB 函数返回的任何错误代码或消息可以为问题提供有价值的见解。

© www.soinside.com 2019 - 2024. All rights reserved.