Export (0) Print
Expand All
Debugging: Root Out Elusive Production Bugs with These Effective Techniques
Smart Tags: Simplify UI Development with Custom Designer Actions in Visual Studio
Ten Essential Tools: Visual Studio Add-Ins Every Developer Should Download Now
XML Comments: Document Your Code in No Time At All with Macros in Visual Studio
Expand Minimize

Troubleshooting .NET Interoperability

Visual Studio .NET 2003
 

Bob Burns
Visual Studio Team
Microsoft Corporation

May 2002

Summary: This article discusses some of the issues that you may encounter when developing an application that uses both managed and unmanaged code. It discusses how to include existing COM objects in managed code and how to access Windows APIs from managed code. (15 printed pages)

Requires

  • Some experience with COM and using COM with Visual Basic .NET.
  • Visual Studio .NET and Visual Basic 6.0 installed.

Contents

Introduction
Using COM Objects Within Visual Basic .NET
Correcting Common Interop Assembly Problems
API Declarations
Module level COM methods
Unhandled Errors in Event Handlers
ActiveX Control Issues
Conclusions

Introduction

Visual Studio® .NET represents a major change in the way that developers create and run applications by introducing the idea of managed code that targets the common language runtime. Managed code provides many advantages including automatic memory management, attributed programming, and a common type system. Unfortunately, the very features that make managed code so powerful also make it fundamentally different from unmanaged code such as Windows APIs and COM objects. Although Visual Studio .NET makes it relatively easy to use and create unmanaged objects, occasionally things can get complicated. The process of getting managed and unmanaged objects to work together is called interoperability, but it is commonly called interop.

The term interop covers three separate issues when associated with Visual Studio .NET:

  • Using COM objects within managed code.

    This is the case when you want to use existing COM objects with managed code such as Visual C#™ or Visual Basic® .NET.

  • Creating COM objects using Visual Studio .NET.

    This is the case when you want the ease of development that Visual Studio .NET provides, but you need to provide traditional COM objects for clients that may not have the .NET Framework installed.

  • Using Windows® APIs from managed code.

    Although the .NET Framework provides managed code replacements for many Windows APIs, occasionally you may need to call a function in a dynamic link library (dll).

Each type of interop has unique challenges. For example, difficulties using COM objects from managed code often are associated with the use of interop assemblies. On the other hand, problems with Windows APIs are usually associated with using the correct MarshalAs attributes. This article will concentrate on using COM objects and Windows APIs.

Using COM Objects Within Visual Basic .NET

COM objects are everywhere in applications today. Managed code in the form of .NET assemblies will eventually replace most COM objects, but until that happens you may need to use and create COM objects.

At first glance, using COM objects in managed code seems no different from using them with previous versions of Visual Basic. You add COM objects to your projects by selecting Add Reference from the Project menu, and selecting the name of your COM component from the COM tab. What is not as obvious is that behind the scenes, Visual Basic now uses interop assemblies to translate between the representation of data in .NET and the representation of data in COM.

Interop assemblies, which are .NET assemblies that act as a bridge between managed and unmanaged code, map COM object members to equivalent .NET managed members. When you add a reference to a COM object, the global assembly cache is checked to see if an appropriate primary interop assembly is available. Primary interop assemblies are interop assemblies that are tailored to work with specific COM objects. Primary interop assemblies are strongly signed, to ensure the identity of the file, and optimized to make a seamless boundary between managed and unmanaged code. Whenever possible you should try to use primary interop assemblies when using COM objects with managed code. Unfortunately, there are not many available primary interop assemblies. If the correct primary interop assembly cannot be found for the COM object you are using, Visual Studio .NET will make an interop assembly for you if you are working in the integrated development environment (IDE). You can also create interop assemblies from the command prompt using the Tlbimp utility.

Visual Studio .NET usually does a good job of creating interop assemblies, but sometimes you need to optimize the code it generates. Problems with interop assemblies occur because type libraries do not always have all the necessary information to handle some kinds of data marshaling. If an interop assembly does not seem to work correctly, or if you notice performance problems when accessing a COM object using an interop assembly, you may need to manually edit the interop assembly to specify data marshaling. The ability to modify interop assemblies also makes it possible to call methods that are not compatible with OLE automation and that cannot be called using Visual Basic 6.0.

