首页 > 解决方案 > 我应该如何将这个 C 函数指针结构移植到 C# .NET 实现

问题描述

我正在将通常用 C/C++ 为 WIN32 编写的引导加载程序主机库移植到 C# 类库(而不是必须与原始库使用互操作,希望使其更易于使用和调试)。作为对 OOP 不太熟悉的人,我试图弄清楚如何将库的这个特定功能实现到 C# .NET(VS2019,.NET Standard 2.0)

在原始 C 库中,我们在库源代码的头文件中有以下结构:

typedef struct
{
    /* Function used to open the communications connection */
    int (*OpenConnection)(void);
    /* Function used to close the communications connection */
    int (*CloseConnection)(void);
    /* Function used to read data over the communications connection */
    int (*ReadData)(uint8_t*, int);
    /* Function used to write data over the communications connection */
    int (*WriteData)(uint8_t*, int);
    /* Value used to specify the maximum number of bytes that can be transfered at a time */
    unsigned int MaxTransferSize;
} CommunicationsData;

这由内部库函数调用,如下所示:

static CommunicationsData* g_comm;

int Bootloader_TransferData(uint8_t* inBuf, int inSize, uint8_t* outBuf, int outSize)
{
    int err = g_comm->WriteData(inBuf, inSize);

    if (CYRET_SUCCESS == err)
        err = g_comm->ReadData(outBuf, outSize);

    if (CYRET_SUCCESS != err)
        err |= CYRET_ERR_COMM_MASK;

    return err;
}

这个想法是库本身与通信协议无关,使用该库的应用程序是您编写 OpenConnection()/CloseConnection()/ReadData()/WriteData() 实现并为 MaxTransferSize 提供值的地方。在使用具有互操作性的原始 C 库的 C# 应用程序中,它是这样完成的:

class Bootloader_Utils{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int OpenConnection();
    
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int CloseConnection();
    
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int ReadData(IntPtr buffer, int size);
    
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int WriteData(IntPtr buffer, int size);
    
    /// <summary>
    /// Structure used to pass communication data down to the unmanged native C code
    /// that handles the bootloading operations.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct CommunicationsData
    {
        /// <summary>
        /// Function used to open the communications connection
        /// </summary>
        public OpenConnection OpenConnection;
        /// <summary>
        /// Function used to close the communications connection
        /// </summary>
        public CloseConnection CloseConnection;
        /// <summary>
        /// Function used to read data over the communications connection
        /// </summary>
        public ReadData ReadData;
        /// <summary>
        /// Function used to write data over the communications connection
        /// </summary>
        public WriteData WriteData;
        /// <summary>
        /// Value used to specify the maximum number of bytes that can be transferred at a time
        /// </summary>
        public uint MaxTransferSize;
    };
}

然后您将在应用程序代码中创建一个新的 CommunicationsData 对象并分配方法:

Bootload_Utils.CommunicationsData comm_data = new Bootload_Utils.CommunicationsData();
    
comm_data.OpenConnection = OpenConnection;
comm_data.CloseConnection = CloseConnection;
comm_data.ReadData = ReadData;
comm_data.WriteData = WriteData;
comm_data.MaxTransferSize = 64;

然后在 C# 应用程序中定义方法,如 OpenConnection() 的示例:

public int OpenConnection()
{
    int status = (int)ReturnCodes.CYRET_SUCCESS;

    if (ConnectionStatus == false)
    {
        try
        {
            serialPort.Open();
            ConnectionStatus = true;
        }
        catch (Exception exc)
        {
            ConnectionStatus = false;
            SetText(tb_StatusLog, " Error in opening serial port: " + exc.Message + "\r\n");

            serialPort.Close();
        }
    }
    return status;
}

我应该如何在 C# 中复制这种行为?我的想法是这样的:

public class CommunicationData
{
    delegate int OpenConnection();
    delegate int CloseConnection();
    delegate int ReadData(ref byte[] dataBuf, int numBytes);
    delegate int WriteData(ref byte[] dataBuf, int numBytes);
    int MaxTransferSize { get; set; }
}

然后我可以在库中创建这个类的一个新实例并调用该方法:

CommunicationData g_Comm = new CommunicationData();
int err = g_Comm.OpenConnection();

但这并不完全正确,因为它仍然需要 OpenConnection() 的定义,我希望将其放在应用程序中,而不是库中。

我在正确的轨道上吗?如何在 .NET 类库中复制此功能?

标签: c#c.netstructdelegates

解决方案


如果OpenConnection要在您编写时由应用程序提供,则使用delegate可能是正确的方法。然后您需要在应用程序中定义这些方法并填充结构。

或者,CommunicationData可以使interface您的应用程序或其他一些类需要实现,然后将其传递库函数。或者也许将一个库包装在一个类中(g_comm成为一个字段)并将接口仅传递给它的构造函数。


推荐阅读