This information will be included on MSDN soon...

How to: Create Log Files

Introduction

You can create log files with diagnostic information about interoperability, loading the application, and networking. You can enable logging by setting the registry keys.

By default, log files are written to the same directory containing the application being diagnosed. However, you can you can specify a path and other options with registry keys as follows:

  • Use an alternate path to write the log files. This requires privileged access to the secure registry.
  • Include the application name in the log file name.
  • Include the process ID in the log file name.

A log file has the following parts: "netcf_" + (Application name) + (component) + (Process ID) + ".log".
[component] corresponds to the type of logging information, either Interop, Loader, or Network.
[Application name] is optional and [process ID] are optional and are specified with registry settings.

For example, a Loader log file for "MyApp.exe" with the application name and process ID would be: netcf_MyApp_Loader_2066923010.log

Procedures

To enable logging:
Set the Enabled key value 1: HKLM\Software\Microsoft\.NETCompactFramework\Diagnostics\Logging\Enabled
This key value must be set to enable the three types of logging: Interop, Loader, and Networking. Note that the sub keys under Logging do not exist by default.
You can turn off all logging by setting this value to zero.

To specify a path for the log file (optional):
Set the Path key value to a string: HKLM\Security\.NETCompactFramework\Diagnostics\Logging\Path
This key is only accessible by applications that can write to the secure registry. If a path is not specified, the log file is written to the same directory containing the application.

To include the application in the name (optional):
Set the UseApp key value to 1: HKLM\Software\Microsoft\.NETCompactFramework\Diagnostics\Logging\UseApp
This key is useful if you want to run multiple applications and get separate log files. If there are two applications writing log files to the same directory, the older log file will always get overwritten with the newer log file when the second application is run. This key can be used as a differentiator for the log file.

To include the process ID in the name (optional):
Set the UsePid key value to 1: HKLM\Software\Microsoft\.NETCompactFramework\Diagnostics\Logging\UsePid
This key is useful if you want to run the same application but have separate logs. This adds the process ID to the log file name, so that each run of the same application creates a new log file with a different name.

To log events as they occur (optional):
Set the Flush key value to 1: HKLM\Software\Microsoft\.NETCompactFramework\Diagnostics\Logging\Flush
This value causes the common language runtime to write log events to the log file as they occur instead of keeping them in the buffer and writing then when the buffer is full. Flushing negatively affects performance of the application and might modify timing of the application slightly since every piece of logging information is written out immediately, but it can be useful to diagnose problems related to application crashes or other errors where you might want to get the last few logs that resulted in the crash. If this key is not present or not set, then the default operation is to not flush.

To enable Interop logging:
Set the Enabled key value to 1: HKLM\Software\Microsoft\.NETCompactFramework\Diagnostics\Logging\Interop\Enabled

To enable Loader logging:
Set Enabled value to 1 to enable Loader logging, or set to 2 to enable Loader and GAC related logging: HKLM\Software\Microsoft\.NETCompactFramework\Diagnostics\Logging\Loader\Enabled

To enable Networking logging:
Set the Enabled value to 1: HKLM\Software\Microsoft\.NETCompactFramework\Diagnostics\Logging\Networking\Enabled
The networking log file is binary and cannot be read without the viewer, which is not currently available.

Interop Log Files

The output for interop logging consists of the signatures of interop function calls as they occur at run time, as well as any error messages.

Function Signatures

The signatures for both managed-to-native and native-to-managed calls are logged, and include the following types of calls:

  • Platform invoke calls.
  • COM vtable and Dispatch calls.
  • Delegate callbacks.

These function signatures can help you troubleshoot problems when calling or returning from an interop function call, such as when a parameter is not initialized as expected or when the program terminates unexpectedly.

The output for a function signature entry consists of three lines for each interop call:

Line 1) The first line represents flags identifying the type of function call made, and has one or more of the following elements:

[pinvokeimpl] - A managed-to-native call that uses the System.Runtime.InteropServices.DllImportAttribute attribute. 
[Ctor] - A constructor for an interop assembly class, generated by the Type Library Importer (Tlbimp.exe).
[preservesig] - The managed and native functions are assumed to 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.