Modifying interop assemblies

Interop assemblies, like all .NET assemblies, exist as binary code within a file. However, you can use the intermediate language disassembler, ildasm, to disassemble them to a human readable format called Microsoft intermediate language (MSIL). To customize an interop assembly, disassemble it using ildasm, edit the intermediate language code, and then reassemble it using the ilasm assembly utility.

To find the name and location of the interop assembly

  • Right-click the name of the COM library in the Solution Explorer reference list and click Properties.

    The name and location of the interop assembly is stored in the path property for the COM object.

To modify the interop assembly

  1. On the Windows Start menu, point to Microsoft Visual Studio .NET, point to Visual Studio .NET Tools, and click Visual Studio .NET Command Prompt.
  2. Change directories to the location of the interop assembly you want to modify.
  3. Disassemble the dll that contains the interop assembly using the ildasm utility.

    The following example shows to disassemble an interop assembly named interop.comclass.dll to intermediate code in a file named interop.ComClass.il

    ildasm interop.comclass.dll /out=interop.ComClass.il
    
  4. Using a text editor such as Notepad or Visual Studio, modify the MSIL as appropriate and save the changes.

To reassemble the interop assembly

  • At the Visual Studio .NET Command Prompt, type the command to reassemble the MSIL file to an assembly using the ilasm utility.

    The following example shows how to reassemble intermediate code named interop.comclass.il to an interop assembly named interop.NewIA.dll

    ilasm interop.comclass.il /dll /output=interop.NewIA.dll
    

To add a reference to the new interop assembly

  1. Remove the reference to the old interop assembly.
  2. On the Project menu, click Add Reference, click the .NET tab, click the Browse button, and select the name of the new interop assembly.

Correcting Common Interop Assembly Problems

The following section describes some common problems with interop assemblies and how to make them work correctly.

Although COM objects can be created using several methods, the description of the COM objects in this section is presented using Interface Definition Language (IDL). Although IDL and MSIL are similar, they have different syntaxes and data types. The table in the Appendix of this article lists conversions between the data types used in Visual Basic 6.0, Visual Basic .NET, IDL, and MSIL.

Conformant C-Style Arrays

The following Interface Definition Language (IDL) declaration shows a C-style array.

HRESULT ConformantArray([in] int cElems, [in, size_is(cElems)] int 
aConf[]);

Tlbimp.exe imports the second parameter as a reference to the integer and not as a managed array. You can adjust the parameter by editing the MSIL as follows:

Search the MSIL for the following code:

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32& aConf) runtime managed internalcall

Replace it with the following code:

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32[] marshal([+0]) aConf) runtime managed internalcall

In/Out C-Style Arrays

The following IDL declaration shows an In/Out C-style array.

HRESULT InOutArray([in, out] int* pcElems, [in, out, size_is(*pcElems)] 
int** ppInOut);

In this case, the array can be resized and the new size can be passed back. Tlbimp.exe imports the second parameter as an IntPtr type. Although you can still call this method from managed code, to resize the array you must edit the MSIL and use methods from the Marshal class to manually handle the allocation and release of memory.

Search the MSIL for the following code:

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int ppInOut) runtime managed internalcall

Replace it with the following code:

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int& ppInOut) runtime managed internalcall

Multidimensional C-Style Arrays

The following IDL declaration shows a two-dimensional, C-style array.

HRESULT TwoDimArray([in] int cDim, [in, size_is(cDim)] int aMatrix[][3]);

Tlbimp.exe imports the second parameter as an IntPtr type and not as a managed multidimensional array. You can adjust the parameter by editing the MSIL.

Search the MSIL for the following code:

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] native int aMatrix) runtime managed internalcall

Replace it with the following code:

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] int32[,] marshal([+0]) aMatrix) runtime managed internalcall

Non-Zero Lower Bound SAFEARRAY

The following IDL declaration shows a SAFEARRAY parameter.

HRESULT InSArray([in] SAFEARRAY(int) *ppsa);

This SAFEARRAY represents a non-zero lower bound array. In managed code, such arrays are represented by the System.Array type. However, by default, the importer converts all SAFEARRAY parameters to references to managed arrays. You have two options for changing the default behavior:

  • Import all the arrays in a type library as System.Array types by using Tlbimp.exe with the /sysarray switch.

    -or-

  • Import specific parameters as System.Array types by manually editing the MSIL, as the following example shows.

