• C# PInvoke 方式调用 C/C++ 库 简单例子


    本文意在给出一个可用的 C# 调用 C/C++ lib 的实例。

    C/C++ lib

    C/C++ header

    #ifndef DPS_H
    #define DPS_H
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #if (defined _WIN32) || (defined _WINDOWS) || (defined WIN32)
    #ifdef MM_EXPORTS
    #define MMAPIEXP __declspec(dllexport)
    #else
    #define MMAPIEXP __declspec(dllimport)
    #endif
    #define MMAPI __cdecl
    #else
    #define MMAPIEXP
    #define MMAPI
    #endif
    
        // 定义一个回调
        typedef int(MMAPI *MMCallBack)(int handle, char* UserData);
    
        // 一个结构体
        typedef struct
        {
            int a;
            int b;
            char c;
            char* d;
        }DataInfo;
    
         // API 函数指针组成的结构体
        typedef struct
        {
            int (MMAPI* func01)(int a, int b);
            void (MMAPI* func02)(int c, DataInfo* d, MMCallBack cb);
        }MMAPIINFO;
    
          // 定义一个获取 API的函数,所有 API 通过该函数获取,注意,MMAPIEXP  用于指定WIN下导出函数名称
        MMAPIEXP  void  MMAPI GetAPI(MMAPIINFO* api);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    CPP 实现

    
    #include <cstddef>
    #include <cstring>
    #include "dps.h"
    
    // func01 函数实现
    int func01(int a, int b){
        return a + b;
    }
    
    // func01 函数实现
    void func02(int c, DataInfo* d, MMCallBack cb){
        cb(c,d->d);
    }
    
    // 定义获取 API 的函数
    void GetAPI(MMAPIINFO* api){
        if (api == NULL)
              return;
        memset(api, 0x00, sizeof(MMAPIINFO));
        api->func01 = func01;
        api->func02 = func02;
    }
    

    上述文件编译成为dll/lib. 对于C#而言,要调用的是dll. 上述文件可以生成为 dps.dll.
    注:名称无实际意义, 该代码自说明。

    C# 调用

    using System;
    using System.Runtime.InteropServices;
    
    namespace loadLib
    {
        class Program
        {
            static void Main(string[] args)
            {
                // 实例化一个 GetApi 类
                GetApi ss = new GetApi();
    
                Console.WriteLine(ss.Ffunc01(1,2));
    
                GetApi.DataInfo data = new GetApi.DataInfo
                {
                    a = 1,
                    b = 2,
                    c = 'x'
                };
                string str1 = "adbcd";
                data.d = str1;
    
                ss.Ffunc02(1024,ref data);
            }
        }
    
        class GetApi
        {
            // 特性定义了 MMAPIINFO 函数指针结构体的序列化方式和字符集
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public struct MMAPIINFO
            {
                public IntPtr func01;
                public IntPtr func02;
            }
    
            // 特性定义了 DataInfo 结构体的序列化方式和字符集
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public struct DataInfo
            {
                public int a;
                public int b;
                public char c;
                // char* 要使用 MarshalAs
                [MarshalAs(UnmanagedType.LPStr)]
                public string d;
            }
    
    
            // 委托函数MMCB的定义用于传入回调函数,对应于 dps.h 中的
            // int(MMAPI *MMCallBack)(int handle, char* UserData)
            // 特别注意:C# 回调函数的调用约定必须声明为 CallingConvention.Cdecl
            // 因为 C# 的调用约定是 __stdcall, 如果不指定调用约定,会默认 __stdcall,
            // 不同的调用约定会导致冲突
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            public delegate void MMCB(int handle, string userData);
    
            // 定义 MMAPIINFO.func01 的委托函数
            public delegate int Func01( int a, int b);
    
            // 定义 MMAPIINFO.func02 的委托函数
            public delegate void Func02(int c, ref DataInfo data, MMCB cbMmcb);
    
    
            // 封装函数,封装调用 MMAPIINFO.func01
            public int Ffunc01(int aa, int bb)
            {
                Func01 func = (Func01) Marshal.GetDelegateForFunctionPointer(A.func01, typeof(Func01));
                return func(aa, bb);
            
            }
    
            // 封装函数,封装调用 MMAPIINFO.func02
            public void Ffunc02(int c, ref DataInfo dat)
            {
                Func02 func = (Func02) Marshal.GetDelegateForFunctionPointer(A.func02, typeof(Func02));
                func(c, ref dat, apiCB);
            }
    
            // 成员变量 MMAPIINFO A 作为指针结构体
            public static MMAPIINFO A = new MMAPIINFO();
    
    
            // 声明 dsp.dll 的导出函数,注意调用约定 __cdecl 和字符集
            [DllImport("dps.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
            public static extern void GetAPI(ref MMAPIINFO param);
    
            // 构造函数用于从 dsp.dll 中获取api函数结构体
            public GetApi()
            {
                GetAPI(ref A);
            }
    
            // 实际传入的回调(被委托)函数
            public void apiCB(int handle, string userData)
            {
                Console.WriteLine(handle);
                Console.WriteLine(userData);
            }
        }
    }
    
  • 相关阅读:
    让history命令 显示执行命令的时间
    实现SSH免密登录
    数据库表空间一夜之间爆满 查看最大的表却才几百M 原来是大字段对象CLOB造成的
    Python-集合的操作
    Vim使用技巧
    Linux的内部命令和外部命令
    Redis缓存穿透和雪崩
    网络编程学习一:IP地址转换函数
    每日一问15:C++中的.h,cpp以及.hpp文件
    每日一问14: 缓存和缓冲的区别
  • 原文地址:https://www.cnblogs.com/gardenofhu/p/13675969.html
Copyright © 2020-2023  润新知