1

I am using RegisterRawInputDevices of user32.dll to register the keyboard, but it's returning false. I am developing an Office add-in using VSTO and C#.

[StructLayout(LayoutKind.Sequential)]
public struct RAWINPUTDEVICE
{
    [MarshalAs(UnmanagedType.U2)]
    public UInt16 usUsagePage;
    [MarshalAs(UnmanagedType.U2)]
    public UInt16 usUsage;
    [MarshalAs(UnmanagedType.U4)]
    public int dwFlags;
    public IntPtr hwndTarget;
}

[DllImport("User32.dll", SetLastError = true)]
public static extern bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice,
    UInt32 uiNumDevices, UInt32 cbSize);

[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

public void Register()
{
    IntPtr hwnd = FindWindow("PPTFrameClass", "Presentation1 - PowerPoint");
    RegisterKeyboardDevice(hwnd);
}

public void RegisterKeyboardDevice(IntPtr hwnd)
{
    const int RIDEV_INPUTSINK = 0x00000100;
    RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1];
    rid[0].usUsagePage = Convert.ToUInt16(1);
    rid[0].usUsage = Convert.ToUInt16(6);
    rid[0].dwFlags = RIDEV_INPUTSINK;
    rid[0].hwndTarget = hwnd;
    if (!RegisterRawInputDevices(rid, Convert.ToUInt32(rid.Length),
        Convert.ToUInt32(Marshal.SizeOf(rid[0]))))
    {
        throw new ApplicationException("Failed to register raw input device(s). " +
            "Error code: " + Marshal.GetLastWin32Error());
    }
}

But the same logic is working fine for VB.NET and COM-addins. Please correct me if I am going wrong.

haindl
  • 3,111
  • 2
  • 25
  • 31
  • Try using `[DllImport("User32.dll", SetLastError = true)]`. Now the `Marshal.GetLastWin32Error()` should be filled with something more useful to pinpoint the error. – haindl Oct 24 '16 at 13:38
  • I got only "87" in the exception message. i am not getting any details for this exception. If you could help it will be better. – Abhinav Srivastava Oct 25 '16 at 11:51
  • A Win32 error of 87 is `0x00000057` in hex and means `ERROR_INVALID_PARAMETER`. (Referring to [here](https://msdn.microsoft.com/en-us/library/cc231199.aspx).) So you have to check all of your parameters. The next step I would try, is to add this to the first parameter or your external method signature according to [here](http://www.pinvoke.net/default.aspx/user32.registerrawinputdevices): `[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] RAWINPUTDEVICE[] pRawInputDevice` – haindl Oct 25 '16 at 12:09
  • I have checked all the parameters again,all looks correct. The same set of parameter is working fine for vb.net. I have also tried using the method signature provided by you. – Abhinav Srivastava Oct 27 '16 at 07:47
  • Did you check if `hwnd` is a valid window handle and not zero? Is the window of this handle already created and fully initialized? I tested your code and I got error 87 only when `hwnd` was `0` or an `IntPtr` that didn't exist. (For example something like `new IntPtr(0x22)` just for testing.) So please check your `hwnd`. If it still doesn't work then post your definition of `struct RAWINPUTDEVICE` with its sub-types and the value of `RIDEV_INPUTSINK` that you used and I will look back to it. – haindl Oct 27 '16 at 08:24
  • yes, i ma getting hwnd as valid window handle,. The value of RIDEV_INPUTSINK is 256. please find below the code i have used: [StructLayout(LayoutKind.Sequential)] public struct RAWINPUTDEVICE { [MarshalAs(UnmanagedType.U2)] public UInt16 usUsagePage; [MarshalAs(UnmanagedType.U2)] public UInt16 usUsage; [MarshalAs(UnmanagedType.U4)] public int dwFlags; public IntPtr hwndTarget; } – Abhinav Srivastava Oct 27 '16 at 09:06
  • [DllImport("User32.dll")] public static extern bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, UInt32 uiNumDevices, UInt32 cbSize); [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); static void Main(string[] args) { Console.Title = "test"; bool returnedValue = RegisterKeyboardDevice((IntPtr)FindWindow(null, "test")); } – Abhinav Srivastava Oct 27 '16 at 09:14
  • public static bool RegisterKeyboardDevice(IntPtr hwnd) { RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1]; rid[0].usUsagePage = Convert.ToUInt16(1); rid[0].usUsage = Convert.ToUInt16(6); rid[0].dwFlags = 256; rid[0].hwndTarget = hwnd; return RegisterRawInputDevices(rid, Convert.ToUInt32(rid.Length), Convert.ToUInt32(Marshal.SizeOf(rid[0]))); } – Abhinav Srivastava Oct 27 '16 at 09:14
  • You could (and should) have edited your question instead of posting the code as comments because nobody can read this. But aside from that I'm pretty sure that you can't use `RegisterRawInputDevices` with a Console window because it doesn't have a usable message loop and therefore isn't a valid window for this call. (See [here](http://stackoverflow.com/q/6800982/6682181) for reference.) You have to create (or use) a Win32 window with a message loop. (I thought you are using VSTO?) The rest of your code looks fine. Just try it in a WinForms or WPF window **after** is has been shown/initialized. – haindl Oct 27 '16 at 09:56
  • sorry for the inconvenience, i tried to edit the code but could not be able to do that as am new to this platform. – Abhinav Srivastava Oct 28 '16 at 07:05
  • Yes, i am using RegisterRawInputDevices for VSTO where i have to capture the key stroke on PPT application. Above code was just for sample. let me post the actual code. – Abhinav Srivastava Oct 28 '16 at 07:07

1 Answers1

0

You can call RegisterRawInputDevices only on windows that belong to the same process as your calling code.

I tested your code in an Excel C# VSTO addin as well as in an Excel C# COM addin and in both cases I could successfully call RegisterRawInputDevices on the Excel window itself and on a WPF test window, that was created from inside the addin, without any kind of error. (My version for this test was Excel 2016 64-bit.)

Unfortunately I don't have any kind of reference for this behavior but I think this is intentional by design, maybe because of security concerns as you could potentially hijack some other window and possibly act as a kind of keylogger or doing some other really bad stuff.

So your code does work perfectly but you have to use a window that belongs to the same process. And I think the difference to your VB.NET addin is that it's working because it's playing by this rule.

haindl
  • 3,111
  • 2
  • 25
  • 31