The output for interop logging consists of the signatures of the interop function calls as they occur at run time, with any error messages.
The .NET Compact Framework version 3.5 includes enhanced interop logging support, which is described in the "Deep Marshaling" section later in this topic.
Function Signatures
The signatures for both managed-to-native and native-to-managed calls are logged, and include the following types of calls:
Interop logging can help you troubleshoot problems when you call or return from an interop function call, such as when an incorrect parameter type is marshaled or when the program terminates unexpectedly.
The output for a function signature entry consists of three lines for each interop call. The first line provides flags that identify the type of function call made, and includes one or more of the following elements:
- [pinvokeimpl]
Identifies a managed-to-native call that uses the DllImportAttribute attribute.
- [Ctor]
Identifies a constructor for an interop assembly class, generated by the Type Library Importer (Tlbimp.exe).
- [preservesig]
Assumes that the managed and native functions have the same signature, with no translation from HRESULT to exception enforced by the runtime.
- [delegate]
Indicates that the function is a native-to-managed delegate callback. The delegate acts as a function pointer in native code.
The second line of the interop log file represents the managed signature. For managed-to-native function calls, this line identifies the managed function that calls the native code. For native-to-managed function calls, this line identifies the managed function that is being called from native code.
The third line represents the native signature, as expected by the runtime. This line identifies data types for each parameter and provides information about how the managed object data is marshaled. The runtime assumes that the correct types are specified by the DllImportAttribute attribute or in the COM interface signature definition. Failure to specify the correct types is a common error that can cause unexpected behavior, because the function will be executed with incorrect parameter values.
Note: |
|---|
Specifying an incorrect parameter type may result in a NotSupportedException or a native exception. To help isolate the failure, change parameter types to known supported types or to an IntPtr. |
Every type has a default marshaling type. Note that the marshaling behavior of a managed type can be different for COM calls and DllImportAttribute or delegate callback calls. You can use the MarshalAsAttribute attribute to specify a marshaling type other than the default. You must also use the ref keyword to identify any parameter that represents a pointer to a value type or a pointer to a pointer, for a reference type.
The following table shows interop logging of a platform invoke.
Line number and description | Log entry |
|---|
1 - Type of function call | [pinvokeimpl][preservesig] |
2 - Managed signature | bool PlatformDetector::SystemParametersInfo(uint , uint , System.Text.StringBuilder , uint ); |
3 - Native signature | BOOLEAN (I1_WINBOOL_VAL) SystemParametersInfo(unsigned int (U4_VAL) , unsigned int (U4_VAL) , WCHAR * (STRINGBUILDER_LPWSTR) , unsigned int (U4_VAL) ); |
The following table shows interop logging of a delegate callback.
Line number and description | Log entry |
|---|
1 - Type of function call | [preservesig][delegate] |
2 - Managed signature | int WndProc::Invoke(WndProc , IntPtr , uint , uint , int ); |
3 - Native signature | int (I4_VAL) (*)(INT_PTR (I_VAL) , unsigned int (U4_VAL) , unsigned int (U4_VAL) , int (I4_VAL) ) |
The following table shows interop logging of a native-to-managed COM function call, where the runtime returns a failure HRESULT if a managed exception occurs.
Line number and description | Log entry |
|---|
1 - Type of function call | (no flags) |
2 - Managed signature | int N2MDualComponentImp.IN2MDualInterface::GetInt(N2MDualComponentImp.IN2MDualInterface This); |
3 - Native signature | HRESULT GetInt(IN2MDualInterface *(INTF_VAL) this, [retval] int (I4_VAL) retval); |
Deep Marshaling
The .NET Compact Framework version 3.5 also supports deep marshaling for interop logging. In deep marshaling, information is logged about marshaled objects that are contained in structures or in reference types.
The following log output shows an example of a platform invoke call that uses marshaled objects contained in a structure. The first line of the deep marshaling section specifies why the deep marshaler was called. In this example, it was called to compute the size of the structure. The log shows the data type and the size, in bytes, of each object. The index values (for example, 0004) represent the byte offsets for the specified variables.
DEEP MARSHAL: Get size
struct interoplogging.MyStruct
{
0000: Int32 myVar as Int32 (4 bytes)
0004: Int32 myVar2 as Int32 (4 bytes)
0008: String myString as WCHAR[10] (20 bytes)
}
DEEP MARSHAL: Total size = 28 bytes
[pinvokeimpl][preservesig]
void interoplogging.Form1::MyAPI(interoplogging.MyStruct );
void MyAPI(MyStruct (NONBLIT_VALUETYPE_VAL) );
DEEP MARSHAL: Managed -> Native
struct interoplogging.MyStruct
{
0000: Int32 myVar as Int32 (4 bytes)
0004: Int32 myVar2 as Int32 (4 bytes)
0008: String myString as WCHAR[10] (20 bytes)
}
DEEP MARSHAL: Total size = 28 bytes
Error Messages
Some situations and exceptions can cause error messages to be recorded in the log file. These messages can be especially useful when you are investigating issues that involve interoperating with native components and DLLs for which the native source code is not available. You can use error messages to help with the following issues:
Native-to-managed function calls.
Runtime COM interface calls. An HRESULT error can be returned to native code when a COM interface function that is implemented by the runtime is called. There are several runtime-implemented interfaces (including IUnknown, IDispatch, IConnectionPointContainer, IEnumConnectionPoints, and IConnectionPoint) that native code can call by using a managed object marshaled as a COM interface. When a function call returns an error to native code into one of these interfaces, the runtime prints out an appropriate error message that includes the HRESULT and any additional relevant information.
Native code that expects to use functionality that is unsupported, such as IDispatch::GetTypeInfo.
Unimplemented interfaces. Native code may receive an E_NOINTERFACE error from IUnknown::QueryInterface where it expects the managed COM object to have implemented an additional interface. In this case, the GUID of the unimplemented interface is also provided.
Managed exceptions. These can occur inside the managed function call and cause it to return prematurely. When making a COM call, the runtime converts the exception into a failure HRESULT value, which it returns to native code. However, if a delegate callback or COM call does not expect a HRESULT return value, you cannot make sure that it will become aware of the error, and you may see unexpected behavior as a result. The interop log will contain an error message when an exception occurs during a native-to-managed interop function call. This message will help you identify managed functions that need additional error-handling logic to work well with native code. The following factors can cause a managed exception:
Using types in your COM interface definition or DllImportAttribute signature that are not supported by the .NET Compact Framework will cause an exception to occur during the JIT compilation process. There are often alternative options that are acceptable, such as using an IntPtr.
When either the actual object cannot be coerced to the type specified in the signature or the object data cannot be converted to the type requested, an exception is thrown at run time when the function is called. This usually occurs when you convert a native object into a managed object.
Determining what causes an exception when you create a runtime callable wrapper (RCW) or a COM callable wrapper (CCW) is difficult. The interop log file can help determine the cause of these problems when a detailed error message is not provided with the managed exception.
Differences with the .NET Framework
There are differences between the .NET Compact Framework implementation of COM interoperability and that of the full .NET Framework. The .NET Compact Framework does not support the following:
Creating a CCW that contains an interface without a specified GUID.
Creating an RCW for a class that inherits from an interop assembly class.
Creating a CCW that contains a nongeneric interface with a generic method.
RCWs are usually cleaned up upon finalization, but you can also use the ReleaseComObject or FinalReleaseComObject method to release the RCW that is associated with an object. If you are using these advanced options to manage the lifetime of your objects and you try to use the object after it has been freed to make a native COM call, an exception is thrown and the log file contains an error message about the cause of the exception.