Search the MSIL for the following code:

.method public hidebysig newslot virtual 
instance void  InSArray([in] int32[]&  marshal( safearray int) ppsa) runtime managed internalcall

Replace it with the following code:

.method public hidebysig newslot virtual 
instance void  InSArray(class [mscorlib]System.Array& marshal( safearray) 
ppsa) runtime managed internalcall

Preserve Signature

The following IDL declaration shows a COM method signature.

HRESULT TestPreserveSig2([in] int inParam, [out,retval] int* outParam);

Tlbimp.exe changes the signatures of COM methods. Parameters marked with [out, retval] in IDL become return values of managed methods. All HRESULT values that indicate failure are transformed to managed exceptions. It is sometimes necessary to preserve the original COM method signature, such as when the method returns something other than success HRESULTs. The following managed representation shows an example of a signature that you can modify.

Search the MSIL for the following code:

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam) runtime managed internalcall

Replace it with the following code:

.method public hidebysig newslot virtual 
instance string TestPreserveSig2([in] int32 inParam, [out] string& outParam) runtime managed internalcall preservesig

Passing Null Instead of a Reference to a Value Type

The following IDL declaration shows an IDL parameter declaration for a pointer to a structure.

HRESULT TestPassingNull([in, unique] Point* refParam);

Tlbimp.exe imports the parameter as a reference to the value type Point. In C# and Visual Basic .NET, a null value cannot be passed as parameter when a reference to a value type is expected. If the COM function requires a null parameter, you can alter the signature by editing the MSIL.

Search the MSIL for the following code:

.method public hidebysig newslot virtual 
instance void  TestPassingNull(
[in] valuetype MiscSrv.tagPoint& refParam) 
runtime managed internalcall

Replace it with the following code:

.method public hidebysig newslot virtual 
instance void  TestPassingNull([in] native int) runtime managed internalcall

Windows APIs

Windows API calls fall under a category of interoperability called platform invoke, or PInvoke. Windows APIs are not COM objects and can be difficult to use from any version of Visual Basic.

The main problem when using Windows APIs involves sending data to and from unmanaged code, a process called interop data marshaling. Windows APIs use several different representations for elements, such as arrays and strings, some of which do not have equivalent representations in either COM or the .NET Framework.

To successfully call a Windows API from managed code, you must understand:

  • The kind of data the API uses.
  • How the API specifies the data's format in the unmanaged code of the API call.
  • How to specify the data's format in the managed code of your application.

Before you start writing code, you must determine the name of the function you want to call, its arguments, its argument types, its return value, and the name and location of the DLL that contains it. You can find extensive documentation about Windows APIs in the Platform SDK Windows API. For more information about the constants that Windows APIs use, examine the header files, such as Windows.h, included with the Platform SDK. When this article was written, the Platform SDK documentation still targeted C++ developers prior to the release of Visual Studio .NET, so you must adjust data types accordingly.

Traditionally, Visual Basic programmers have used Declare statements to access static entry points of DLLs. Visual Basic .NET and C# provide another way to access APIs by using the DLLImport attribute. The examples in this paper use Declare statements but there is no advantage to using either style, and you can easily reformat them to use the DLLImport attribute if necessary. For more information, see Walkthrough: Calling Windows APIs.

API Declarations

The following example is based on an actual problem from a discussion group. The developer reported that a structure passed to the GetVersionEx API contained no information when the call returned. The solution involved using the Marshal.SizeOf method and the MarshalAs attribute to explicitly specify how structure members are marshaled.

Imports System.Runtime.InteropServices
Module Module1
   Public Declare Auto Function GetVersionEx Lib "kernel32.dll" 
