PInvokeStackImbalance - вызов неуправляемого кода из HookCallback - PullRequest
0 голосов
/ 10 августа 2010

моя цель

я хочу перевести левый клик в правый клик

мой подход

  • я регистрирую низкоуровневый хук через SetWindowsHookEx (user32.dll)
  • фильтр левого клика мыши
  • проверить, хочу ли я перевести ЭТО конкретный клик
  • на случай, если я действительно хочу
    • не передавать сообщение
    • создать новый щелчок мышью с помощью mouse_event (тоже user32.dll)

проблема

когда я делаю описанные вещи, вот так:

  private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
     if(nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam && doRight) {
        doRight = false;
        MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
        mouse_event(/*right down + right up*/8 | 16, hookStruct.pt.x, hookStruct.pt.y, 0, 0);
        return new IntPtr(1);
     }
     return CallNextHookEx(_hookID, nCode, wParam, lParam);
  }

вызов mouse_event завершается неудачно с PInvokeStackImbalance-Exception, о котором, я думаю, я должен заботиться.

DllImports

, поскольку обычно PInvokeStackImbalance возникает из-за некорректных сигнатур импорта, вот мои:

  [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

  [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  [return: MarshalAs(UnmanagedType.Bool)]
  private static extern bool UnhookWindowsHookEx(IntPtr hhk);

  [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

  [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern IntPtr GetModuleHandle(string lpModuleName);

  [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);

появление

мой обычный подход к изоляции проблемы в этом случае терпит неудачу - так как вызов mouse_event для себя работает, и удаление левых щелчков тоже работает. я ненавижу, когда структура больше, чем сумма частей ...

Ответы [ 3 ]

2 голосов
/ 10 августа 2010

mouse_event определяется как:

VOID WINAPI mouse_event(
  __in  DWORD dwFlags,
  __in  DWORD dx,
  __in  DWORD dy,
  __in  DWORD dwData,
  __in  ULONG_PTR dwExtraInfo
);

WINAPI означает StdCall, DWORD - это uint, ULONG_PTR - это UIntPtr. Так правильно должно быть:

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, UIntPtr dwExtraInfo);

Проблема в том, что long определяется в C # как 64-битный, а (u) int 32-битный. (U) IntPtr является 32-разрядным или 64-разрядным, в зависимости от разрядности операционной системы.

РЕДАКТИРОВАТЬ: Другими словами, вы передаете слишком много данных в функцию. Поскольку вызываемый объект очищает стек, PInvoke замечает, что не все было удалено из стека. Он делает это для вас (но вы передали неверные данные, поэтому функция, скорее всего, сделала нечто иное, чем вы хотели) и предупреждает вас.

2 голосов
/ 10 августа 2010

В Интернете существует много плохих объявлений p / invoke. Как правило, они начали жизнь в VB6. На этом языке Integer равен 16 битам, а Long - 32 битам. Возвращается к VB1, который работал в 16-битных операционных системах. Они не работают должным образом в VB.NET или C #.

MDA стека дисбаланса был специально разработан, чтобы отлавливать такие некорректные объявления, но это вариант (отладка + исключения). Фактический вызов имеет тенденцию работать, если есть только один аргумент или когда передается много нулей. Вероятность того, что код продолжает работать правильно после вызова, к сожалению, тоже неплохая. Значение указателя стека исправляется при возврате метода. Вы можете получить некоторые действительно странные проблемы.

1 голос
/ 10 августа 2010

подпись mouse_event должна быть такой

public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, IntPtr dwExtraInfo);
...