C#总结:C#调用C++的动态库Dll遇到的问题[动态库调用/结构体指针调用/union共同体定义]

记录使用C#调用C++的生成的DLL手柄键盘驱动库包括****.sys(驱动文件)和****.dll(库文件)的全部问题。

C#调用C++的库有两种:静态调用和动态调用

静态调用,使用.net 提供的DllImport 导入相关的C++ 库即可。

静态调用示例:

C++ 的库原型:

int _stdcall VKSendKeyEx_KB(HANDLE hKeyboard, PVK_SENDKEY pVkSendKey);
int _stdcall VKSendKeyAction_MU(HANDLE hMouse, MOUSE_ACTION* pMouAction);
int _stdcall KmdDriverRemove(SC_HANDLE hSysService);
int _stdcall DriverDeviceOpen(char* sDeviceName, HANDLE* phDevice);
int _stdcall DriverDeviceClose(HANDLE hDevice);

C#调用示例:

注意:动态库需要放在exe的运行目录下,

调用头部加上:using System.Runtime.InteropServices;

[DllImport("SayyoDll.dll")]
public static extern int KmdDriverRemove(ref IntPtr phSysService);

[DllImport("SayyoDll.dll")]
public static extern int DriverDeviceOpen([MarshalAs(UnmanagedType.LPStr)] string sDeviceName, ref IntPtr phDevice);

[DllImport("SayyoDll.dll")]
public static extern int DriverDeviceClose(IntPtr hDevice);

[DllImport("SayyoDll.dll")]
public static extern int VKSendMsVirtualKeySingle_KB(IntPtr hKeyboard, byte VirtualKey, bool bUp);

[DllImport("SayyoDll.dll", EntryPoint = "VKSendKeyAction_MU", CallingConvention = CallingConvention.Cdecl)]
public static extern int VKSendKeyAction_MU(IntPtr hMouse, ref MOUSE_ACTION pMouAction);

[DllImport("SayyoDll.dll", EntryPoint = "VKSendKeyAction_MU", CallingConvention = CallingConvention.Cdecl)]
public static extern int VKSendKeyAction_MUIntPtr(IntPtr hMouse, IntPtr pMouAction);

动态调用:

Dll库的目录可能是变化的,或是有些场景,需要根据具体的情况,来动态加载这些Dll库。只要通过LoadLibrary, GetProcess, FreeLibrary这几个函数是可以动态调用动态链接的(它们包含在kernel32.dll中)。

原理
LoadLibrary ( string lpFileName):载入指定的动态链接库,并将它映射到当前进程使用的地址空间。载入成功后即可访问库内保存的资源 , 除了LoadLibrary 方法,还有一个类似的 LoadLibraryEx 方法。

GetProcAddress (int hModule, string lpProcName):GetProcAddress函数检索指定的动态链接库(DLL)中的输出库函数地址。 如果函数调用成功,返回值是DLL中的输出函数地址。 如果函数调用失败,返回值是NULL。调用函数GetLastError ,得到具体的错误信息。

FreeLibrary ( int hModule) :释放指定的动态链接库,它们早先是用LoadLibrary API函数装载的。

GetLastError() : 获取错误信息

动态调用示例:

C++动态库原型:

int _stdcall KmdDriverSetup(char* sPath, SC_HANDLE* phSysService);

1. 将kernel32中的几个方法封装成本地调用类 FDLLWrapper

点击展开代码

源码下载:C#总结:C#调用C++的动态库Dll遇到的问题_林哥哥_好物分享网BestSvps

2. 使用DLLWrapper类动态读取C++Dll,获得函数指针,并且将指针封装成C#中的委托。原因很简单,C#中已经不能使用指针了,如下:

定义委托:对应C++函数原型:int _stdcall KmdDriverSetup(char* sPath, SC_HANDLE* phSysService);

