| 

.NET C# Java Javascript Exception

3
Hallo Leute,

obwohl ich im Web bereits einige Hinweise und Beispielcode zu dem Problem gefunden habe,
bekomme ich es nicht hin, dass ich per COM-Interop auf eine Methode aus einer von mir erstellten C#-DLL zugreifen kann.

Ich habe folgende Funktion, die ich für COM sichtbar machen möchte:

using System;
using System.Runtime.InteropServices;

namespace ComTestCSharp
{
[Guid("F1E80D2D-72F7-434F-86AB-DFC7BF10C447"), ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IComTest
{
[DispId(1)]
int AddFunction(int value1, int value2);
}
[Guid("0C216A19-E1B7-4b05-86D3-4C516BDDC041")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("ComTest")]
public class ComTest : IComTest
{
[DispId(1)]
public int AddFunction(int value1, int value2)
{
return value1 + value2;
}
}
}


In den Projekteigenschaften habe ich den Haken bei "Assembly COM-sichbar machen" gesetzt. Nach dem Übersetzen stehen die beiden Dateien ComTestCSharp.DLL und ComTestCSharp.TLB im Debugordner.

Jetzt lege ich zu Testzwecken eine neue C#-Konsolenanwendung in VS2010 an und kopiere die beiden Dateien in den bin/Debug-Ordner.

Der Programmcode für die Testanwendung sieht wie folgt aus:
using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(AddFunction(5, 2));
}
[DllImport("ComTestCSharp.dll")]
private static extern int AddFunction(int value1, int value2);
}
}


Wenn ich nun das Programm starte bekomme ich bei dem Versuch die Function "AddFunction(5,2)" aufzurufen die Exception
"Der Einstiegspunkt "AddFunction" wurde nicht in der DLL "ComTestCSharp.dll" gefunden."

Was mache ich falsch?

Vielen Dank für Eure Hilfe.
Carsten

PS: Ich arbeite mit C# 4.0 und VS2010 deutsch
17.06.2011
Carsten Ilwig 337 1 6
12 Antworten
2
Probier mal den Befehl [ComVisible(true)] über den Klassennamen zu schreiben

[ComVisible(true)]
public class ComTest : IComTest
17.06.2011
HischLock 73 1 5
2
Hallo Leute,

jetzt bin ich wieder zurück und habe auch mein hier beschriebenes Problem erfolgreich lösen können. :-)
Als erstes einmal vielen Dank an alle, die auf meine Anfrage geantwortet haben und somit auch zur Lösung meines Problems beigetragen haben.

Hier nun die komplette Lösung, falls später einmal jemand vor dem selben Problem stehen sollte:

Folgende Aufgabe galt es zu lösen:
Es sollte von einer bestehenden VB6-Anwenderung heraus auf öffentliche Eigenschaften/Methoden/Functions in einer C#-DLL zugegriffen werden.

Hier meine Lösung:
Die C#-DLL wurde unter Win7 Ultimade mit VS2010 in .NET 4.0 entwickelt.
Wichtig dabei ist, dass, wenn die C#-DLL direkt aus VS heraus für COM sichtbar gemacht werden soll, man VS mit Adminrechten startet, sonst bekommt VS keinen Zugriff auf die Registry um die DLL zu registrieren.
In diesem Fall muss auch in der Assemblyinfo.cs folgende Zeile stehen:
[assembly: ComVisible(false)]