(<MarshalAs(UnmanagedType.Struct)> ByRef osinfo As OSVERSIONINFOEX)
As Int32

   ' Structure definition
   <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
   Public Structure OSVERSIONINFOEX
      Public dwOSVersionInfoSize As Int32
      Public dwMajorVersion As Int32
      Public dwMinorVersion As Int32
      Public dwBuildNumber As Int32
      Public dwPlatformId As Int32
      <MarshalAs(UnmanagedType.ByValTStr, _
         SizeConst:=128)> Public szCSDVersion As String
      Public wServicePackMajor As Int16
      Public wServicePackMinor As Int16
      Public wSuiteMask As Int16
      Public wProductType As Byte
      Public wReserved As Byte
   End Structure

   Sub testAPI()
      Dim osverinfo As OSVERSIONINFOEX
      Dim result As Integer
      osverinfo.dwOSVersionInfoSize = Marshal.SizeOf(osverinfo)
      result = GetVersionEx(osverinfo)
      MsgBox(osverinfo.dwBuildNumber)
   End Sub

End Module

This version works as intended but a few parts could use some explanation. Some Win32 API calls, such as GetVersionEx, expect you to provide the unmanaged size and location of fields within the structure you are using. Because Visual Studio .NET optimizes the way data is stored, the unmanaged representation of the structure does not always match the managed representation. The StructLayout attribute prevents Visual Studio .NET from optimizing by rearranging the way the structure fields are arranged in memory. Such optimization, which is great when working with managed code, could cause problems if the structure's fields are not at the offsets that the API call expects. The Marshal.SizeOf method returns the true size of the structure as it is represented by unmanaged code. This is often different from the optimized size reported by functions such as LEN. The MarshalAs attribute is required to specify the size of the szCSDVersion field on the unmanaged side. As with many PInvoke declarations, the secret to success involves keeping track of how data is represented in both managed and unmanaged code.

Using the Upgrade Wizard to fix declare statements

If you are not sure how to modify declare statements that work with Visual Basic 6.0, you can sometimes use the Upgrade Wizard to suggest changes. This approach will often get you closer to your goal, although you may also need to add MarshalAs attributes.

To use the Upgrade Wizard to update Declare statements

  1. Create a Visual Basic 6.0 project that uses the declare statement you want to use in Visual Studio .NET.
  2. Change the code where appropriate to make it upgradeable to Visual Basic .NET syntax.

    For example, the code Dim Buffer As String * 25 should be changed to the following code that is compatible with Visual Basic .NET.

    Dim Buffer As String
    Buffer = String$(25, " ")
    
  3. Test the Visual Basic 6.0 code to make sure it works.
  4. Save the project and exit Visual Basic 6.0.
  5. Start Visual Studio .NET and open the existing Visual Basic 6.0 project.

    Visual Studio .NET starts the Upgrade Wizard.

  6. When the wizard is done, test the upgraded code.

The Upgrade Wizard often does everything required to make the Declare statement work. For example, if you start with the Visual Basic 6.0 code shown next, the upgrade wizard will create the code in the following section.

Private Declare Function GetUserName Lib "advapi32.dll" _
                Alias "GetUserNameA" (ByVal lpBuffer As String, _
                                      ByRef nSize As Long) As Long

Function UserName() As String
    Dim Ret As Long
    Dim Buffer As String
    Buffer = String$(25, " ")
    Ret = GetUserName(Buffer, 25)
    UserName = Left$(Buffer, InStr(Buffer, Chr(0)) - 1)
End Function
'----------------  End of VB 6 code -------------------

The following section shows the code generated by the Upgrade Wizard.

Option Strict Off
Option Explicit On
Module GetName
    Private Declare Function GetUserName Lib "advapi32.dll" _
       Alias "GetUserNameA" (ByVal lpBuffer As String, _
       ByRef nSize As Integer) As Integer

   Function UserName() As String
      Dim Ret As Integer
      Dim Buffer As String
      Buffer = New String(" ", 25)
      Ret = GetUserName(Buffer, 25)
      UserName = Left(Buffer, InStr(Buffer, Chr(0)) - 1)
   End Function
End Module

This code works correctly because the Upgrade Wizard made a few changes. For example, the return value variable Ret and the nSize parameter changed from Long to Integer because Visual Basic .NET uses 32-bit integers while Visual Basic 6.0 uses 16-bit integers.

Module level COM methods

Most COM objects are used by creating an instance of a COM class using the New keyword and then calling methods of the object. One exception to this rule involves COM objects that contain "AppObj" or "GlobalMultiUse" COM classes. Such classes are similar to module level methods in Visual Basic .NET classes. Visual Basic 6.0 implicitly creates instances of such objects for you the first time you call one of their methods. For example, in Visual Basic 6.0 you can add a reference to the Microsoft DAO 3.6 Object Library and call the DBEngine method without first creating an instance:

Dim db As DAO.Database
' Open the database
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object

Visual Basic .NET requires that you always create instances of COM objects before you can use their methods. To use these methods in Visual Basic .NET, declare a variable of the desired class and use the new keyword to assign the object to the object variable. The Shared keyword can be used when you want to make sure that only one instance of the class is created.

' Declarations level variable.
Shared DBEngine As New DAO.DBEngine()

   Sub DAOOpenRecordset()
      Dim db As DAO.Database
      Dim rst As DAO.Recordset
      Dim fld As DAO.Field
      ' Open the database
      db = DBEngine.OpenDatabase("C:\nwind.mdb")

      ' Open the Recordset
      rst = db.OpenRecordset _
        ("SELECT * FROM Customers WHERE Region = 'WA'", _
         DAO.RecordsetTypeEnum.dbOpenForwardOnly, _
         DAO.RecordsetOptionEnum.dbReadOnly)
      ' Print the values for the fields in the debug window.
      For Each fld In rst.Fields
         Debug.WriteLine(fld.Value & ";")
      Next
      Debug.WriteLine("")
      ' Close the recordset
      rst.Close()
   End Sub

Unhandled Errors in Event Handlers

One common interop problem involves errors in event handlers that handle events raised by COM objects. Such errors are ignored unless you specifically check for errors using On Error or Try...Catch...Finally statements. For example, the following example is from a Visual Basic .NET project that has a reference to the ADODB COM object.

' To use this example, add a reference to ADODB from the 
' COM tab of the project references page.
Dim WithEvents cn As New ADODB.Connection()
Sub ADODBConnect()
   cn.ConnectionString = _
   "Provider=Microsoft.Jet.OLEDB.4.0;" & _
   "Data Source=C:\NWIND.MDB"
   cn.Open()
   MsgBox(cn.ConnectionString)
End Sub

Private Sub Form1_Load(ByVal sender As System.Object, _
                       ByVal e As System.EventArgs) Handles MyBase.Load
   ADODBConnect()
End Sub

Private Sub cn_ConnectComplete(ByVal pError As ADODB.Error, _
                               ByRef adStatus As ADODB.EventStatusEnum, _
                               ByVal pConnection As ADODB.Connection) _
                               Handles cn.ConnectComplete
'  This is the event handler for the cn_ConnectComplete event raised 
'  by the ADODB.Connection object when a database is opened.
   Dim x As Integer = 6
   Dim y As Integer = 0
   Try
      x = x / y ' Attempt to divide by zero.
      ' This procedure would fail silently without exception handling.
   Catch ex As Exception
      MsgBox("There was an error: " & ex.Message)
   End Try
End Sub

This example raises an error as expected. However, if you try the same example without the Try...Catch...Finally block, the error is ignored as if you used the OnError Resume Next statement. Without error handling, the division by zero silently fails. Because such errors never raise unhandled exception errors, it is vital that you use some form of exception handling in event handlers that handle events from COM objects.

Understanding COM interop errors

Without error handling, interop calls often generate errors that provide little information. Whenever possible, use structured error handling to provide more information about problems when they occur. This can be particularly helpful when debugging applications For example:

Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try

You can find out information such as the error description, HRESULT, and the source of COM errors by examining the contents of the exception object.

ActiveX Control Issues

Most ActiveX controls that work with Visual Basic 6.0 work with Visual Basic .NET without trouble. The main exceptions are container controls, or controls that visually contain other controls. Some examples of older controls that do not work correctly with Visual Studio .NET are:

  • Microsoft Forms 2.0 Frame control
  • Up-Down control, also known as the spin control
  • Sheridan Tab Control

There are only a few workarounds for unsupported ActiveX control problems. You can migrate existing controls to Visual Studio .NET if you own the original source code. Otherwise, you can check with software vendors for updated .NET compatible versions of controls to replace unsupported ActiveX controls.

Passing ReadOnly Properties of Controls ByRef

