C#:Cdecl DllExport,参数中带有指向 class 实例的指针

C#: Cdecl DllExport with a pointer to class instance in the arguments

我正在考虑为名为 SmartDVB 的软件编写插件。它需要我导出以下函数(这是一个 C++ 示例):

BOOL RegisterAddOn(UINT iAddOnId, IAddOnInterfaces *pInt, AddOnSettings &settings) {
    /* ... */ 
    return TRUE;
}

其中IAddonInterfaces是一个抽象class(接口):

class IAddOnInterfaces {
public:
    virtual HRESULT AddSectionFilter(UINT uiAddOnId, UINT pid, BYTE *filter, BYTE *mask, BYTE length, DeviceSettings *pDev) = 0;
    virtual HRESULT RemoveSectionFilter(UINT uiAddOnId, UINT pid, DeviceSettings *pDev) = 0;
    virtual HRESULT AddFilter(UINT uiAddOnId, UINT pid, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT RemoveFilter(UINT uiAddOnId, UINT pid, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT GetChannel(UINT uiAddOnId, CHANNEL *chn, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT GetTransponder(UINT uiAddOnId, SATELLITE *sat, TRANSPONDER *t, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT SetTransponder(UINT uiAddOnId, SATELLITE sat, TRANSPONDER t, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT CreateOSDElement(UINT uiAddOnId,  OSDWindowType type, ULONG &ulId, OSDWindowInfo &info) = 0;
    virtual HRESULT ChangeChannel(UINT uiAddOnId,  CHANNEL &chn, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT RegisterOSDEvents(UINT iAddOnId, IAddOnOSDEvents *pEvents) = 0;
    virtual HRESULT RegisterMenuEvents(UINT iAddOnId, IAddOnMenuEvents *pEvents) = 0;
    virtual HRESULT RegisterChnEvents(UINT iAddOnId, IAddOnChnEvents *pEvents) = 0;
    virtual HRESULT DoDVBCmd(UINT iAddOnId, BYTE pCmd, UINT uiLen, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT DoDiseqc(UINT iAddOnId, CHANNEL &chn, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT RecordBusy(UINT iAddOnId, BOOL *bBusy, DeviceSettings *pDev=NULL) = 0;
    virtual HRESULT GetDeviceSettings(UINT iAddOnId, DeviceSettings &Dev) = 0;
    virtual HRESULT GetSignalStrength(UINT iAddOnId, LONG *lStrength, DeviceSettings *Dev) = 0;
    virtual HRESULT GetSignalQuality(UINT iAddOnId, LONG *lQuality, DeviceSettings *Dev) = 0;
};

AddOnSettings是一个结构:

typedef struct _AddOnSettings{
    HINSTANCE hInst; // SmartDVB application instance
    HWND hwnd;
    TCHAR name[256]; // addon name
    HMENU menu;      // addon popup menu
    WORD idmenustart;
} AddOnSettings;

在c#中,我将结构定义为

using HINSTANCE = System.IntPtr;
using HWND = System.IntPtr;
using HMENU = System.IntPtr;

/* ... */

public unsafe struct AddOnSettings{
    HINSTANCE hInst; // SmartDVB application instance
    HWND hwnd;
    [MarshalAs(UnmanagedType.LPArray, SizeConst = 256)]
    char[] name; // addon name
    HMENU menu;      // addon popup menu
    ushort idmenustart;
};

/* ... */

但是,我仍然需要以某种方式定义 class,以便它可以导出。

最后,我想要这样的东西:

    [DllExport("RegisterAddOn", CallingConvention = CallingConvention.Cdecl)]
    public static bool RegisterAddOn(uint iAddOnId, IAddOnInterfaces* pInt, ref AddOnSettings settings)
    {
        /* ... */
        return true;
    }

问题是,当我这样做时,出现以下错误:

Error   1   Cannot take the address of, get the size of, or declare a pointer to a managed type ('ChannelSwitchCS.IAddOnInterfaces')    D:\Work\coding\projects\buster\smartdvb\ChannelSwitchCS\ChannelSwitchCS\Class1.cs   102 28  ChannelSwitchCS

P.S。我不要求完整的解决方案,我只需要了解这个特殊情况,即如何(如果可能的话)在导出函数中有一个指向 class 实例的指针。所以,我确实意识到我将不得不转换 IAddOnInterfaces.

方法中使用的所有其他结构和 classes

当我不得不使用 PInvoke 并且需要将 类 的实例作为指针传递时,我为我将在 C++ 中使用的非托管代码编写了一个简单的包装器,它只接受原始类型和将它们安排成任何需要的类型。

在将指针传递给 类 的实例时,我使用了 IntPtr,一切都按照我的预期进行。