相互運用性のトラブルシューティング (Visual Basic)Troubleshooting Interoperability (Visual Basic)

.NET Framework の COM とマネージコードの間で相互運用を行う場合は、次の一般的な問題の1つ以上が発生する可能性があります。When you interoperate between COM and the managed code of the .NET Framework, you may encounter one or more of the following common issues.

相互運用マーシャリングInterop Marshaling

場合によっては、.NET Framework の一部ではないデータ型の使用が必要になることがあります。At times, you may have to use data types that are not part of the .NET Framework. 相互運用機能アセンブリは COM オブジェクトのほとんどの作業を処理しますが、マネージオブジェクトが COM に公開されるときに使用されるデータ型の制御が必要になる場合があります。Interop assemblies handle most of the work for COM objects, but you may have to control the data types that are used when managed objects are exposed to COM. たとえば、クラスライブラリ内の構造体は、 BStr Visual Basic 6.0 以前のバージョンで作成された COM オブジェクトに送信される文字列に対してアンマネージ型を指定する必要があります。For example, structures in class libraries must specify the BStr unmanaged type on strings sent to COM objects created by Visual Basic 6.0 and earlier versions. このような場合は、 MarshalAsAttribute属性を使用して、マネージ型をアンマネージ型として公開することができます。In such cases, you can use the MarshalAsAttribute attribute to cause managed types to be exposed as unmanaged types.

アンマネージコードへの固定長文字列のエクスポートExporting Fixed-Length Strings to Unmanaged Code

Visual Basic 6.0 以前のバージョンでは、文字列は、null 終端文字のないバイトのシーケンスとして COM オブジェクトにエクスポートされます。In Visual Basic 6.0 and earlier versions, strings are exported to COM objects as sequences of bytes without a null termination character. 他の言語との互換性のために、Visual Basic .NET には文字列をエクスポートするときに終了文字が含まれます。For compatibility with other languages, Visual Basic .NET includes a termination character when exporting strings. この非互換性を解決する最善の方法は、終了文字を持たない文字列をまたByteCharの配列としてエクスポートすることです。The best way to address this incompatibility is to export strings that lack the termination character as arrays of Byte or Char.

継承階層のエクスポートExporting Inheritance Hierarchies

COM オブジェクトとして公開されると、マネージクラスの階層はフラット化されます。Managed class hierarchies flatten out when exposed as COM objects. たとえば、メンバーを持つ基本クラスを定義した後、COM オブジェクトとして公開されている派生クラスで基底クラスを継承した場合、COM オブジェクトの派生クラスを使用するクライアントは、継承されたメンバーを使用できません。For example, if you define a base class with a member, and then inherit the base class in a derived class that is exposed as a COM object, clients that use the derived class in the COM object will not be able to use the inherited members. 基底クラスのメンバーは、COM オブジェクトから基底クラスのインスタンスとしてのみアクセスできます。その後、基底クラスも COM オブジェクトとして作成されます。Base class members can be accessed from COM objects only as instances of a base class, and then only if the base class is also created as a COM object.

オーバーロードされたメソッドOverloaded Methods

オーバーロードされたメソッドは Visual Basic で作成できますが、COM ではサポートされていません。Although you can create overloaded methods with Visual Basic, they are not supported by COM. オーバーロードされたメソッドを含むクラスが COM オブジェクトとして公開されると、オーバーロードされたメソッドの新しいメソッド名が生成されます。When a class that contains overloaded methods is exposed as a COM object, new method names are generated for the overloaded methods.

たとえば、 Synchメソッドの2つのオーバーロードを持つクラスを考えてみます。For example, consider a class that has two overloads of the Synch method. クラスが COM オブジェクトとして公開されている場合、生成されるSynch新しいSynch_2メソッド名はとになります。When the class is exposed as a COM object, the new generated method names could be Synch and Synch_2.

名前を変更すると、COM オブジェクトのコンシューマーに2つの問題が発生する可能性があります。The renaming can cause two problems for consumers of the COM object.

  1. クライアントは、生成されたメソッド名を予期していない可能性があります。Clients might not expect the generated method names.

  2. COM オブジェクトとして公開されるクラスに生成されたメソッド名は、新しいオーバーロードがクラスまたはその基底クラスに追加されたときに変更される可能性があります。The generated method names in the class exposed as a COM object can change when new overloads are added to the class or its base class. これにより、バージョン管理の問題が発生する可能性があります。This can cause versioning problems.

