记录使用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
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);
源码下载:(注意,因公司版权问题,已经去掉对应的键盘鼠标的库文件)
此处内容需要权限查看
VIP免费查看