Visual Basic .NET sometimes raises COM errors such as "Error 0x800A017F CTL_E_SETNOTSUPPORTED" when you to pass ReadOnly properties of some older ActiveX controls as ByRef parameters to other procedures. Similar procedure calls from Visual Basic 6.0 do not raise an error, and the parameters are treated as if you passed them by value. The error message you see in Visual Basic .NET is the COM object reporting that you are attempting to change a property that does not have a property Set procedure.

If you have access to the procedure being called, you can prevent this error by using the ByVal keyword to declare parameters that accept ReadOnly properties. For example:

Sub ProcessParams(ByVal c As Object)
   'Use the arguments here.
End Sub

If you do not have access to the source code for the procedure being called, you can force the property to be passed by value by adding an extra set of brackets around the calling procedure. For example:

Sub PassByVal ()
' The extra set of parens around the arguments
' forces them to be passed by value.
   ProcessParams((Me.AxListView1.ListItems))
End Sub

Conclusions

  • COM interop is a great way to retain your investment in unmanaged code while you transition to managed code.
  • Overall, Visual Studio .NET is easy to use with unmanaged code.
  • Even though problems may arise, most issues can be solved.
  • You can edit interop assemblies to fix interop problems or to improve performance.
  • The key to successfully calling a Windows API from managed code involves understanding the kind of data the API uses.
  • The MarshalAs attribute can be used to specify how data is sent to a function when accessing static entry points of DLLs such as Windows APIs.
  • You can use the Upgrade Wizard to suggest changes to Declare statements that work correctly in Visual Basic 6.0.
  • Always use some form of error handling with the event procedures for COM objects.
  • Use caution when passing ReadOnly properties by reference to other procedures.

Appendix

Understanding data types and their equivalents is important when using COM interop. The following table compares data types as described in the Interface Definition Language (IDL), which is commonly used to define COM interfaces, and the equivalent Visual Basic 6.0, Visual Basic .NET, .NET Framework, and Microsoft Intermediate Language (MSIL) data types.

IDL Visual Basic 6.0 Visual Basic .NET .NET Framework MSIL
char N/A N/A* System.SByte int8
short Integer Short System.Int16 int16
long, int, HRESULT and SCODE Long Integer System.Int32 int32
int64 N/A Long System.Int64 int64
unsigned char Byte Byte System.Byte unsigned int8
unsigned short N/A N/A* System.Uint16 unsigned int16
unsigned int N/A N/A* System.UInt32 unsigned int32
uint64 N/A N/A* System.UInt32 unsigned int64
float Single Single System.Single float32
double Double Double System.Double float64
BSTR, LPSTR, and LPWSTR String String System.String String
VARIANT_BOOL Boolean Boolean System.Boolean bool
DATE* Date Date System.DateTime Valuetype [mscorlib]System.DateTime
GUID N/A N/A* System.GUID Valuetype [mscorlib]System.GUID
DECIMAL N/A Decimal System.Decimal Valuetype [mscorlib]System.Decimal
CURRENCY Currency Decimal System.Decimal Valuetype [mscorlib]System.Decimal
VARIANT Variant Object System.Object object
IUnknown* N/A Object System.Object object
IDispatch* Object Object System.Object object
void* Any N/A System.IntPtr native int
IEnumVARIANT N/A N/A* System.Collections.IEnumerator N/A

* Although these types are not intrinsic to Visual Basic .NET you can use equivalent types, such as System.SByte from the System namespace.

Additional Resources

Troubleshooting Interoperability
Covers some common issues associated with COM interop.
Introduction to COM Interop
Provides an overview of COM interop.
Creating a Wrapper Manually
Explains how to create a runtime callable wrapper, which is a proxy that the common langauge runtime uses to expose COM objects.
Interop Marshaling Overview
Provides background on the concept of interop marshaling.
Type Library Importer (Tlbimp.exe)
Explains the purpose of the Type Library Importer and lists its arguments and options.
Type Library Exporter (Tlbexp.exe)
Explains the purpose of the Type Library Exporter and lists its arguments and options.
Default Marshaling for Arrays
Contains details about managed and unmanaged arrays, passing array parameters to .NET code, and passing arrays to COM.
Editing An Interop Assembly
Explains how to edit an interop assembly after you have used the Type Library Importer.
MSIL Disassembler (Ildasm.exe)
Explains the purpose of the MSIL Disassembler and lists its options.
Show:
© 2014 Microsoft