両方の問題を解決するには、COM オブジェクトとして公開されるオブジェクトを開発するときに、オーバーロードを使用するのではなく、各メソッドに一意の名前を指定します。To solve both problems, give each method a unique name, instead of using overloading, when you develop objects that will be exposed as COM objects.

相互運用機能アセンブリによる COM オブジェクトの使用Use of COM Objects Through Interop Assemblies

相互運用機能アセンブリは、それらが表す COM オブジェクトのマネージコード置換の場合とほぼ同じように使用します。You use interop assemblies almost as if they are managed code replacements for the COM objects they represent. ただし、これらはラッパーであり、実際の COM オブジェクトではないため、相互運用機能アセンブリと標準アセンブリの使用にはいくつかの違いがあります。However, because they are wrappers and not actual COM objects, there are some differences between using interop assemblies and standard assemblies. これらの違いの領域には、クラスの公開、およびパラメーターと戻り値のデータ型が含まれます。These areas of difference include the exposure of classes, and data types for parameters and return values.

インターフェイスとクラスの両方として公開されるクラスClasses Exposed as Both Interfaces and Classes

標準アセンブリのクラスとは異なり、COM クラスは相互運用機能アセンブリで、COM クラスを表すインターフェイスとクラスの両方として公開されます。Unlike classes in standard assemblies, COM classes are exposed in interop assemblies as both an interface and a class that represents the COM class. インターフェイスの名前は、COM クラスの名前と同じです。The interface's name is identical to that of the COM class. 相互運用クラスの名前は、元の COM クラスの名前と同じですが、"Class" という単語が追加されています。The name of the interop class is the same as that of the original COM class, but with the word "Class" appended. たとえば、COM オブジェクトの相互運用機能アセンブリへの参照を持つプロジェクトがあるとします。For example, suppose you have a project with a reference to an interop assembly for a COM object. COM クラスにという名前MyComClassが付けられている場合、IntelliSense MyComClassとオブジェクトブラウザーにはMyComClassClassという名前のインターフェイスとという名前のクラスが表示されます。If the COM class is named MyComClass, IntelliSense and the Object Browser show an interface named MyComClass and a class named MyComClassClass.

.NET Framework クラスのインスタンスの作成Creating Instances of a .NET Framework Class

一般に、クラス名を持つNewステートメントを使用して、.NET Framework クラスのインスタンスを作成します。Generally, you create an instance of a .NET Framework class using the New statement with a class name. 相互運用機能アセンブリによって表される COM クラスを使用することは、ステートメントをNewインターフェイスで使用できるケースの1つです。Having a COM class represented by an interop assembly is the one case in which you can use the New statement with an interface. COM クラスをInheritsステートメントで使用している場合を除き、クラスの場合と同様に、インターフェイスを使用できます。Unless you are using the COM class with an Inherits statement, you can use the interface just as you would a class. 次のコードは、Microsoft ActiveX データオブジェクト 2.8 Commandライブラリ COM オブジェクトへの参照を持つオブジェクトをプロジェクトに作成する方法を示しています。The following code demonstrates how to create a Command object in a project that has a reference to the Microsoft ActiveX Data Objects 2.8 Library COM object:

Dim cmd As New ADODB.Command

ただし、COM クラスを派生クラスのベースとして使用している場合は、次のコードのように、COM クラスを表す interop クラスを使用する必要があります。However, if you are using the COM class as the base for a derived class, you must use the interop class that represents the COM class, as in the following code:

Class DerivedCommand
    Inherits ADODB.CommandClass
End Class

注意

相互運用機能アセンブリは、COM クラスを表すインターフェイスを暗黙的に実装します。Interop assemblies implicitly implement interfaces that represent COM classes. これらのインターフェイスを実装するためImplementsにステートメントを使用しないでください。または、エラーが発生します。You should not try to use the Implements statement to implement these interfaces or an error will result.

パラメーターと戻り値のデータ型Data Types for Parameters and Return Values

