1

So I am trying to write a wrapper around a c++ API so I can use it with C#. The c++ api and information about it is located here.

So I am trying to interact with the C++ dll from C# code. I am able to do so succesfully up to a certain point. But right now I am hung up trying to get a certain function working correctly. An example of a working call to the function in C++ is:

interception_set_filter(context, interception_is_keyboard, INTERCEPTION_FILTER_KEY_DOWN | INTERCEPTION_FILTER_KEY_UP);

where INTERCEPTION_FILTER_KEY_DOWN and INTERCEPTION_FILTER_KEY_DOWN are defined by:

typedef int (*InterceptionPredicate)(InterceptionDevice device);

enum InterceptionKeyState
{
    INTERCEPTION_KEY_DOWN             = 0x00,
    INTERCEPTION_KEY_UP               = 0x01,
...
};

    enum InterceptionFilterKeyState
    {
    INTERCEPTION_FILTER_KEY_DOWN             = INTERCEPTION_KEY_UP,
    INTERCEPTION_FILTER_KEY_UP               = INTERCEPTION_KEY_UP << 1,
};

and interception_is_keyboard one of various predicates that can be passed into interception_set_filter. The function of the predicate is described this way:

The interception_set_filter function has three parameters, the context of communication, a function pointer and a desired filter. This second parameter, the function pointer, is a device selection predicate, a function that receives a device id (like INTERCEPTION_KEYBOARD(0), INTERCEPTION_KEYBOARD(1), etc) as parameter and returns true if the device id passed is one of a device that must be filtered through the chosen filter, or false for the devices that are not to be filtered through this filter. So the interception_set_filter works by scanning all possible devices, and using the provided predicate as the criteria to know for which devices the provided filter should be applied.

Further, a method that implements the signature of this predicate interception_is_keyboard is defined in code thus:

int interception_is_keyboard(InterceptionDevice device)
{
    return device >= INTERCEPTION_KEYBOARD(0) && device <= INTERCEPTION_KEYBOARD(INTERCEPTION_MAX_KEYBOARD - 1);
}

The definition in the .h file for interception_set_filter is:

void ITERCEPTION_API interception_set_filter(InterceptionContext context, InterceptionPredicate predicate, InterceptionFilter filter);

where ITERCEPTION_API is:

#define ITERCEPTION_API __declspec(dllimport)

So, my question is, how can I set up to be able to call interception_set_filter and use the predicate interception_is_keyboard from a C# app (managed code)?


JayGee
  • 577
  • 6
  • 16

2 Answers2

1

You can use C# delegates to create function pointers for PInvoke parameters. A similar question was asked here.

EDIT filter is given by the enum, or Int32. So that is known.

EDIT 2 Hans Passant commented that I do not need to use [MarshalAs(UnmanagedType.FunctionPtr)], it is assumed (default)I learned something new, thank you Hans). Also he makes a great point to, "make sure that the delegate stays referenced so it cannot get garbage collected while the native code is making callbacks". Thank you very much.

EDIT 3 I read a little from the link you provided, and apparently context is a void*. I would assume you also have to PInvoke interception_create_context to acquire your void*, and then pass that pointer. Assuming you have a valid pointer, I've adjusted the answer accordingly.

Also, I don't see where you define InterceptionDevice, I'll assume Int32 for now.

My C# delegate would look like this:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void InterceptionPredicateType(Int32 device);

[DllImport("your.dll")]
public extern static int interception_is_keyboard(IntPtr context, InterceptionPredicateType predicate, Int32 filter);
Community
  • 1
  • 1
payo
  • 4,501
  • 1
  • 24
  • 32
  • 1
    The [MarshalAs] attribute is not necessary. You need to warn the OP that he needs to make sure that the delegate stays referenced so it cannot get garbage collected while the native code is making callbacks. – Hans Passant Mar 17 '14 at 19:30
  • Thanks to both of you. The definition for InterceptionDevice is typedef int InterceptionDevice; – JayGee Mar 17 '14 at 19:45
  • when you speak about making sure the delegate stays referenced, I assume you mean don't let it go out of scope? – JayGee Mar 17 '14 at 19:45
  • @JayGee not exactly, partially yes. Scope and resource deallocation doesn't work the same in a managed world. When an object goes out of scope in c# it is eligible for release, but the garbage collector can do it at its leisure (for optimization purposes). For safety, it is good to assume the data is released when it goes out of scope, but it could be technically delayed. Normally in c# when you pass around references (even refs to functions) the objects are ref counted and kept loaded, but when marshaling you lose that protection. – payo Mar 17 '14 at 19:48
1

There was already a C# wrapper here. The author dumped it because he preferred to make use of it in Clojure instead. Someone also made a Python wrapper and I've heard of others.

oblitum
  • 11,380
  • 6
  • 54
  • 120
  • Thanks very much. I'll look into his code to see how he took care of the interop declarations, etc. And thanks for putting interception together. – JayGee Mar 17 '14 at 21:29