1

I'm having trouble using _set_purecall_handler with P/Invoke in C#.

Basically, this works:

(C++)

_set_purecall_handler(MyPureCallHandler);

void MyPureCallHandler(void)
{
    // gets called
}

But this doesn’t:

(C#)

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void PureCallHandler();

[DllImport("msvcr100.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr _set_purecall_handler([MarshalAs(UnmanagedType.FunctionPtr)] PureCallHandler handler);

_set_purecall_handler(MyPureCallHandler);

private void MyPureCallHandler()
{
    // *** doesn’t get called ***
}

I’m not sure if my P/Invoke method signature is correct, but it doesn't throw any errors when I call the function (it just doesn't fire the callback on a pure virtual call error).

Background

We have a number of apps (C++, C++/CLI and C#) that use a single C# library for catching exceptions. This registers various handlers (AppDomain.CurrentDomain.UnhandledException, SetUnhandledExceptionFilter, etc) and catches most exceptions.

However, it doesn't catch pure virtual call errors and so we need to register the above function.

Dunc
  • 18,404
  • 6
  • 86
  • 103
  • Are you 100% sure that your C# is calling into the same instance of the C runtime as is you C++ code that is calling the pure virtual functions? – David Heffernan Jul 25 '12 at 12:22
  • David, we already P/Invoke some kernel32.dll functions which successfully intercept errors in C++ (SetUnhandledExceptionFilter, SetProcessUserModeExceptionPolicy, ...) but haven't imported msvcr100.dll before. Shouldn't this be the same? – Dunc Jul 25 '12 at 13:30
  • You might not be getting the same C++ runtime as is used by your native code. But that could easily be a red herring. – David Heffernan Jul 25 '12 at 13:33
  • It won't work if your c++ code is statically linked to the runtime library. – Camford Jul 25 '12 at 14:45
  • @Camford Indeed so. That's a special case of the possible problem that I highlight. – David Heffernan Jul 25 '12 at 15:49

1 Answers1

1

After trial and error, I found that referencing msvcr100d.dll (d = debug) instead of msvcr100.dll worked as I was under the debugger.

Here's my source (I don't know if this is good practice, but I've successfully tested under debug / release mode):

private const string DllName =
    #if DEBUG
        "msvcr100d.dll";
    #else
        "msvcr100.dll"; 
    #endif

public delegate void PureCallHandler();

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern PureCallHandler _set_purecall_handler(PureCallHandler handler);
Dunc
  • 18,404
  • 6
  • 86
  • 103
  • In other words this is exactly as I guessed, you were linking to a different instance of the runtime. If I were you I would enumerate module handles, or ask the native code to give you a module handle, and then link with GetProcAddress. That way you can be sure to get the right runtime instance. – David Heffernan Jul 25 '12 at 22:43
  • @David thanks - RE module handles, some links or code examples would be welcomed (surely I'm not the first person to hook into msvcr100?) – Dunc Jul 26 '12 at 09:00
  • Are you able to modify the native C++ code that actually links to the MSVC runtime? – David Heffernan Jul 26 '12 at 11:47
  • @David, not really - the problem is that the C# project is consumed by a number of separate mixed apps, so it would be much easier if we didn't have to update every one – Dunc Jul 26 '12 at 14:09
  • Then you'll want to scratch around looking for any instances of the MS runtime. And that would fail if you ever used static linking. Which you probably don't. Couldn't you just modify the C# project's interface so that it's initialisation routines received the _set_purecall_handler function pointer. Would force code changes, but that would be pretty trivial. – David Heffernan Jul 26 '12 at 14:13