標準アセンブリのメンバーとは異なり、相互運用機能アセンブリのメンバーは、元のオブジェクト宣言で使用されるデータ型とは異なるデータ型を持つ場合があります。Unlike members of standard assemblies, interop assembly members may have data types that differ from those used in the original object declaration. 相互運用機能アセンブリは、COM 型を互換性のある共通言語ランタイム型に暗黙的に変換しますが、ランタイムエラーを防ぐために、両側で使用されるデータ型に注意する必要があります。Although interop assemblies implicitly convert COM types to compatible common language runtime types, you should pay attention to the data types that are used by both sides to prevent runtime errors. たとえば、Visual Basic 6.0 以前のバージョンで作成された COM オブジェクトでは、 Integer型の値は、.NET Framework Short等価な型であると仮定します。For example, in COM objects created in Visual Basic 6.0 and earlier versions, values of type Integer assume the .NET Framework equivalent type, Short. インポートしたメンバーを使用する前に、その特性を確認するには、オブジェクトブラウザーを使用することをお勧めします。It is recommended that you use the Object Browser to examine the characteristics of imported members before you use them.

モジュールレベルの COM メソッドModule level COM methods

ほとんどの com オブジェクトは、 Newキーワードを使用して com クラスのインスタンスを作成し、オブジェクトのメソッドを呼び出すことによって使用されます。Most COM objects are used by creating an instance of a COM class using the New keyword and then calling methods of the object. この規則の1つの例外としてAppObjGlobalMultiUseまたは com クラスを含む com オブジェクトがあります。One exception to this rule involves COM objects that contain AppObj or GlobalMultiUse COM classes. このようなクラスは Visual Basic .NET クラスのモジュールレベルメソッドに似ています。Such classes resemble module level methods in Visual Basic .NET classes. Visual Basic 6.0 以前のバージョンでは、メソッドのいずれかを初めて呼び出すときに、これらのオブジェクトのインスタンスが暗黙的に作成されます。Visual Basic 6.0 and earlier versions implicitly create instances of such objects for you the first time that you call one of their methods. たとえば、Visual Basic 6.0 では、最初にインスタンスを作成せずに、Microsoft DAO 3.6 オブジェクトライブラリDBEngineへの参照を追加し、メソッドを呼び出すことができます。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 では、メソッドを使用する前に、常に COM オブジェクトのインスタンスを作成する必要があります。Visual Basic .NET requires that you always create instances of COM objects before you can use their methods. これらのメソッドを Visual Basic で使用するには、目的のクラスの変数を宣言し、new キーワードを使用してオブジェクトをオブジェクト変数に割り当てます。To use these methods in Visual Basic, declare a variable of the desired class and use the new keyword to assign the object to the object variable. Sharedキーワードは、クラスのインスタンスが1つだけ作成されるようにする場合に使用できます。The Shared keyword can be used when you want to make sure that only one instance of the class is created.

' Class 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.ToString & ";")
    Next
    Debug.WriteLine("")
    ' Close the Recordset.
    rst.Close()
End Sub

イベントハンドラーでのハンドルされないエラーUnhandled Errors in Event Handlers

一般的な相互運用の問題には、COM オブジェクトによって発生するイベントを処理するイベントハンドラーでのエラーが含まれます。One common interop problem involves errors in event handlers that handle events raised by COM objects. またはOn Error Try...Catch...Finallyステートメントを使用してエラーを明示的にチェックしない限り、このようなエラーは無視されます。Such errors are ignored unless you specifically check for errors using On Error or Try...Catch...Finally statements. たとえば、次の例は、Microsoft ActiveX データオブジェクト2.8 ライブラリ COM オブジェクトへの参照を含む Visual Basic .NET プロジェクトからのものです。For example, the following example is from a Visual Basic .NET project that has a reference to the Microsoft ActiveX Data Objects 2.8 Library COM object.

' To use this example, add a reference to the 
'     Microsoft ActiveX Data Objects 2.8 Library  
' 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 = CInt(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. ただし、 Try...Catch...FinallyブロックをOnError Resume Next使用せずに同じ例を実行すると、ステートメントを使用した場合と同様にエラーが無視されます。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. エラー処理がなければ、0による除算は自動的に失敗します。Without error handling, the division by zero silently fails. このようなエラーによってハンドルされない例外エラーが発生することはないため、COM オブジェクトからのイベントを処理するイベントハンドラーで何らかの形式の例外処理を使用することが重要です。Because such errors never raise unhandled exception errors, it is important that you use some form of exception handling in event handlers that handle events from COM objects.

COM 相互運用エラーについて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 especially helpful when you debug applications. 例えば:For example:

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

例外オブジェクトの内容を調べることによって、エラーの説明、HRESULT、COM エラーの発生源などの情報を確認できます。You can find information such as the error description, HRESULT, and the source of COM errors by examining the contents of the exception object.