Hier der Code für die C#-DLL (Assemblyname: VB6TestCSharp.DLL):

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ComTestCSharp
{
[Guid("F1E80D2D-72F7-434F-86AB-DFC7BF10C447")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IComTest
{
[DispId(1)]
int AddFunction(int value1, int value2);
[DispId(2)]
string GetMyProperty();
[DispId(3)]
string MyProperty { get; set; }
}

[Serializable]
[Guid("0C216A19-E1B7-4b05-86D3-4C516BDDC041")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
[ProgId("ComTest")]
public class ComTest : IComTest
{
public string MyProperty { get; set; }

public ComTest() { }

public int AddFunction(int value1, int value2)
{
return value1 + value2;
}
public string GetMyProperty()
{
MessageBox.Show("MyProperty: "+MyProperty);
return MyProperty;
}
}
}

Und hier der VB6-Code:

Function MyTest()
Dim test As Long
Dim theObj As New ComTest
test = theObj.AddFunction(5, 6)
theObj.MyProperty = "Test"
Dim strValue As String
strValue = theObj.GetMyProperty()
Exit Function
End Function


Um auf die C#-Eigenschaften zu zugreifen, muss zuvor in VB6 über den Menüpunkt 'Projekt/Verweise' ein Haken bei ' VB6TestCSharp' gesetzt werden, damit ein Verweis auf die C#-DLL in das Projekt eingebunden wird.
Sollte der Eintrag ' VB6TestCSharp' nicht in der Auflistung stehen, dann ist ein Fehler bei der Com-Registrierung der DLL aufgetreten.

Eine manuelle Registrierung kann auf folgende Weise geschehen:

regasm VB6TestCSharp.dll /tlb /CodeBase /register

Das Programm 'RegAsm' finden man unter ' C:\Windows\Microsoft.NET\Framework\v??'
Die Fragezeichen (??) stehen für die zu verwendente .NET-Version.
In einer 64-bit Umgebung muss die Datei aus dem Ordner ' C:\Windows\Microsoft.NET\Framework64\v??' verwendet werden.


Was ich derzeit noch nicht klären konnte, ist, wie man in benutzerdefinierte Objekte aus VB6 an C# übergibt und umgekehrt und wie man in VB6 ein aus der C#-DLL heraus ausgelöstes Event 'abfängt'.

Beispiel:

benutzerdefiniertes Object in VB6:

Public Type MyType
A As String
B As String
C As String
End Type


Wie wird so ein Typ an die C#-DLL übergeben, bzw. von der C#-DLL zurückgeliefert?
30.06.2011
Carsten Ilwig 337 1 6
1
Ich verwende das um diverse Methoden in Excel als Com-Addin zur Verfügung zu haben, mit Delphi konnte ich das so auch schon aufrufen.
namespace VisualPkAddIn
{
[Guid("744B3544-FDA6-4AE9-9CAD-15D10412DFE8")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class PkFunctions
{
public double Runden0(double zahl)
{
return MathExtensions.Round0(zahl);
}

[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type type)
{
Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable"));
RegistryKey key = Registry.ClassesRoot.OpenSubKey(GetSubKeyName(type, "InprocServer32"), true);
key.SetValue("", Environment.SystemDirectory + @"\mscoree.dll", RegistryValueKind.String);
}

[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type type)
{
Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type, "Programmable"), false);
}

private static string GetSubKeyName(Type type, string subKeyName)
{
System.Text.StringBuilder s = new System.Text.StringBuilder();
s.Append(@"CLSID\{");
s.Append(type.GUID.ToString().ToUpper());
s.Append(@"}\");
s.Append(subKeyName);
return s.ToString();
}
}
}
17.06.2011
vorauler 11 1
Danke für den Beispielcode.
Leider kann ich den so nicht bei meiner Anforderung verwenden, da das "ComRegisterFunctionAttribute" nur für statische Methoden ohne Rückgabetyp funktioniert und mann auch nur Parameter vom Typ 'Type' übergeben kann.
Carsten Ilwig 17.06.2011
1
Also wie man eine COM-Klasse erstellen kann ist hier ja schön beschrieben. Was noch ein paar Stolpersteine sein können:
Denk daran, dass die Klasse eigene GUIDs braucht:

<ComClass(myClass.ClassId, myClass.InterfaceId, myClass.EventsId)> _
Public Class myClass

#Region "COM-GUIDs"
' Diese GUIDs stellen die COM-Identität für diese Klasse
' und ihre COM-Schnittstellen bereit. Wenn Sie sie ändern, können vorhandene
' Clients nicht mehr auf die Klasse zugreifen.
Public Const ClassId As String = "c978ce23-7cee-43e2-a676-355f586b2ded"
Public Const InterfaceId As String = "342c5708-5da9-4773-81d3-bc7fb17ae263"
Public Const EventsId As String = "1a8b4b0a-7f7d-4f9f-add3-82d9830248f5"
#End Region

Klasse muss einen leeren Konstruktor beinhalten.
Alle Methoden müssen dann PUBLIC sein (is eh klar)
und wichtig das kind muss dann registriert werden:
%systemroot%\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe "Pfad zu meiner DLL.dll" /unregister
%systemroot%\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe "Pfad zu meiner DLL.dll" /tlb:meineDLL.tlb /codebase

Anschließend solltest du über COM zumindest schonmal deine Klasse sehen.
17.06.2011
daWastl 277 1 7
1
Ich nutze in einem Projekt regel COM da ich auf die Klassen mit VBS zugreifen muss.
Wichtig dabei ist, dass z.b. im Projekt "Register for COM Interop" selektiert ist. Hilft bie mir oft.

Zudem musst ich für jede Klasse auch ein Interface deklarieren.

[ComVisible(true)]
[Guid("e272a619-3b80-42a3-9156-df57ed9eb3f7")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IFach


Die Klasse selber sieht bei mir so aus.
[Serializable]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("5330F296-FC59-45C3-8368-7F1A022FA635")]
public class Fach : IFach


Und schon kann ich aus VBS darauf zugreifen.

In der AssemblyInfo.cs habe ich noch die Zeile [assembly: ComVisible(true)] drin.

Vieleicht hilft es.
17.06.2011
GENiALi 2,5k 1 2 8
Ich habe meine zu veröffentlichende DLL genauso aufgebaut, wie Du es beschreibst.
Trotzdem bekomme ich keinen Zugriff auf die Funktion.
Kann ich Dir vielleicht mal meine Test-DLL zusenden,
damit Du bei Dir testen kannst, ob Du Zugriff auf die Funktion bekommst.
Wenn ja, dann muss ich ja einen Fehler beim Registrieren der DLL im System, oder in der Zielanwendung machen.
Bekommst Du keinen Zugriff, muss der Fehler in der DLL selbst liegen.
Carsten Ilwig 17.06.2011
Ich hätte ja alles um es zu testen. Dein Code oben scheint vollständig zu sein. Werde mir es anschauen. Zur Zeit bin ich aber im Büro, Job, und kann nicht einfach so machen. :-)
GENiALi 17.06.2011
Das wäre prima. :-) Vielen Dank schon mal vorab.
Carsten Ilwig 17.06.2011
Konnte es jetzt nich sein lassen und habs getestet. Das Zeugs wird in der Registiry richtig abgelegt. Dort finde ich keinen Unterschied. Mit einem VBS scirpt gibt auch einen Fehler.