[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate int Delegate_KmdDriverSetup([MarshalAs(UnmanagedType.LPStr)] string sFullPath, ref IntPtr FService);

3. 调用函数

            //1. 加载dll
            IntPtr FLibHandle = FDllWrapper.LoadSDK(@dllPath);
            if (FLibHandle == null)
            {
                FLog.d("driver load failed");
            }
            else
            {
                FLog.d("driver load success");
            }

            // 2. 通过handle 找到相关的函数
            Delegate_KmdDriverSetup FKmdDriverSetupFuc = (Delegate_KmdDriverSetup)FDllWrapper.GetFunctionAddress(FLibHandle, "KmdDriverSetup", typeof(Delegate_KmdDriverSetup));

            if (FKmdDriverSetupFuc(@sysFullPath, ref FLyService) == 1)
            {
                FLog.d("driver init success");
            }
            else
            {
                FLog.d("driver init failed");
            }

            if (DriverDeviceOpen(ckbDriver, ref VKeyboardDevice) == 1)
            {
                FLog.d("Visual keyboard device open success");
            }
            else
            {
                FLog.d("Visual keyboard device open faield");
            }
            if (DriverDeviceOpen(cmuDriver, ref VMouseDevice) == 1)
            {
                FLog.d("Visual mouse device open success");
            }
            else
            {
                FLog.d("Visual mouse device open failed");
            }

C#调用C++的动态库中含有结构体指针

C++函数原型及结构体定义

typedef struct {
    USHORT Type;        /* 鼠标动作类型,0: 移动,1: 按键, 2: 滚轮 */
    USHORT Reserved;
    union {
        struct {
            USHORT SimulateType;    // 模拟移动时采用的算法,0: Simple, 1: Line, 2: Adjust, 3: Fast
            USHORT AxisFlag;    // 0: 相对坐标,1: 相对坐标
            int x;              // 目标坐标
            int y;              // 目标坐标
        } Move;
        struct {
            USHORT ButtonType;  /* 0: 按下和弹出单独模拟,1: 按下和弹出同时模拟,并且可以指示多少个按键同时按下 */
            USHORT ButtonFlags; /* 依据ButtonType的不同含义而不同,ButtonType为0时,含义等于MOUSE_INPUT_DATA.ButtonFlags, 当ButtonType为1时,每一位代表一个按键 */
        } Button;
        struct {
            short int Count;    /* 滚动数量, 上滚: 正数;下滚: 负数 */
            short int Reserved;
        } Wheel;
    };
} MOUSE_ACTION;

int _stdcall VKSendKeyAction_MU(HANDLE hMouse, MOUSE_ACTION *pMouAction);

注意函数原型如果这么写就是结构体数组了,不是结构体指针

int _stdcall VKSendKeyAction_MU(HANDLE hMouse, MOUSE_ACTION* pMouAction);

C#定义对应的结构体并调用:

注意:C#内没有union对应的结构需要使用 [StructLayout(LayoutKind.Explicit)]和[FieldOffset(num)]来结合声明共同体

类型对应关系及字节表

C++类型 C#类型 bit 字节
unsigined short System.UInt16 16 2
int System.Int32 32 4
short System.Int16 16 2
    [StructLayout(LayoutKind.Explicit, Size = 16)]
    public struct MOUSE_ACTION
    {
        [FieldOffset(0)]
        public System.UInt16 Type;
        [FieldOffset(2)]
        public System.UInt16 Reserved;
        [FieldOffset(4)]
        public MOUSE_ACTION_Move mMove;
        [FieldOffset(4)]
        public MOUSE_ACTION_Button mButton;
        [FieldOffset(4)]
        public MOUSE_ACTION_Wheel mWheel;
    }
    //12字节
    public struct MOUSE_ACTION_Move
    {
        public System.UInt16 SimulateType;
        public System.UInt16 AxisFlag;
        public System.Int32 x;
        public System.Int32 y;
    }

    //4字节
    public struct MOUSE_ACTION_Button
    {
        public System.UInt16 ButtonType;
        public System.UInt16 ButtonFlags;
    }
    //4字节
    public struct MOUSE_ACTION_Wheel
    {
        public System.Int16 Count;
        public System.Int16 Reserved;
    }

C#调用结构体指针实例:

使用IntPtr方式调用:

MOUSE_ACTION oMouseAction = new MOUSE_ACTION();
//分配大小为结构体MOUSE_ACTION的intptr指针地址
IntPtr ptrMouseAction = Marshal.AllocHGlobal(Marshal.SizeOf(oMouseAction));

//初始化结构体
oMouseAction.Type = 0;
oMouseAction.Reserved = 0;
oMouseAction.mMove.SimulateType = 2;
oMouseAction.mMove.AxisFlag = 0;
oMouseAction.mMove.x = 100;
oMouseAction.mMove.y = 200;

//为指针赋值
Marshal.StructureToPtr(oMouseAction, ptrMouseAction, false);

//指针方式调用
VKSendKeyAction_MUIntPtr(VMouseDevice, ptrMouseAction);

//这种方式没测试是否成功
VKSendKeyAction_MU(VMouseDevice, ref oMouseAction);
//释放指针
Marshal.FreeHGlobal(ptrMouseAction);

源码下载:

C#总结:C#调用C++的动态库Dll遇到的问题_林哥哥_好物分享网BestSvps

原文链接:www.bestsvps.com/3184.html

你可能感兴趣的:(c#,c++,mfc,wpf,开发语言)