ActiveX コントロールの問題ActiveX Control Issues

Visual Basic 6.0 で動作するほとんどの ActiveX コントロールは、Visual Basic .NET では問題なく動作します。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. Visual Studio で正しく動作しない古いコントロールの例を次に示します。Some examples of older controls that do not work correctly with Visual Studio are as follows:

  • Microsoft Forms 2.0 Frame コントロールMicrosoft Forms 2.0 Frame control

  • アップダウンコントロール。スピンコントロールとも呼ばれます。Up-Down control, also known as the spin control

  • Sheridan タブコントロールSheridan Tab Control

サポートされていない ActiveX コントロールの問題には、いくつかの回避策があります。There are only a few workarounds for unsupported ActiveX control problems. 元のソースコードを所有している場合は、既存のコントロールを Visual Studio に移行できます。You can migrate existing controls to Visual Studio if you own the original source code. それ以外の場合は、ソフトウェアベンダーに更新プログラムがあるかどうかを確認できます。サポートされていない ActiveX コントロールを置き換える、互換性のあるコントロールのバージョン。Otherwise, you can check with software vendors for updated .NET-compatible versions of controls to replace unsupported ActiveX controls.

コントロールの読み取り専用プロパティを ByRef で渡すPassing ReadOnly Properties of Controls ByRef

Visual Basic .net では、一部の古い ActiveX コントロールのプロパティを他のプロシージャのパラメーター ReadOnlyとしてByRef渡すと、"Error 0x800A017F CTL_E_SETNOTSUPPORTED" などの COM エラーが発生することがあります。Visual Basic .NET sometimes raises COM errors such as, "Error 0x800A017F CTL_E_SETNOTSUPPORTED", when you pass ReadOnly properties of some older ActiveX controls as ByRef parameters to other procedures. Visual Basic 6.0 からの同様のプロシージャ呼び出しではエラーは発生せず、パラメーターは値渡しで渡されたものとして扱われます。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. Visual Basic .net のエラーメッセージは、プロパティSetプロシージャを持たないプロパティを変更しようとしていることを示します。The Visual Basic .NET error message indicates that you are trying to change a property that does not have a property Set procedure.

呼び出されているプロシージャにアクセスできる場合は、 ByValキーワードを使用してプロパティを受け取るReadOnlyパラメーターを宣言することで、このエラーを回避できます。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. たとえば、Microsoft ActiveX データオブジェクト2.8 ライブラリ COM オブジェクトへの参照を持つプロジェクトでは、次のようなを使用できます。For example, in a project that has a reference to the Microsoft ActiveX Data Objects 2.8 Library COM object, you can use:

Sub PassByVal(ByVal pError As ADODB.Error)
    ' The extra set of parentheses around the arguments
    ' forces them to be passed by value.
    ProcessParams((pError.Description))
End Sub

相互運用機能を公開するアセンブリの配置Deploying Assemblies That Expose Interop

COM インターフェイスを公開するアセンブリを配置すると、いくつかの固有の課題が生じます。Deploying assemblies that expose COM interfaces presents some unique challenges. たとえば、別のアプリケーションが同じ COM アセンブリを参照すると、問題が発生する可能性があります。For example, a potential problem occurs when separate applications reference the same COM assembly. この状況は、新しいバージョンのアセンブリがインストールされていて、別のアプリケーションが以前のバージョンのアセンブリを引き続き使用している場合によく発生します。This situation is common when a new version of an assembly is installed and another application is still using the old version of the assembly. DLL を共有するアセンブリをアンインストールする場合、そのアセンブリを他のアセンブリで使用できないようにすることができます。If you uninstall an assembly that shares a DLL, you can unintentionally make it unavailable to the other assemblies.

この問題を回避するには、共有アセンブリをグローバルアセンブリキャッシュ (GAC) にインストールし、コンポーネントにマージモジュールを使用する必要があります。To avoid this problem, you should install shared assemblies to the Global Assembly Cache (GAC) and use a MergeModule for the component. GAC にアプリケーションをインストールできない場合は、バージョン固有のサブディレクトリにある CommonFilesFolder にインストールする必要があります。If you cannot install the application in the GAC, it should be installed to CommonFilesFolder in a version-specific subdirectory.

共有されていないアセンブリは、呼び出し元のアプリケーションのディレクトリに並べて配置する必要があります。Assemblies that are not shared should be located side by side in the directory with the calling application.

関連項目See also