Test.vbs
--------
set obj = CreateObject("ComTestCSharp.ComTest")
MsgBox obj.AddFunction(2, 5)

Vermutung ist noch da, dass es an irgend welchen Berechtigungen liegen könnte. Aber wo, das weiss ich noch nicht. :-/
GENiALi 17.06.2011
1
Begriffsverwirrung...

COM und P/Invoke sind zwei grundverschiedene Dinge. Ich kenne keine COM-DLL die mittels DllImport aufzurufen wäre. Bitte um entsprechendes Googeln.

Nachtrag: Anstelle DLL-Import, eine COM-Referenz ins Projekt aufnehmen (da die DLL ja COM registriert ist, sollte sie bei Add References in der COM Lasche aufscheinen) und dann die Objekte verwenden, wie man es von COM Objekten kennt.

Und P/Invoke == Platform Invoke bezieht sich auf Native DLLs (C++), deren Funktionen man aufrufen möchte. Die haben nichts mit COM, oder managed Code der COM visible ist, zu tun.

Grüße
Maria
17.06.2011
Maria Simlinger 1,1k 1 9
0
Vielen Dank für die schnelle Antwort, leider bringt diese Änderung auch keinen Erfolg.
Die Fehlermeldung beim Aufruf der Funktion bleibt bestehen.
17.06.2011
Carsten Ilwig 337 1 6
0
als Schuss ins Blaue:

InterfaceIsDual?

und nur so als wirklich zwei dumme fragen:

das ist eine com dll. warum aus .net heraus eine in .net entwickelte com dll verwenden?

muss man eine solche nicht mit regasm (weil eben com) registrieren?
17.06.2011
nabuchodonossor 1,3k 5
0
Mit "ClassInterfaceType.AutoDual", ich gehe mal davon aus, dass Du diesen Wert meintest, habe ich es auch schon probiert, leider ohne Erfolg.