Line 2) 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 describes the PInvoke or com interface method. For native-to-managed function calls, this line identifies the managed function that is being called from native code.

Line 3) 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 the correct types are specified by the System.Runtime.InteropServices.DllImportAttribute or in the COM interface signature definition. Failure to specify the correct types is a common error that can result in unexpected behavior because the function called is executed with incorrect parameter values. 
Every type has a default marshaling type. Note that the marshaling behavior of a managed type can differ between COM calls and System.Runtime.InteropServices.DllImportAttribute calls or delegate callback calls. You can use the System.Runtime.InteropServices.MarshalAsAttribute attribute to specify a marshaling type other then the default. You must also use the ref keyword to identify parameters that are expected to be pointers in native code.

The following example shows a signature entry for a PInvoke call: 

1) [pinvokeimpl][preservesig]
2) bool  PlatformDetector::SystemParametersInfo(uint , uint , System.Text.StringBuilder , uint );
3) BOOLEAN (I1_WINBOOL_VAL) SystemParametersInfo(unsigned int (U4_VAL) , unsigned int (U4_VAL) , WCHAR * (STRINGBUILDER_LPWSTR) , unsigned int (U4_VAL) );

The following example shows a signature entry for a delegate callback.

1) [preservesig][delegate]
2) int  WndProc::Invoke(WndProc , IntPtr , uint , uint , int );
3) int (I4_VAL) (*)(INT_PTR (I_VAL) , unsigned int (U4_VAL) , unsigned int (U4_VAL) , int (I4_VAL) )

The following example shows a signature entry for a native-to-managed COM function call, where the runtime returns a failure HRESULT in the event of a managed exception.

1) [no flags]
2) int  N2MDualComponentImp.IN2MDualInterface::GetInt(N2MDualComponentImp.IN2MDualInterface This);
3) HRESULT GetInt(IN2MDualInterface *(INTF_VAL) this, [retval] int (I4_VAL) retval);

Error Messages

You should search the interop log file for the keywords ‘ERROR’ and ‘WARNING’. Some situations can cause error messages to be recorded in the log file, which can be especially useful when 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 diagnose the following issues:

Native-to-managed function calls:

  • Calling runtime COM interfaces. An HRESULT error can be returned to native code when a runtime-implemented COM interface function is called. There are several runtime-implemented interfaces, including IUnknown, IDispatch, IConnectionPointContainer, IEnumConnectionPoints, and IConnectionPoint, that native code can call into by using a managed object marshaled as a COM interface. When an error is returned to native code from a function call into one of these interfaces, the runtime prints out an appropriate error message giving the HRESULT and any additional relevant information.
  • Native code expecting to use functionality that is unsupported, such as the CCW (COM callable wrapper) implementation of IDispatch::GetTypeInfo. 
  • Unimplemented interfaces. Native code may receive an E_NOINTERFACE error from a CCW implementation of 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:

  • Managed exceptions 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 and returns this back to native code. However, for delegate callbacks and COM calls that do not expect HRESULT return values, there is no way to ensure native code is made aware of the error, and you may see unexpected behavior as a result. The interop log will contain an error message when an exception happens during a native-to-managed interop function call, helping 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 System.Runtime.InteropServices.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 an System.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 converting a native object into a managed object.
  • Determining what causes an exception when creating 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.
  • There are differences between .NET Compact Framework implementation of COM interoperability and that of the full .NET Framework. The following situations will cause a managed exception under the .NET Compact Framework:
    • Creating a CCW (COM callable wrapper) containing an interface without a specified GUID.
    • Instantiating a class that inherits from an interop assembly class.
    • Creating a CCW containing a nongeneric interface with a generic method.
  • Runtime callable wrappers (RCWs) are usually cleaned up upon finalization, but you can also use the System.Runtime.InteropServices.Marshal.ReleaseComObject(System.Object) or System.Runtime.InteropServices.Marshal.FinalReleaseComObject(System.Object) method to release the RCW associated with an object. If you are using these advanced options to manage the lifetime of your objects and you attempt 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.

[Author: Katie Blanch]

Disclaimers:
This posting is provided "AS IS" with no warranties, and confers no rights.
Some of the information contained within this post may be in relation to beta software.  Any and all details are subject to change.