首页 > 解决方案 > 使用 dart:ffi 实现 winapi(SendInput)

问题描述

我正在尝试使用 ffi 在 dart 中实现SendInput函数。它需要一个无符号整数、一个输入数组和一个整数。我发现很难实现 INPUT 数组结构,这是我到目前为止所做的:

import 'dart:ffi';

import 'package:ffi/ffi.dart';

import 'user32.dart'; //import dylib

typedef SendInputC = Uint32 Function(
    Uint32 cInputs, Pointer pInputs, Int32 cbSize);

typedef SendInputDart = int Function(int inputCount, Pointer inputs, int size);

int SendInput(int type, {MouseInput mi, KeyboardInput ki, HardwareInput hi}) {
  final SendInputP =
      dylib.lookupFunction<SendInputC, SendInputDart>('SendInput');

  var input = Input.allocate(
      type: type,
      mi: mi?.addressOf ?? nullptr,
      ki: ki?.addressOf ?? nullptr,
      hi: hi?.addressOf ?? nullptr);

  var result = SendInputP(1, input.addressOf, sizeOf<Input>());
  return result;
}

class Input extends Struct {
  @Uint32()
  int type;

  Pointer<MouseInput> mi;
  Pointer<KeyboardInput> ki;
  Pointer<HardwareInput> hi;

  factory Input.allocate(
          {int type,
          Pointer<MouseInput> mi,
          Pointer<KeyboardInput> ki,
          Pointer<HardwareInput> hi}) =>
      allocate<Input>().ref
        ..type = type
        ..mi = mi
        ..ki = ki
        ..hi = hi;
}

/// MouseInput struct
class MouseInput extends Struct {
  @Int32()
  int dx;

  @Int32()
  int dy;

  @Uint32()
  int mouseData;

  @Uint32()
  int dwFlags;

  @Uint32()
  int time;

  @Uint32()
  int dwExtraInfo;

  factory MouseInput.allocate(
          {int dx = 0,
          int dy = 0,
          int mouseData = 0,
          int dwFlags = 0,
          int time = 0,
          int dwExtraInfo = 0}) =>
      allocate<MouseInput>().ref
        ..dx = dx
        ..dy = dy
        ..mouseData = mouseData
        ..dwFlags = dwFlags
        ..time = time
        ..dwExtraInfo = dwExtraInfo;
}

class KeyboardInput extends Struct {
  @Uint16()
  int wVk;

  @Uint16()
  int wScan;

  @Uint32()
  int dwFlags;

  @Uint32()
  int time;

  @Uint32()
  int dwExtraInfo;

  factory KeyboardInput.allocate(
          {int wVk = 0,
          int wScan = 0,
          int dwFlags = 0,
          int time = 0,
          int dwExtraInfo = 0}) =>
      allocate<KeyboardInput>().ref
        ..wVk = wVk
        ..wScan = wScan
        ..dwFlags = dwFlags
        ..time = time
        ..dwExtraInfo = dwExtraInfo;
}

class HardwareInput extends Struct {
  @Uint32()
  int uMsg;

  @Uint16()
  int wParamL;

  @Uint16()
  int wParamH;

  factory HardwareInput.allocate(
          {int uMsg = 0, int wParamL = 0, int wParamH = 0}) =>
      allocate<HardwareInput>().ref
        ..uMsg = uMsg
        ..wParamL = wParamL
        ..wParamH = wParamH;
}

但调用该SendInput函数不会产生任何效果并返回 0,例如此代码不发送鼠标输入。

  var mi = MouseInput.allocate(dx: 505, dy: 175, dwFlags: 0x0002);
  var r = SendInput(0, mi: mi);

这就是函数在 C++ 中的样子

    INPUT input;
    input.type = INPUT_MOUSE;
    input.mi.dx = 505;
    input.mi.dy = 175;
    input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    input.mi.dwExtraInfo = 0;
    input.mi.time = 0;


    SendInput(1, &input, sizeof(input));

tagInput结构包含似乎尚不支持的联合(https://github.com/dart-lang/sdk/issues/38491),但我想知道在支持联合之前是否有解决方法。

标签: c++winapidartffi

解决方案


你的方法行不通,因为你的结构太大了。您真正想要的是分配的内存具有与常规 C 版本相同的大小(即 28 字节),因此如下所示:

class KeyboardInput extends Struct {
  @Uint32() int type;
  @Uint16() int virtualCode;
  @Uint16() int scanCode;
  @Uint32() int flags;
  @Uint32() int time;
  Pointer dwExtraInfo;
  @Uint32() int _padding;
}

话虽如此 - 它仍然不起作用,我真的不知道为什么。我什至尝试过只传递原始字节数组指针,它在 dart 中仍然不起作用(在 C 中它工作得很好)。这可能是飞镖 FFI 本身的一个错误,但可能是很多其他的东西。我正在尝试破解它,因为我也需要它来工作。

现在,您可以使用已弃用的功能:

import 'dart:ffi';
DynamicLibrary _user32 = DynamicLibrary.open("user32.dll");

typedef _keybd_event_C = Void Function(Uint8 bVk, Uint8 bScan, Uint32 dwFlags, Pointer dwExtraInfo);
typedef _keybd_event_Dart = void Function(int bVk, int bScan, int dwFlags, Pointer dwExtraInfo);
var keybd_event = _user32.lookupFunction<_keybd_event_C, _keybd_event_Dart>("keybd_event");

const MAPVK_VK_TO_VSC = 0;
typedef _MapVirtualKeyW_C = Uint32 Function(Uint32 uCode, Uint32 uMapType);
typedef _MapVirtualKeyW_Dart = int Function(int uCode, int uMapType);
var MapVirtualKeyW = _user32.lookupFunction<_MapVirtualKeyW_C, _MapVirtualKeyW_Dart>("MapVirtualKeyW");


void main() {
  keybd_event(
    0x5a, 
    MapVirtualKeyW(0x5a, MAPVK_VK_TO_VSC), 
    0, 
    nullptr);
}

PS:此外,如果您需要 WinAPI 的更多功能,您可以使用我的快速拼接飞镖绑定生成器来获取 WinAPI 功能。您只需运行它并将 WinAPI 函数粘贴到上部 textarea 中,底部区域应显示 dart 绑定。有时它缺少一些类型定义(只需添加另一种类型Mapping),但在大多数情况下它工作得很好:)(不适用于结构强硬)


推荐阅读