Diese Konstellation dass ich eine .NET-DLL in einer anderen .NET-Anwendung via COM aufrufe dient nur dem grundsätzlichem Funktionstest. Das eigentliche Ziel ist, auf die Funktionen aus der .NET-DLL von VB6 aus zuzugreifen.
Da aber die VB6-IDE nicht auf einem 64bit-System läuft (meinem Arbeitssystem) habe ich mir diesen Testfall konstruiert um nicht ständig zwischen den Systemen wechseln zu müssen.
Wenn ich aus der .NET-Testanwendung heraus via COM auf meine Funktion in der .NET-DLL zugreifen kann, dann klappt es hoffentlich auch von VB6 aus.

Ja, mann muss diese DLL via regasm registrieren. Zum einen sollte das VS bereits autom. machen, durch den Haken "Assembly COM-sichtbar machen" in den Projekteigenschaften, aber zur Sicherheit habe ich die DLL über regasm auch nochmal manuell registriert.
Es funktioniert aber trotzdem nicht. Allerdings vermute ich, dass das Problem irgendwie mit der Registrierung zu tun hat, ich weiß nur nicht was ich falsch mache.
Die DLL an sich wird in der Testanwendung auch gefunden, nur halt die darin enthaltene Funktion nicht.
17.06.2011
Carsten Ilwig 337 1 6
Hast du auch die regasm aus dem "Windows\Microsoft.NET\Framework64\v2.0.50727" verwendet? 64Bit!! Mit den Optionen "/register /codebase /tlb" ?
Floyd 17.06.2011
Warum soll ich die regasm von .NET V2 verwenden?
Wenn ich diese nehme, kommt die Meldung, dass die DLL angeblich keine .NET-Assembly ist.
Ich verwende deshalb regasm aus dem Ordner "C:\Windows\Microsoft.NET\Framework64\v4.0.30319" also von .NET V4.
Carsten Ilwig 17.06.2011
Sorry, das mit .Net 4 hab ich überlesen. Mir war eher wichtig auf die 64-Bit-Version hinzuweisen. Viele machen den Fehler und benutzen nur die 32-Bit Version und wundern sich dann warum es im 64-Bit-Modus nicht funktioniert.
Floyd 17.06.2011
Kein Problem. Hätte ja auch durchaus die Fehlerursache sein können.
Carsten Ilwig 17.06.2011
@carsten: nein, das meinte ich nicht. sondern dieses:

http://www.developmentnow.com/g/21_2004_1_0_0_105068/ComInterfaceType-InterfaceIsDual.htm

nabuchodonossor 20.06.2011
aber ganz abgesehen davon: ich arbeite auf einer 64 bit kiste mehr oder weniger problemlos mit vb6 UND com interopt. natürlich habe ich als target x86, und daher in vb6 keine probleme mit com dlls die unter .net erstellt wurden.
nabuchodonossor 20.06.2011
0
Dein Beispielcode stammt leider aus VB.NET und funktioniert nicht in C#.
Da ist "ComClass" unbekannt und ein Äquivalent dafür in C# kann ich nicht finden.

>Anschließend solltest du über COM zumindest schonmal deine Klasse sehen.
Wie stelle ich das an, mir von einer via COM?
Ich kenne mich mit COM-Zugriffen bisher überhaupt nicht aus.
17.06.2011
Carsten Ilwig 337 1 6
0
Hast du in der Assembly.cs die folgenden Anweisungen drin stehen?

[Assembly: ComVisible(True)]

