Using the Interop Log to Diagnose P/Invoke Call Issues

Sometimes, determining why an unmanaged function call (P/Invoke) has failed can be challenging.  Fortunately, version 2 of the .NET Compact Framework has a new diagnostic log for this data -- the Interop Log.  In June 2005, Steven Pratschner wrote a great post detailing the Inteop Log and it's contents: Diagnosing Marshalling Errors using Interop Logging.

I would like to take some time to explore a specific example where the Interop Log can save time and make P/Invoke call debugging quick and easy.

Enabling the Interop Log
The Interop Log is enabled via the device's registry. In my steps, below, I will be using the Remote Registry Editor that is included with Visual Studio 2005.

WARNING: Using Remote Registry Editor incorrectly can cause serious problems that may require you to hard reset your device. Microsoft cannot guarantee that problems resulting from the incorrect use of Remote Registry Editor can be solved. Use Remote Registry Editor at your own risk.

  1. Start the Remote Registry Editor
    • Open the Start Menu
    • Select All Programs
    • Select Microsoft Visual Studio 2005
    • Select Visual Studio Remote Tools
    • Select Remote Registry Editor
  2. Connect to your device
    • On the Target menu, select Connect
    • Select your device and click OK
  3. Enable diagnostic logging
    • In the left hand pane, expand the device
    • Expand HKEY_LOCAL_MACHINE
    • Expand SOFTWARE
    • Expand Microsoft
    • Expand .NETCompactFramework
    • Expand Diagnostics
      If this key does not exist, you will need to create it
      • Right click on .NETCompactFramework
      • Select New > Key
      • Type "Diagnostics" (without quotes)
      • Click OK
    • Expand Logging
      If this key does not exist, you will need to create it
      • Right click on Diagnostics
      • Select New > Key
      • Type "Logging" (without quotes)
      • Click OK
    • Set the value of Enabled to 1
      If this value does not exist, you will need to create it
      • Right click on Logging
      • Select New > DWORD Value
      • Set the Name to "Enabled" (without quotes)
      • Set the value to 1
      • Click OK
  4. Enable interop logging
    • In the Logging key (from step 3) expand Interop
      If this key does not exist, you will need to create it
      • Right click on Logging
      • Select New > Key
      • Type "Interop" (without quotes)
      • Click OK
    • Set the value of Enabled to 1
      If this value does not exist, you will need to create it
      • Right click on Interop
      • Select New > DWORD Value
      • Set the Name to "Enabled" (without quotes)
      • Set the value to 1
      • Click OK

To disable the Interop Log, follow the above steps and set the value of Enabled to 0.
To disable all logging, follow only steps 1-3, and set the value of Enabled to 0.

There are a few optional settings that can be set to configure how the Interop Log is created. For details, please refer to Steven's post.

One optional setting I highly recommend is to create application specific log files. This causes the .NET Compact Framework to name the log file based on your application name. For example, if I enable logging with the UseApp option, the log file created for an application called DkTest.exe will be netcf_DkTest_interop.log.

To enable application specific log files please make the following addition to the above steps.

  1. Enable application specific log files
    • In the Logging key set the value of UseApp to 1
      If this value does not exist, you will need to create it
      • Right click on Logging
      • Select New > DWORD Value
      • Set the Name to "UseApp" (without quotes)
      • Set the value to 1
      • Click OK

Diagnosing a P/Invoke call issue
One of the things that I sometimes forget is that .NET types are not necessarily the same as Win32 types.  One type, in particular, where this is true is long.  On Win32, a long (or LONG) takes up 4 bytes (32 bits).  In the .NET world, a long is 8 bytes (64 bits) in size.

Let's look at an example where this type size difference can cause application issues.

First, a simple function written in C/C++
int SampleFunction(long lData){    // int == long on Win32 systems    return lData;}To be able to call this function, our managed application must define the signature for the P/Invoke.
[DllImport("InteropLogExampleNative.dll", SetLastError=true)]public static extern int SampleFunction(long lData);
Now we can call our function.
int result = SampleFunction(long.MaxValue);
If you add the above code to a simple project and attempt to call the P/Invoke method, depending on the data passed to the function, you may encounter "calculation errors" related to the wrong data type being sent to the unmanaged function.

If the Interop logging is enabled, we can see that the application tried to call the method with an 8 byte value when the method was expecting only 4 bytes. 

[pinvokeimpl][preservesig]int InteropLogPInvokeExample.Program::SampleFunction(long );int (I4_VAL) SampleFunction(INT64 (I8_VAL) );
To quote Steven: "The Compact Framework does not have intrinsic knowledge of the native function you are calling – it simply takes the managed definition you’ve provided and creates an equivalent native signature. If this signature doesn’t match the actual signature of the targeted native function, a marshaling error will occur."

By changing the P/Invoke signature to specify a 4 byte integer, the call succeeds.

// managed code// p/invoke method signature.  //// note: a Win32 long is the same size as a .NET int[DllImport("InteropLogExampleNative.dll", SetLastError=true)]public static extern int SampleFunction(int lData);

These types of issues can sometimes be difficult to notice.  For example, in our simple unmanaged (native) function, we are simply returning the value passed as the argument.  If the value happens to be one that can fit within a 32 bit integer, the failure may go unnoticed.  If the value exceeds that which will fit in a 32 bit integer and the code does not validate the data returned from the call, the failure may also go unnoticed.

The moral of this story is that it is very important to validate the behavior of all P/Invoke calls by examining the returned data or any other means appropriate.  I also highly recommend enabling Interop logging and examining the P/Invoke signature data in the log file as a standard part pre-checkin unit testing and release criteria if your application makes calls to unmanaged functions.

Take care,
-- DK

Edit: Fix error in registry steps.
Edit: Fix UseApp steps

Disclaimer(s):
This posting is provided "AS IS" with no warranties, and confers no rights.

Comments

  • Anonymous
    February 07, 2006
    4 Enable interop logging
    In the Logging key (from step 3) expand Interop
    If this key does not exist, you will need to create it
    Right click on Interop
                  ~~~~~~~??
    Select New > Key
    Type "Interop" (without quotes)
    Click OK
  • Anonymous
    February 07, 2006
    YuYuan,
    Thanks for catching that mistake.  I have updated the registry steps with the fix.
    -- DK
  • Anonymous
    February 14, 2006
    you are welcome.I like this post:-)
  • Anonymous
    March 08, 2006
    It doesn't sem to be creating any log file. I have updated the registry with the keys . Is there something which I am missing
  • Anonymous
    March 09, 2006
    When using the UseApp value in the Interop key, the log file will be created in the same folder as the executable.  If the application does not call an unmanaged function (via P/Invoke or COM interop), the file will be empty.

    I have experienced the file not being created if one of the following is true:

    1. The application is running on version 1 of the .NET Compact Framework.
    If your application was built for version 1, you may need to use an application configuration file to cause it to run using version 2.  Please see the following for more information: http://blogs.msdn.com/davidklinems/archive/2005/11/09/491113.aspx

    2. Logging is not enabled.
    For Interop logging to occur, both the LoggingEnabled and LoggingInteropEnabled values must be set to 1.

    3. There is a type-o in the registry.
    I find this is <i>my</i> most common issue with logging.  If any of the key or value names are mistyped, logging will not be enabled.

    I hope this helps!
    -- DK
  • Anonymous
    December 28, 2006
    I started this series last year and thought I would continue the tradition with my best of 2006 collection.