记录使用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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace WpfApp1.Utils
{
    /// <summary>
    /// DLLWrapper
    /// </summary>
    internal class FDllWrapper
    {
        [DllImport("kernel32.dll")]
        private static extern uint GetLastError();


        /// <summary>
        /// API LoadLibraryEx
        /// </summary>
        /// <param name="lpFileName"></param>
        /// <param name="hReservedNull"></param>
        /// <param name="dwFlags"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", EntryPoint = "LoadLibraryEx", SetLastError = true)]
        private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary", SetLastError = true)]
        private static extern int LoadLibrary(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
        /// <summary>
        /// API GetProcAddress
        /// </summary>
        /// <param name="handle"></param>
        /// <param name="funcname"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", SetLastError = true)]
        public static extern IntPtr GetProcAddress(IntPtr handle, string funcname);

        /// <summary>
        ///  API FreeLibrary
        /// </summary>
        /// <param name="handle"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary", SetLastError = true)]
        private static extern int FreeLibrary(IntPtr handle);

        ///<summary>
        /// 通过非托管函数名转换为对应的委托 
        ///</summary>
        ///<param name="dllModule"> 通过 LoadLibrary 获得的 DLL 句柄 </param>
        ///<param name="functionName"> 非托管函数名 </param>
        ///<param name="t"> 对应的委托类型 </param>
        ///<returns> 委托实例,可强制转换为适当的委托类型 </returns>
        public static Delegate GetFunctionAddress(IntPtr dllModule, string functionName, Type t)
        {
            IntPtr address = GetProcAddress(dllModule, functionName);
            var errCode = GetLastError();
            FLog.d($"GetFunctionAddress:{address} ErrorCode: {errCode}");
            if (address == null)
                return null;
            else
                return Marshal.GetDelegateForFunctionPointer(address, t);
        }

        ///<summary>
        /// 将表示函数地址的 intPtr 实例转换成对应的委托
        ///</summary>
        public static Delegate GetDelegateFromIntPtr(IntPtr address, Type t)
        {
            if (address == IntPtr.Zero)
                return null;
            else
                return Marshal.GetDelegateForFunctionPointer(address, t);
        }

        ///<summary>
        /// 将表示函数地址的 int  转换成对应的委托
        ///</summary>
        public static Delegate GetDelegateFromIntPtr(int address, Type t)
        {
            if (address == 0)
                return null;
            else
                return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
        }

        /// <summary>
        /// 加载sdk
        /// </summary>
        /// <param name="lpFileName"></param>
        /// <returns></returns>
        public static IntPtr LoadSDK(string lpFileName)
        {

            //if (File.Exists(lpFileName))
            {
                var hReservedNull = IntPtr.Zero;
                var dwFlags = LoadLibraryFlags.LOAD_WITH_ALTERED_SEARCH_PATH;

                IntPtr result = LoadLibraryEx(lpFileName, hReservedNull, dwFlags);

                var errCode = GetLastError();
                FLog.d($"LoadSDK Result:{result}, ErrorCode: {errCode}");

                return result;
            }
            //return 0;
        }

        /// <summary>
        /// 释放sdk
        /// </summary>
        /// <param name="handle"></param>
        /// <returns></returns>
        public static int ReleaseSDK(IntPtr handle)
        {
            try
            {
                if (handle != null)
                {
                    FLog.d($"FreeLibrary handle:{handle}");
                    var result = FreeLibrary(handle);
                    var errCode = GetLastError();
                    FLog.d($"FreeLibrary Result:{result}, ErrorCode: {errCode}");
                    return 0;
                }
                return -1;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                //LogHelper.Error(ex);
                return -1;
            }
        }
    }

    /// <summary>
    /// LoadLibraryFlags
    /// </summary>
    public enum LoadLibraryFlags : uint
    {
        /// <summary>
        /// DONT_RESOLVE_DLL_REFERENCES
        /// </summary>
        DONT_RESOLVE_DLL_REFERENCES = 0x00000001,

        /// <summary>
        /// LOAD_IGNORE_CODE_AUTHZ_LEVEL
        /// </summary>
        LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,

        /// <summary>
        /// LOAD_LIBRARY_AS_DATAFILE
        /// </summary>
        LOAD_LIBRARY_AS_DATAFILE = 0x00000002,

        /// <summary>
        /// LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
        /// </summary>
        LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,

        /// <summary>
        /// LOAD_LIBRARY_AS_IMAGE_RESOURCE
        /// </summary>
        LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,

        /// <summary>
        /// LOAD_LIBRARY_SEARCH_APPLICATION_DIR
        /// </summary>
        LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,

        /// <summary>
        /// LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
        /// </summary>
        LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,

        /// <summary>
        /// LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
        /// </summary>
        LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,

        /// <summary>
        /// LOAD_LIBRARY_SEARCH_SYSTEM32
        /// </summary>
        LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,

        /// <summary>
        /// LOAD_LIBRARY_SEARCH_USER_DIRS
        /// </summary>
        LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,

        /// <summary>
        /// LOAD_WITH_ALTERED_SEARCH_PATH
        /// </summary>
        LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
    }
}

 

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);

源码下载:(注意,因公司版权问题,已经去掉对应的键盘鼠标的库文件)

隐藏内容

此处内容需要权限查看

  • 普通用户购买价格:0.1心愿
  • VIP用户购买价格:免费
  • SVIP用户购买价格:免费推荐
VIP免费查看

 

发表评论

您的电子邮箱地址不会被公开。