Rozwiązywanie problemów z współdziałaniem (Visual Basic)
W przypadku współdziałania między modelem COM i kodem zarządzanym programu .NET Framework może wystąpić co najmniej jeden z następujących typowych problemów.
Interop Marshalling
Czasami może być konieczne użycie typów danych, które nie są częścią programu .NET Framework. Zestawy międzyoperacyjne obsługują większość pracy dla obiektów COM, ale może być konieczna kontrola typów danych używanych podczas eksponowania obiektów zarządzanych do COM. Na przykład w bibliotekach klas struktury muszą określać niezarządzany typ BStr
dla ciągów znaków przesyłanych do obiektów COM utworzonych w programie Visual Basic 6.0 i starszych wersjach. W takich przypadkach można użyć atrybutu MarshalAsAttribute, aby spowodować uwidocznienie typów zarządzanych jako typy niezarządzane.
Eksportowanie ciągów Fixed-Length do kodu niezarządzanego
W Visual Basic 6.0 i wcześniejszych wersjach, ciągi są eksportowane do obiektów COM jako sekwencje bajtów, bez zakończenia znakiem null. Aby uzyskać zgodność z innymi językami, program Visual Basic .NET zawiera znak zakończenia podczas eksportowania ciągów. Najlepszym sposobem rozwiązania tej niezgodności jest wyeksportowanie ciągów, które nie mają znaku zakończenia jako tablice Byte
lub Char
.
Eksportowanie hierarchii dziedziczenia
Hierarchie klas zarządzanych spłaszczają się, gdy są uwidocznione jako obiekty COM. Jeśli na przykład zdefiniujesz klasę bazową z elementem członkowskim, a następnie odziedziczysz klasę bazową w klasie pochodnej uwidocznionej jako obiekt COM, klienci korzystający z klasy pochodnej w obiekcie COM nie będą mogli używać dziedziczonych składowych. Dostęp do składowych klasy bazowej można uzyskać z obiektów COM tylko jako wystąpień klasy bazowej, a następnie tylko wtedy, gdy klasa bazowa jest również tworzona jako obiekt COM.
Metody przeciążone
Chociaż można tworzyć metody przeciążone za pomocą języka Visual Basic, nie są one obsługiwane przez com. Gdy klasa zawierająca przeciążone metody jest uwidoczniona jako obiekt COM, dla przeciążonych metod są generowane nowe nazwy metod.
Rozważmy na przykład klasę, która ma dwa przeciążenia metody Synch
. Gdy klasa jest uwidoczniona jako obiekt COM, nowe wygenerowane nazwy metod mogą być Synch
i Synch_2
.
Zmiana nazwy może powodować dwa problemy dla użytkowników obiektu COM.
Klienci mogą nie oczekiwać wygenerowanych nazw metod.
Wygenerowane nazwy metod w klasie uwidocznione jako obiekt COM mogą ulec zmianie po dodaniu nowych przeciążeń do klasy lub jej klasy bazowej. Może to spowodować problemy z wersjonowaniem.
Aby rozwiązać oba problemy, nadaj każdej metodzie unikatową nazwę, zamiast używania przeciążenia, podczas tworzenia obiektów, które będą widoczne jako obiekty COM.
Korzystanie z obiektów COM za pośrednictwem asambli międzyoperacyjnych
Zestawy międzyoperacyjne są używane prawie tak, jakby zastępowały kod zarządzany dla obiektów COM, które reprezentują. Jednak ponieważ są to opakowania, a nie rzeczywiste obiekty COM, występują pewne różnice między używaniem zestawów międzyoperacyjnych i zestawów standardowych. Te obszary różnic obejmują ekspozycję klas i typy danych dla parametrów i wartości zwracanych.
Klasy uwidocznione jako zarówno interfejsy, jak i klasy
W przeciwieństwie do klas w zestawach standardowych klasy COM są uwidocznione w zestawach międzyoperacyjnych jako interfejs i klasa reprezentująca klasę COM. Nazwa interfejsu jest identyczna z nazwą klasy COM. Nazwa klasy międzyoperacyjnej jest taka sama jak nazwa oryginalnej klasy COM, ale z dołączonym słowem "Klasa". Załóżmy na przykład, że masz projekt z odwołaniem do zestawu międzyoperacowego dla obiektu COM. Jeśli klasa COM ma nazwę MyComClass
, funkcja IntelliSense i przeglądarka obiektów zawierają interfejs o nazwie MyComClass
i klasę o nazwie MyComClassClass
.
Tworzenie wystąpień klasy .NET Framework
Ogólnie rzecz biorąc, należy utworzyć wystąpienie klasy .NET Framework przy użyciu instrukcji New
o nazwie klasy. Posiadanie klasy COM reprezentowanej przez zestaw interoperacyjny to jedyny przypadek, gdy można użyć instrukcji New
z interfejsem. Jeśli nie używasz klasy COM z instrukcją Inherits
, możesz użyć interfejsu w taki sam sposób, w jaki używasz klasy. Poniższy kod przedstawia sposób tworzenia obiektu Command
w projekcie, który zawiera odwołanie do obiektu COM biblioteki Microsoft ActiveX Data Objects 2.8:
Dim cmd As New ADODB.Command
Jeśli jednak używasz klasy COM jako podstawy dla klasy pochodnej, musisz użyć klasy międzyoperacyjnej, która reprezentuje klasę COM, jak w poniższym kodzie:
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Notatka
Zestawy międzyoperacyjne implikująco implementują interfejsy reprezentujące klasy COM. Nie należy próbować używać instrukcji Implements
w celu zaimplementowania tych interfejsów lub wystąpi błąd.
Typy danych dla parametrów i wartości zwracanych
W przeciwieństwie do elementów członkowskich standardowych zestawów, elementy członkowskie zestawu międzyoperacyjnego mogą mieć typy danych, które różnią się od tych używanych w oryginalnej deklaracji obiektu. Mimo że zestawy międzyoperacyjności konwertują typy modelu COM na zgodne typy środowiska CLR, należy zwrócić uwagę na typy danych używane przez obie strony, aby zapobiec błędom czasu wykonywania. Na przykład w obiektach COM utworzonych w języku Visual Basic 6.0 i starszych wersjach, wartości typu Integer
przyjmują równoważny typ w .NET Framework, Short
. Zaleca się użycie Przeglądarki obiektów do zbadania właściwości importowanych elementów przed ich użyciem.
Metody COM poziomu modułu
Większość obiektów COM jest używana poprzez utworzenie instancji klasy COM za pomocą słowa kluczowego New
, a następnie wywołanie metod tego obiektu. Jednym wyjątkiem od tej reguły są obiekty COM zawierające AppObj
lub GlobalMultiUse
klasy COM. Takie klasy przypominają metody na poziomie modułu w klasach platformy .NET języka Visual Basic. Visual Basic 6.0 i starsze wersje niejawnie tworzą wystąpienia takich obiektów przy pierwszym wywołaniu jednej z ich metod. Na przykład w języku Visual Basic 6.0 można dodać odwołanie do biblioteki obiektów Microsoft DAO 3.6 i wywołać metodę DBEngine
bez uprzedniego utworzenia wystąpienia:
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
Program Visual Basic .NET wymaga, aby zawsze tworzyć wystąpienia obiektów COM przed użyciem ich metod. Aby użyć tych metod w Visual Basic, zadeklaruj zmienną żądanej klasy i użyj nowego słowa kluczowego, aby przypisać obiekt do zmiennej obiektu. Słowo kluczowe Shared
można użyć, gdy chcesz upewnić się, że utworzono tylko jedno wystąpienie klasy.
' 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
Nieobsługiwane błędy w programie obsługi zdarzeń
Jednym z typowych problemów międzyoperacyjności są błędy w programach obsługi zdarzeń, które obsługują zdarzenia zgłaszane przez obiekty COM. Takie błędy są ignorowane, chyba że w szczególności sprawdzasz błędy przy użyciu instrukcji On Error
lub Try...Catch...Finally
. Na przykład poniższy przykład pochodzi z projektu platformy .NET języka Visual Basic, który zawiera odwołanie do obiektu COM biblioteki Microsoft ActiveX Data Objects 2.8.
' 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 = "..."
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
Ten przykład zgłasza błąd zgodnie z oczekiwaniami. Jeśli jednak spróbujesz tego samego przykładu bez bloku Try...Catch...Finally
, błąd zostanie zignorowany tak, jakby użyto instrukcji OnError Resume Next
. Bez obsługi błędów dzielenie przez zero dyskretnie kończy się niepowodzeniem. Ponieważ takie błędy nigdy nie zgłaszają nieobsługiwanych błędów wyjątków, ważne jest, aby stosować jakąś formę obsługi wyjątków w procedurach obsługi zdarzeń, które obsługują zdarzenia z obiektów COM.
Zrozumienie błędów współpracy COM
Bez obsługi błędów wywołania międzyoperacowe często generują błędy, które zapewniają niewiele informacji. Jeśli to możliwe, użyj obsługi błędów strukturalnych, aby uzyskać więcej informacji na temat problemów, gdy wystąpią. Może to być szczególnie przydatne podczas debugowania aplikacji. Na przykład:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
Informacje takie jak opis błędu, HRESULT i źródło błędów COM można znaleźć, sprawdzając zawartość obiektu wyjątku.
Problemy z kontrolą ActiveX
Większość kontrolek ActiveX, które współpracują z Visual Basic 6.0, współpracuje z Visual Basic .NET bez problemów. Główne wyjątki to kontrolki kontenera lub kontrolki, które wizualnie zawierają inne kontrolki. Niektóre przykłady starszych kontrolek, które nie działają poprawnie w programie Visual Studio, są następujące:
Kontrolka Ramka Microsoft Forms 2.0
kontrolka Up-Down, znana również jako kontrolka spin
Sheridan Tab Control
Istnieje tylko kilka obejść problemów z nieobsługiwaną kontrolką ActiveX. Istniejące kontrolki można migrować do programu Visual Studio, jeśli jesteś właścicielem oryginalnego kodu źródłowego. W przeciwnym razie możesz skontaktować się z dostawcami oprogramowania w celu uzyskania zaktualizowanych wersji kontrolek zgodnych z platformą .NET, które zastępują nieobsługiwane kontrolki ActiveX.
Przekazywanie właściwości ReadOnly kontrolek ByRef
Program Visual Basic czasami zgłasza błędy COM, takie jak "Błąd 0x800A017F CTL_E_SETNOTSUPPORTED", podczas przekazywania ReadOnly
właściwości niektórych starszych kontrolek ActiveX jako ByRef
parametrów do innych procedur. Podobne wywołania procedury z programu Visual Basic 6.0 nie powodują błędu, a parametry są traktowane, jakby były przekazywane przez wartość. Komunikat o błędzie w Visual Basic .NET wskazuje, że próbujesz zmienić właściwość, która nie ma procedury właściwości o nazwie Set
.
Jeśli masz dostęp do wywoływanej procedury, możesz zapobiec temu błędowi, używając słowa kluczowego ByVal
do deklarowania parametrów, które akceptują właściwości ReadOnly
. Na przykład:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Jeśli nie masz dostępu do kodu źródłowego dla wywoływanej procedury, możesz wymusić przekazanie właściwości przez wartość, dodając dodatkowy zestaw nawiasów wokół procedury wywołującej. Na przykład w projekcie, który zawiera odwołanie do obiektu COM biblioteki Microsoft ActiveX Data Objects 2.8, można użyć:
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
Wdrażanie zestawów umożliwiających interoperacyjność
Wdrażanie zestawów, które uwidacznia interfejsy COM, stanowi pewne unikatowe wyzwania. Na przykład potencjalny problem występuje, gdy oddzielne aplikacje odwołują się do tego samego zestawu COM. Taka sytuacja jest powszechna, gdy jest zainstalowana nowa wersja zestawu, a inna aplikacja nadal używa starej wersji zestawu. W przypadku odinstalowania zestawu, który współudzieli bibliotekę DLL, można przypadkowo uczynić go niedostępnym dla innych zestawów.
Aby uniknąć tego problemu, należy zainstalować zestawy udostępnione w globalnej pamięci podręcznej zestawów (GAC) i użyć modułu MergeModule dla składnika. Jeśli nie możesz zainstalować aplikacji w GAC, należy ją zainstalować w folderze CommonFilesFolder w podkatalogu specyficznym dla wersji.
Zestawy, które nie są udostępniane, powinny znajdować się obok siebie w katalogu z aplikacją wywołującą.