Hallo,
ich habe ein fertiges Programm in .Net/C# geschrieben und möchte dies nun in Visual Basic 6 einbinden. Ich weiß das es mit Com geht bzw. gehen sollte, nur leider nicht wo ich anfangen soll.
Die COM-Klasse soll 3 Funktionen enthalten
void Open();
void Close();
int Parse(string input, ref output);
Wie ist dazu die vorgehensweise bzw. was ist dabei wichtig umzusetzen. Und gibts vielleicht gute "Schritt für Schritt"-Anleitungen?
Dankeschonmal
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Hallo
Also der einfachste Fall ist, wenn Du kein Interface benutzt, sondern nur diese Klasse im VB6 sehen willst. Dann brauchst Du nur die Klassenattribute vor Deiner Klasse zu setzen
<ClassInterface(ClassInterfaceType.None), Guid("-> neue GUID ")> _
Class MyNetClass
..
und in der Projekteigenschaften unter Compile das Häkchen für COM zu setzen. Dann sollte es schon gehen.
(Die Syntax der Attribute hier ist für VB.NET. In C# brauchst du glaube ich [] statt <>)
Grüße
Elric
OK.Danke, werd das gleich mal probieren. Muss die Datei mit Regsvr bzw. regasm registriert werden?
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Auf dem Entwicklungsrechner nicht, aber auf dem Zielrechner mit regasm.
Grüße
Elric
PS: sry, dass die Antwort so kurz war, aber hatte vorhin grad wenig Zeit. Wenns nicht klappt schreibe ich es nochmal ausführlicher zusammen.
So ich kann die Datei jetzt in VB über Projekte-->Verweise einfügen. Allerdings sehe ich im Objektkatalog mein Objekt nicht.
[
ClassInterface( ClassInterfaceType.None),
Guid("f525452c-61ae-46ec-8818-11f920fd39f7")
]
public class MyBRP
{
public string CurrentTime()
{
return DateTime.Now.ToString();
}
public MyBRP()
{
}
}
Unter Projekteigenschaften-->Erstellen hab ich das häkchen für Com-Interop registrieren gesetzt und auch bei Anwendung-->Assemblyinformationen den Haken bei Assembly Com-sichtbar machen gesetzt.
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Ja, das liegt an dem InterfaceType.None. Mir fallen im Moment 2 Möglichkeiten ein (ich glaub es gibt noch eine einfachere, aber da müßte ich erst noch in meinem alten Code recherchieren.)
Du setzt den InterfaceType von None auf Autodual. Dann siehst Du die Methoden im VB6. Die MSDN sagt allerdings "Using AutoDual is strongly discouraged ". Man sollte es also nur zum Entwickeln setzen und später wieder auf None.
Oder:
Du machst Dir ein Interface, musst aber dann der Klasse das Interface als Source geben. In Deinem Fall also ungefähr so:
[GuidAttribute("7121BBE3-0F4A-428a-A6E5-978378AC92D4"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyBRP
{
[DispId(1)] string CurrentTime();
}
[ClassInterface(ClassInterfaceType.None), Guid("f525452c-61ae-46ec-8818-11f920fd39f7"), ComSourceInterfaces(typeof(IMyBRP))
]
public class MyBRP:IMyBRP
{
public string CurrentTime()
{
return DateTime.Now.ToString();
}
public MyBRP()
{
}
}
Schönes Wochenende.
Elric
Ein Problem hab ich aber noch. Wenn ich im Quelltext mit Guid() die Nummer angebe, nehm ich die von den Projekteigenschaften--> AssemblyInformationen und kopiere diese dann dort rein. Beim registrieren mit regasm /tlb: bekomm ich den Fehler Element nicht gefunden.
//Nachtrag
habe die Guids jetzt weggelassen und es funktioniert. Danke
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Also, wenn eine GUID, dann eine neue erzeugen und nicht die aus dem AssemblyInfo nehmen (geht im Visual Studio unter Tools->Create GUID).
Aber wenns so auch geht, dann lass es einfach.
Aso. danke wusst ich nicht. Nö das mit weglassen ist ganz ok so.
Für was ist dann die aus den AsyymblyInfo(s)?
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Eine GUID ist ja erstmal nichts anderes als ein eindeutiger Identifier für bestimmte Elemente (ähnlich einem Primärschlüssel in einer Datenbanktabelle), ist somit auch nicht auf Klassen beschränkt, sondern gilt auch für Assemblies, Interfaces, Enumerations, Structures,...
Das ist der Grund warum Du für die Klasse nicht diesselbe GUID hernehmen durftest, die das Assembly schon hat, da dadurch die Eindeutigkeit verletzt wurde.
In "reinen" .NET Anwendungen werden GUIDs nicht mehr gebraucht, aber sie werden gebraucht, wenn etwas nach außen (von nicht .NET) identifiziert werden soll. Deshalb hat das Assembly vorsichtshalber schon mal eine und deshalb brauchen auch die Klassen, die über COM angesprochen werden auch eine.
Das Weglassen der fixen GUID, so wie Du es gemacht hast, bewirkt erstmal, dass eine GUID beim Kompilieren erzeugt wird. Das bedeutet aber, dass es zwar jetzt funktioniert, aber bei jedem Kompilieren eine neue GUID erzeugt wird und Du somit auch jedesmal die VB6 Anwendung neu Kompilieren musst (ein Unterschieben einer neu kompilierten .NET-DLL zu einer bestehenden VB6-EXE dürfte nicht funktionieren).
Deshalb solltest Du das GUID-Attribut noch setzen, damit die GUID immer fix ist.
Grüße
Elric
OK. werd das noch berücksichtigen.
Hab aber noch eine frage, wenn ich mit Assemby.GetCallingAssemby().Location bzw. Assembly.GetExecutionAssembly().Location, welche Pfade geben mir diese unter VB6 zurück, bzw. welche von den vielen Varianten zur aktuelle Verzeichnisbestimmung ist in diesem Fall am besten?
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Ich hab leider noch ein Problem, vllt. kann mir jemand helfen. Hab das ganze wunderbar in VB6 einbinden können. Wollte das ganze jetzt schrittweise debuggen und sobal ich die erste Funktion aufrufe, kommt ein Fehler von VB6 das die Datei nicht gefunden wurde.
Vllt. zur Hintergrundinformation noch folgende Dinge.
Die kompilierte Assembly für VB6 greift auf zwei weitere Assembly (ganz normal .Net ohne Com+ Funktionen und implementierte Klassen) drauf zu. Die Dateien befinden sich im selben Verzeichnis wie die ursprungsCom-Plus-Assemby.
Die Dateien liegen aber nicht Gac.
Wenn ich jetzt das ganze aber in VB6 ohne debuggen machen, über die kompilierte Exe-Datei funktioniert alles wunderbar. Gibt es da noch einen Trick, fürs Debuggen?
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Hallo kleines_eichhoernchen,
wenn es immer noch aktuell ist (ist ja jetzt schon Ewigkeiten her), dann solltest Du auch unbedingt folgendes Anschauen:
Das ist eine Toolbox mit der man WinForms und Usercontrols in .NET entwickeln kann, und in VB6 verwendet. Komplett mit Eventsystem und allem was dazugehört.
Eventuell ist es bei Deiner Arbeit hilfreich (bei mir extrem).
Grüße
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
Ja Danke, werd ich mir mal anschauen. Aber musste zum Glück das ganze nur einmal bis jetzt machen, da es nur ein Wrapper für eine alte VB6 Anwendung sein musste.
Jetzt hab ich nur das selbe Problem in Perl, da mein Kollege die alte VB6 Anwendung in einer mir nicht ganz so "einleuchtenden" und verständlichen Sprache schreiben wird. Ich schreibe aber meinen Programmteil in C/C++, und denke das das keine Probleme bereiten wird. Trotzdem nochmal Danke
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Ich versuche auch grad des ans laufen zu bekommen.
[GuidAttribute("C747BE58-7921-480c-97D8-8D6FE31BE5E9"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface myOSCServer
{
[DispId(1)] void Start();
}
/// <summary>
/// Listens for and processes incoming Open Sound Control packets.
/// </summary>
[ClassInterface(ClassInterfaceType.None), Guid("889D6707-3EE7-48cb-81E7-4F54BEF8F86E"),
ComSourceInterfaces(typeof(myOSCServer))]
public class OscServer : myOSCServer
{
Kompilern lässt es sich und einbinden auch.
Nur erscheint die Klasse trotzdem nicht ...
Als Typ erscheint die Klasse, nur kann ich kein neues Object der Klasse erstellen.
Zu all dem Gesagten, und ohne dass ich das jetzt ausprobiert hätte, aber geht eine solche Anbindung auch unter VB5. Ich bin selbst nicht betroffen davon, und werde auch die nächsten 100 Jahre sicherlich kein VB anfassen, aber wir sitzen hier in dem Fall, dass wir einige VB5 Anwendungen hier laufen haben, und da wäre es interessant zu wissen, ob diese Anbindung auch unter VB5 funktioniert?
Hallo,
ich habe zu diesem Thema zwei kleine Fragen.
Wenn man auf das Interface das Attribut [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] und auf die Klasse, die von diesem Interface erbt, das Attribut [ClassInterfaceType.None] anwendet, müssen dann nicht die Methoden, Eigenschaften usw. vom Typ Objekt zurückgeben?
Soweit ich mich informiert habe, bedeutet Dispatch = spätes Binden, d.h. der Objekttyp wird später deklariert.
Da würde der Code (siehe Elric`s Beitrag) dann so aussehen:
[GuidAttribute("7121BBE3-0F4A-428a-A6E5-978378AC92D4"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyBRP
{
[DispId(1)] object CurrentTime();
}
[ClassInterface(ClassInterfaceType.None), Guid("f525452c-61ae-46ec-8818-11f920fd39f7"), ComSourceInterfaces(typeof(IMyBRP))]
public class MyBRP:IMyBRP
{
public object CurrentTime()
{
return DateTime.Now.ToString();
}
public MyBRP()
{
}
}
Ich kenne mich mit der COM-.NET-Materie nicht so gut aus (siehe mein Thread: Kommunikation COM --> .NET über Properties: .NET-Properties für COM sichtbar machen ), deshalb soll das eine Frage, keine Feststellung sein 😉.
Des Weiteren würde ich gerne wissen wozu das Attribut [DispId(1)] dient. Folgt es nach dem gleichen Prinzip wie das Attribut [Guid()]?
Wegen der DispID hat mir auf eine Frage von mir Rainbird eine Antwort geschrieben
"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein
Hmm schonmal etwas.
Kann mir wer weiterhelfen warum des immer noch nicht geht?
Hallo,
Kompilern lässt es sich und einbinden auch.
Nur erscheint die Klasse trotzdem nicht ...
Als Typ erscheint die Klasse, nur kann ich kein neues Object der Klasse erstellen.
Was meinst du damit, kommt da ein Fehler oder werden die Properties und Methoden nicht in der IntelliSense angezeigt?
Wenn Letzteres zutrifft, einfach den Namen der Methode/ des Properties schreiben, auch wenn es durch die IntelliSense nicht angezeigt wird (war bei mir auch so). Eventuell auch das Attribut des Interfaces von .InterfaceIsIDispatch auf .InterfaceIsIUnknown (<- frühes Binden) ändern.
@LastGentleman:
Danke für den Link, hatte bis dahin keine Erklärung gefunden.
Ok danke, ich werds mal probieren obs geht.
EIDT: scheint nicht zu gehen.
Dim Test As Bespoke_Common_Osc.OscServer
Test = New OscServer
bringt nur nen "Invalid use of new keyword"
Für Objekterzeugung ist in VB6 das Set-Schlüsselwort nötig:
Dim objTest As Bespoke_Common_Osc.OscServer
Set objTest = New OscServer
Hmm geht immer noch nicht ... leider
Private Sub Form_Load()
Dim objTest As Bespoke_Common_Osc.OscServer
Set objTest = New Bespoke_Common_Osc.OscServer
End Sub
Was heißt geht nicht? Kommt eine Ausnahme? Wenn ja, welche?
Dir ist klar, dass das Objekt nur kurz erzeugt und danach sofort wieder zerstört wird?
Was sollte denn beim Erzeugen des COM-Objekts passieren?
Jo ist mir schon klar des es nur kurz erzeugt wird, und dann zerstört würde.
Aber leider kommt immer noch der gleiche Fehler wie ohne des Set also "Invalid use of new keyword"
Versuchs mal mit Dispatch.
Ändere das Attribut ClassInterface Deiner COM-Wrapper-Klasse folgendermaßen ab:
[ClassInterface(ClassInterfaceType.AutoDispatch)]
Versuche dann, das Objekt so zu erzeugen:
Dim objTest as Bespoke_Common_Osc.OscServer
Set objTest=CreateObject("Bespoke.Common.Osc.OscServer")
hmpf ... will immer noch nicht.
Mein C# Code sieht so aus:
[GuidAttribute("C747BE58-7921-480c-97D8-8D6FE31BE5E9"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface myOSCServer
{
[DispId(1)] void Start();
}
/// <summary>
/// Listens for and processes incoming Open Sound Control packets.
/// </summary>
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class OscServer : myOSCServer
{
in VB6 hab ich des geschrieben, was du gepostet hast.
Kommt immer der Fehler "ActiveX component can't create object"
Da fällt auf, dass Deine Klasse keinen Guid hat. Auch die Klasse sollte ein Guid-Attribut mit eindeutigem Schlüssel bekommen.
Hast Du auch in den Projekteigenschaften das Häkchen bei "Für COM-Interop registrieren" gesetzt?
Ansonsten könnten es noch Namnsprobleme sein. Um dem vorzubeugen, solltest Du sowohl Interface als auch Klasse ein ProgId-Attribut geben, und dort den COM-Namen (ProgID) eintragen.
Hmm will immer nocht nicht ...
VB6
Private Sub Form_Load()
Dim objTest As Bespoke_Common_Osc.OscServer
Set objTest = CreateObject("Bespoke.Common.Osc.myOscServer")
End Sub
C#:
[GuidAttribute("C747BE58-7921-480c-97D8-8D6FE31BE5E9"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface myOSCServer
{
[DispId(1)] void Start();
}
/// <summary>
/// Listens for and processes incoming Open Sound Control packets.
/// </summary>
[ClassInterface(ClassInterfaceType.AutoDispatch), Guid("889D6707-3EE7-48cb-81E7-4F54BEF8F86E"),
ComSourceInterfaces(typeof(myOSCServer)), ProgId("Bespoke.Common.Osc.OscServer")]
public class OscServer : myOSCServer
{
Die GUI-ID hatte ich vorher auch schon dran, nur hatte ich die zum testen wieder weg gemacht.
Des "Für COM-Interop registrieren" ist gesetzt.
Es kommt immer noch des gleiche Fehler wie vorher ...
// FEHLER: myOscServer ist ein Interface und kann deshalb nicht erzeugt werden
Set objTest = CreateObject("Bespoke.Common.Osc.myOscServer")
// KORREKTUR: Die ProgId zur Erstellung verwenden "Bespoke.Common.Osc.OscServer"
Set objTest=CreateObject("Bespoke.Common.Osc.OscServer")
Wenn es dann immer noch nicht geht, solltest Du die Assembly mal manuell mit dem Konsolentool REGASM.EXE registrieren. Da bekommst Du möglicherweise Fehler angezeigt, die beim registrieren auftreten. So wendest Du das Tool an:
REGASM.EXE DeineAssembly.dll /codebase /tlb:DeineAssembly.tlb /verbose
Weitere Hilfe zu diesem Tool findest Du unter: Assembly Registration-Tool (Regasm.exe)
Noch eine Frage: Ist Deine Assembly signiert? Du solltest Dein Assembly signieren (Stichwort Strong Name). Stelle außerdem sicher, dass Deine Assembly den CAS-Berechtigungssatz Fulltrust erhält.
C:\Users\Markus\Documents\0 Programmieren\C#\LumosPlugins\OSCServerPlugin\Source
Code\Framework\Bespoke.Common.Osc\bin\x86\Debug>"c:\Windows\Microsoft.NET\Frame
work\v2.0.50727\RegAsm.exe" Bespoke.Common.Osc.dll /codebase /tlb:OSC.tlb /verbo
se
Microsoft (R) .NET Framework Assembly Registration Utility 2.0.50727.3053
Copyright (C) Microsoft Corporation 1998-2004. Alle Rechte vorbehalten.Die Typen wurden registriert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Der Typ "B" wurde exportiert.
Die Assembly wurde nach "C:\Users\Markus\Documents\0 Programmieren\C#\LumosPlugi
ns\OSCServerPlugin\Source Code\Framework\Bespoke.Common.Osc\bin\x86\Debug\OSC.tl
b" exportiert, und die Typbibliothek wurde registriert.
Regasm sagt nichts besonderes. Signiert hab ichs auch mittlwerweile ...
Aber immer noch nichts. Hab auch schon die dlls die benötigt werden mit ins VB6-Projektverzeichniss gepackt.
Kann es daran liegen, dass das Objekt selber noch andere Objekte intern braucht und auch noch eine andere DLL?
Hab auch schon die dlls die benötigt werden mit ins VB6-Projektverzeichniss gepackt.
Kann es daran liegen, dass das Objekt selber noch andere Objekte intern braucht und auch noch eine andere DLL?
Die abhängigen DLLs müssen entweder im GAC registriert sein, oder im selben Verzeichnis liegen wie die registrerte COM-Interop-aktivierte Assembly. Wenn die Assembly über COM-Interop in den Prozess der VB6-Anwendung geladen wird, sucht die CLR im Verzeichnis der Assembly nach Abhängigkeiten (und natürlich im GAC).
Probier mal was anderes. Erstelle ein neues Visual Studio Projekt und baue eine HalloWelt-COM-Wrapper-Assembly. Da kannst Du das Problem leichter isolieren.
Wie sieht Dein Konstruktor aus? COM unterstützt keine parametrisierten Konstruktoren. Die Objekterstellung funktioniert nicht, wenn Deine Klasse keinen Standardkonstruktor hat.
Wird Deine COM-Wrapper-Assembly mit Ihren Klassen und deren Methoden im VB6-Objektkatalog korrekt angezeigt?
Welche Datentypen nehmen die Methoden der OscServer-Klasse als Parameter entgegen, bzw. welche Datentypen geben sie zurück? Sind alle diese Datentypen ComVisible? Können all diese Datentypen automatisch in entsprechende COM-Datentypen umgewandelt werden, oder ist möglicherweise der manuelle Eingriff in den COM-Marshalling-Vorgang nötig (das ist häufig bei Collections und Arrays der Fall)? Solche Datentyp-Probleme können bereits das Erzeugen einer Instanz der Klasse verhindern. Deshalb ganz wichtig: Was zeigt der VB6-Objektkatalog an?
In dem Zusammenhang könnte folgender Beitrag weiterhelfen:
[gelöst] C# zu VBA
Wie sieht Dein Konstruktor aus? COM unterstützt keine parametrisierten Konstruktoren. Die Objekterstellung funktioniert nicht, wenn Deine Klasse keinen Standardkonstruktor hat.
Hab glaub ich nur Parametrisierte ...
Welche Datentypen nehmen die Methoden der OscServer-Klasse als Parameter entgegen, bzw. welche Datentypen geben sie zurück? Sind alle diese Datentypen ComVisible? Können all diese Datentypen automatisch in entsprechende COM-Datentypen umgewandelt werden, oder ist möglicherweise der manuelle Eingriff in den COM-Marshalling-Vorgang nötig (das ist häufig bei Collections und Arrays der Fall)? Solche Datentyp-Probleme können bereits das Erzeugen einer Instanz der Klasse verhindern.
Das muss ich mir auch mal anschauen. Die Datentypen sehe ich auch alle. Das sind aber auch eigentlich alles Objekte. Werd des mal mit dem HelloWorld probieren.
Hab glaub ich nur Parametrisierte ...
Mach nen Standardkonstruktor (ohne Argumente) draus und baue ne Funktion Init ein, die die Initalisierung deiner Klasse vornimmt.
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Naja ich hab jetzt mal zusätzlich einen ohne eingebaut, aber auch des brachte nichts.
Die DLL soll ja nicht nur für VB6 sein sondern auch normal für C#. Daher kann ich nicht alle parametrisierten Konstruktoren raus nehmen.
Wenn ich wieder mehr Zeit hab, werd ich des ganze mal mit nem Hello World Programm probieren.
Die DLL soll ja nicht nur für VB6 sein sondern auch normal für C#.
Das solltest Du nicht machen. Für die COM-Unterstützung immer separate Wrapper schreiben, sonst kaufst Du Dir alle Beschränkungen von COM automatisch in Deine C#-Architektur mit ein.