平台调用——指针与函数的封送

本文介绍了在C#中如何调用C/C++的函数,强调了使用extern关键字的重要性。同时,讨论了如何在C#中处理包含函数指针的结构体,并给出了一个C#提供函数给C/C++调用的例子,展示了跨语言的函数调用和数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数的封送

在C#中调用C/C++函数时要注意:

在C/C++导出函数时,在导出函数前要加上extern关键字,不然C#中调用时会找不到。

在C/C++中,导出函数

extern __declspec(dllexport) void Add(int a, int b);

函数库名Add.dll
在C#中,需要将函数声明为:

[DllImport("Add.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Add(int a, int b);

函数指针在C/C++与C#中的调用

假设在C/C++中,定义了如下函数指针:

typedef int32 (*PServiceDataRead_f)(void *pContext, unsigned char *pData, 
                                    int *pDataLen, void *pArg);

要在C#中调用这个结构,则需将使用委托,重新定义:

public delegate int PServiceDataRead_f(IntPtr pContext, IntPtr pData,
                                       IntPtr pDataLen, IntPtr pArg);

一个例子:

在C中定义了一个函数,初始化了一个结构,结构中包含一个函数指针。

//service.dll

typedef int (*PServiceDataRead_f)(int serviceId, unsigned char* pRecv);
typedef struct tagService_t
{
     int serviceId;
     PServiceDataRead_f PServiceDataReadFun;
}Service_t

//export funtion
export __declspec(dllexport) Service_t* InitService();  //return Service_t pointer.


InitService函数会初始化一个service结构体的指针出来,service_t结构体包含了一个int变量与函数指针PServiceDataRead_f。

当需要在C#中引用这个Service_t结构时,需要如下定义:

public class Service
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]    // 调用约定
    public delegate int PServiceDataRead_f(int serviceId, Byte[] pRecv);

    public struct Service_t
    {
        int serviceId;
        IntPtr PServiceDataReadFun;
    }

    public static PServiceDataRead_f PServiceDataReadFun;
 
    [DllImport("service.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr InitService();

    // Call 
    static int Main()
    {
        IntPtr Service = InitService(); // 指针无法直接在C#中使用,需要先将其转到IntPtr中
        Byte[] recvData = new Byte[1024];
        Service_t service = new Service_t(); // 初始化结构体
        
        //将Service 转换成Service_t结构体
        Marshal.PtrToStructure(Service, service);

        PServiceDataReadFun 
        = (PServiceDataRead_f)Marshal.GetDelegateForFunctionPointer(service.PServiceDataReadFun, typeof(PServiceDataRead_f));
     
        //指针在C#中并不能直接使用,一般得配合unsafe关键字一起使用。
        /* 使用指针可以直接转换将Service 转换成指针后使用函数。
        unsafe
        {
             Service_t *pService = (Service_t*)Service.ToPointer();
             PServiceDataReadFun 
             = (PServiceDataRead_f)Marshal.GetDelegateForFunctionPointer(pService->PServiceDataReadFun,  typeof(PServiceDataRead_f));
        }
        */
        
        int ret = PServiceDataReadFun(service.serviceId, recvData);
        if(ret < 0)
        {
            Console.WriteLine("PServiceDataReadFun error, ret {0}", ret);
        }

        return 0;
    }
}


第二个例子:

第一个例子是在C#中调用C/C++函数,而第二例子则是在C#中提供函数并注册到C/C++函数中。即C/C++调用C#函数。

假设有一个服务,使用C编写,但是需要在C#中进行调用,并且需要在C#中处理数据。
在C中定义了如下:

//ais.dll
typedef struct tagAisgwMsg_t
{
    ...
}AisgwMsg_t;

typedef int (__stdcall *AisgwMessageReport)(AisgwMsg_t *pMsg);  // 注意这里的调用约定__stdcall

typedef struct AisgwServiceParam
{
    unsigned int net;
    AisgwMessageReport messageReport;
}AisgwServiceParam_t;

extern int32_t AisgwServiceInit(AisgwServiceParam_t *pParam);


C#:
public class Ais
{
        public struct AisgwMsg_t
        {
                ...
        }

        public delegate int AisgwMessageRepeort(ref AisgwMsg_t pMsg); 

        public struct AisgwServiceParam
        {
                public uint net;
                AisgwMessageRepeort messageReport;
        }

        [DllImport("ais.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern Int32 AisgwServiceInit(ref AisgwServiceParam pParam);

        public static Int32 AisgwMessageReport(ref AisGateWay.AisgwMsg pMsg)
        {
                ...
        }

        static int Main()
        {
                AisgwServiceParam aisgwServiceParam = new AisgwServiceParam();
                aisgwServiceParam.net = 0; // use default
                aisgwServiceParam.messageReport = AisgwMessageReport;  //注册

                ret = AisgwServiceInit(ref aisgwServiceParam);  // 成功之后,ais服务将会调用注册函数
                if(ret < 0)
                {
                        Console.WriteLine("AisgwServiceInit fail, ret {0}", ret);
                }

                return 0;
        }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值