//The following GUID is for the ID of the typelib if this project is exposed to COM
[Assembly: Guid("86798dbb-1cc6-4799-8c05-3994daa3db81")]
17.06.2011
Floyd 14,6k 3 9
Ja, diese Angaben stehen in der Assembly.cs alle drin.
Carsten Ilwig 17.06.2011
0
Mal ins Blaue rein vermutet und in die Diskussion geworfen: vielleicht ist es gar nicht möglich, aus einer C#-Dll heraus eine andere C#-Dll unter Com aufzurufen? Denn eigentlich braucht es Com zwischen zwei C#-Dlls gar nicht, die Dlls sollten sich auch ohne Com verstehen. Als ich mit C#-Com angefangen habe (das ist schon ein paar VS-Versionen und ein paar Frameworks her), da bekam ich bei einer solchen Konstellation mal eine Fehlermeldung, die mich auf so etwas hinwies, verbunden mit der Aufforderung, dass ich die Dll direkt referenzieren soll. Könnte das die Ursache für dein Problem sein?
17.06.2011
KN 1,7k 1 8
KN 1,7k 1 8
Wäre aber unlogisch. Wenn die C#-DLL die richtigen COM-Schnittstellen exportiert und der C#-Client mit C# umgehen kann (wobei wir wissen das letzteres definitiv zutrifft) ist es egal in welcher Sprache die COM-DLL geschrieben ist.
Floyd 17.06.2011
Wenn ich die C#-Dll aus einer anderen C#-Anwendung über COM heraus aufrufe, weiß die aufrufende Anwendung doch gar nicht, dass es sich bei dem COM-Object ja eigentlich um eine C#(.NET)-DLL handelt, da der Zugriff ja reinweg über die Typbeschreibung erfolgt.
Außerdem bekomme ich genau den gleichen Fehler "Einsprunkpunkt nicht gefunden.." wenn ich versuche in VB6 die C#-DLL zu verwenden.
Aber trotzdem Danke für Deinen Hinweis.
Carsten Ilwig 17.06.2011
Ja, erscheint mir auch nicht unbedingt logisch. Aber ich erinnere mich genau an diesen Hinweis damals. Wollte das nur mal einwerfen.
KN 17.06.2011
Ich habe das noch einmal mit meinem Kollegen diskutiert, der in Com ebenfalls sehr erfahren ist. Er sagt auch, dass die P/Invoke-Funktionalität in diesem Fall gar nicht greift. Die AddFunction ist ja ein nicht-statisches Member der Klasse. Du müsstest also erst einmal die Klasse instantiieren, bevor du auf eine Methode darin zugereifen kannst. Uns ist keine P/Invoke-Funktionalität bekannt, die sowas abbildet. Warum willst du denn zwischen 2 C#-Dlls unbedingt über Com zugreifen? Das geht doch auch direkt.
KN 17.06.2011
Mir ist noch etwas eingefallen: Du willst entweder eine C#-Dll unter Com veröffentlichen, um sie dann unter einer anderen Umgebung aufzurufen, oder du willst den umgekehrten Weg gehen. Sehe ich das richtig? Wenn das so ist: welche Richtung willst du gehen, und welches ist die andere Umgebung? Wir haben hier schon vieles in allen möglichen Richtungen gemacht. Vielleicht kann ich dir ja für das, was du letztendlich willst, einen Tipp geben.
KN 17.06.2011
Richtig, diese Konstellation, dass ich eine .NET-DLL in einer anderen .NET-Anwendung via COM aufrufe dient nur dem grundsätzlichem Funktionstest. Das eigentliche Ziel ist, auf die Funktionen aus der .NET-DLL von VB6 aus zuzugreifen.
Da aber die VB6-IDE nicht auf einem 64bit-System läuft (meinem Arbeitssystem) habe ich mir diesen Testfall konstruiert um nicht ständig zwischen den Systemen wechseln zu müssen.
Wenn ich aus der .NET-Testanwendung heraus via COM auf meine Funktion in der .NET-DLL zugreifen kann, dann klappt es hoffentlich auch von VB6 aus.
Carsten Ilwig 17.06.2011
Mit VB6 haben wir noch nix gemacht, da kann ich nicht weiter helfen. Ich denke, dass P/Invoke hier das falsche Instrument ist. Schon alleine wegen der nicht-statischen Methode. Wenn wir .Net-Dlls mit Klassen und nicht-statischen Methoden aus anderen Umgebungen heraus aufrufen, instantiieren wir auch immer zuerst ein Objekt der Klasse, bei dem wir dann die Methode aufrufen. Ich habe deinen Code hier bei mir ausprobiert und bekomme den gleichen Fehler. Ich habe nichts gefunden, wie ich die Methode außen sichtbar machen kann (Anschauen mit Depends, einem Tools, das bis 2008 beim VS dabei war).
KN 17.06.2011

Stelle deine .net-Frage jetzt!
TOP TECHNOLOGIES CONSULTING GmbH