Laden...

Problem mit Remoting und Com-Klasse

Erstellt von spikis vor 17 Jahren Letzter Beitrag vor 16 Jahren 10.382 Views
S
spikis Themenstarter:in
7 Beiträge seit 2005
vor 17 Jahren
Problem mit Remoting und Com-Klasse

Hallo,

ich hab ein Problem mit Remoting, bei dem ich nicht mehr weiterkomme.
Ich versuch es mal zu beschreiben:

Es gibt eine Klasse (RemoteClass), die im Singelton-Modus für eine serverseitige Aktivierung registriert wird. In dieser Klasse gibt es eine Methode(getAllocationUtility), die ein neues Objekt der Klasse AllocationUtility zurück gibt. Diese Klassen sind in einer Klassenbibliothek, die vom Server und Client verwendet wird.

Der Client ist eine Com-Klasse in der per Remoting auf das RemoteClass Objekte auf dem Server zugegriffen wird. Wenn ich im Client die Methode getAllocationUtility von RemoteClass aufrufe bekomme ich die Fehlermeldung: "Das Rückgabeargument hat einen ungültigen Typ."

Zum Testen haben ich mal ein Programm geschrieben, das ohne die Com-Klasse auf den Server zugreift und da funktioniert alles wunderbar. Der Aufbau der Verbindung ist in dem Programm genauso wie in der Com-Klasse.

Ich hoffe es ist einigermaßen rüber gekommen, was ich meine.

Ich wäre für jeden Tipp dankbar!

Gruß
spikis

3.728 Beiträge seit 2005
vor 17 Jahren
COM-Typsystem

Das Problem ist, dass der COM-Client den Typ "AllocationUtility" nicht kennt. Damit er ihn kennt, muss die betreffende Assembly für COM-Interop registriert sein, er muss [ComVisible(true)], [ClassInterface(ClassInterfaceType.None)], einen [Guid("xxxxxxxxx-xxxx-xxxxx")] haben und eine COM-Schnittstelle implementieren.

Ich arbeite sehr viel mit Hybrid-Anwendungen (Teilweise COM / Teilweise .NET). Die Erfahrung hat gezeigt, dass es nicht gut ist, wenn man beide Welten zusammenwurstelt. Ich würde deshalb eine eigene kleine Wrapper-API für den COM-Client schreiben. Diese muss unbedingt in einer separaten Assembly liegen. Das hat den Vorteil, dass Du beim entwickeln deiner Anwendung auf COM überhaupt keine Rücksicht nehmen musst (Keine Guids, keine COM-Schnittstellen, nicht auf Überladung und parametrisierte Konstruktoren verzichen müssen, nicht auf Datentypen verzichen müssen, die es im COM-Typsystem nicht gibt; Wie z.B. Image, DataSet, DataTable, ...). Die COM-Warpper-Assembly kapselt den Zugriff speziell für die COM-Clients.

Hier siehst Du, wie man sowas baut:
Warum ist Access so verhaßt?
COM Interface für die eigene Application

S
spikis Themenstarter:in
7 Beiträge seit 2005
vor 17 Jahren

Hallo Rainbird,

erstmal vielen Dank für Deine Hilfe!

Zuerst hatte ich in der Com-Klasse nur einen Aufruf auf dem Server über Remoting gemacht und alles weitere wurde dann am Server ausgeführt. Da hatte ich nur das Problem, das sowie ich einen Dialog geöffnet habe, ich am Ende der aufgerufenen Methode den Fehler "InvalidOperationException wurde nicht behandelt. Beim Vorgang zum Rückgängigmachen wurde ein Kontext gefunden, der sich vom Kontext des entsprechenden Set-Vorgangs unterschied. Möglicherweise war der Kontext für den Thread Set und wurde nicht zurückgesetzt (rückgängig gemacht)." erhalten habe.

Da ich die Ursache des Fehlers nicht gefunden habe, habe ich den Aufruf des Dialogs in den Client gepackt, was auch wunderbar funktioniert hat, solange der Client nicht eine Com-Klasse ist. Hast Du eventuell eine Idee wodurch der Fehler aufgetreten sein könnte?

In den Eigenschaften des Assemblys, in der das AllocationUtility ist, habe ich die Haken für "Assembly Com-sichtbar machen" und "Für Com-Interop registrieren" gesetzt. Die Klasse implementiert ein Interface und die Einträge über der Klasse ComVisible, Guid und ClassInterface sind auch da. Die Registrierung scheint aber noch nicht so richtig zu klappen. Mit regasm.exe bekomme ich keinen Fehler aber der Fehler mit dem unbekannten Typ kommt immer noch.

3.728 Beiträge seit 2005
vor 17 Jahren
Typen

Das kommt, wenn Du in der Schnittstelle Typen verwendest, die es im COM-Typsystem nicht gibt. Welche Typen verwendet Du für Parameter und Rückgabewerte?

S
spikis Themenstarter:in
7 Beiträge seit 2005
vor 17 Jahren

Ich verwende list<string> und eine Klasse die von List<EigeneKlasse> erbt.
Gibt es denn eine Auflistung was ich in Com verwenden kann?
Oder verstehe ich da jetzt was falsch?

3.728 Beiträge seit 2005
vor 17 Jahren
Array

Generische Typen wie List<xxx> gibt es in der COM-Welt nicht.
Am besten verwendest Du statt List<string> einfach string[] in Deiner COM-Wrapper Klasse. Für die "Eigene Klasse" müsstest Du eine COM-Umsetzung in Deiner Wrapper-Assembly schreiben. Und die Liste als EigeneKlasse[] zurückgeben.

Wenn es sich aber nur um Datenaustausch handelt, nimmst Du am besten ein verbindungsloses ADODB.Recordset (COM-Verweis "adodb" einbinden).

Tja, die ganzen schönen neuen .NET Sachen funktionieren nicht für COM-Clients (Hätte COM das alles geboten, wäre .NET wahrscheinlich nie erfunden worden). Deshalb ist es sehr wichtig, die COM-Welt über einen Wrapper an die eigene .NET Anwendung anzubinden, statt direkt. Sonst musst Du auf sehr viel komfort verzichten.

S
spikis Themenstarter:in
7 Beiträge seit 2005
vor 17 Jahren

Ich werde mal versuchen das so umzusetzen.

Vielen Dank für Deine Hilfe!

spikis

193 Beiträge seit 2005
vor 16 Jahren

*rauskram*
ich habe ebenfalls den Fehler, der im ersten Beitrag genannt ist:
Beim Vorgang zum Rückgängigmachen wurde ein Kontext gefunden, der sich vom Kontext des entsprechenden Set-Vorgangs unterschied. Möglicherweise war der Kontext für den Thread &quot;Set&quot; und wurde nicht zurückgesetzt (rückgängig gemacht)

Ich benutze jedoch keinerlei Com-Objekte (soviel ich überschauen kann 😉).
Das ganze tritt bei mir wie folgt auf (Objektiv gesehen).
Der Server schickt eine "Anfrage" an einen Client. Auf dem Client poppt ne Form auf, die ausgefüllt werden soll und anschließend das Ergebnis zurückliefert (in meinem Fall den Index eines Eintrages aus einer Liste).
Das ganze sieht so aus:


        public int MessageboxQuestionList(requestToClientInformationClass ret)
        {
            MsgBox msg = new MsgBox();
            msg.MsgCaption = "Test";
            msg.MsgText = "Test Test Test";
            string[] test = new string[3];
            test[0] = "1";
            test[1] = "2";
            test[2] = "3";
            msg.MsgList = test;
            //msg.Show();
            if (msg.ShowDialog() == DialogResult.OK)
                return (msg.Response);
            else
                return (-1);
        }

requestToClientInformationClass ist eine eigene Klasse, wo die sachen drin stehen, die hier noch manuell angegeben werden (hat jetzt testgründe, warum ich das nicht direkt so auslese und übergebe).

So, bis der Server die Anfrage abgeschickt hat, der Client diese Prozedur aufgerufen hat und die Form angezeigt wird ist alles OK.
Der Fehler tritt nun auf, wenn der Wert der oben gezeigten Prozedur zurück an den Server geschickt werden soll.
Der Debuger makiert die letzte }.
Ich hab keine Ahnung, was nun gemacht werden soll, da mir die Fehlermeldung ungefähr so viel sagt wie 1+1=3 => gar nix bzw. nichts, womit ich etwas anfangen könnte.

Achja: der Fehler kommt nicht beim Komplimieren, sondern bei Debuggen, wenn der Client läuft.
Der Server bekommt trotz der Fehlermeldung seine Antwort auf seine "Frage".

Visit me @ www.beremote.net

3.728 Beiträge seit 2005
vor 16 Jahren
Fehler eingrenzen

Hallo Hunv,

was genau für eine Ausnahme bekommst Du?
Wie werden die Objekte auf Deinem Remoting Host aktiviert?*Serverseitig SingleCall *Serverseitig Singleton *via Direct Remoting *Clientseitig aktiviert

Du sprichst von Threads im Client und von Kontexten. Warum arbeitest Du im Client mit mehreren Threads?
Was tust Du im Client, um diese Threads zu synchronisieren?

193 Beiträge seit 2005
vor 16 Jahren

die genaue Fehlermeldung lautet:
InvalidOperationException was unhandled (VS-Meldung, desshalb englisch - mein Framework ist deutsch und spuckt zu dieser Überschrift folgendes aus🙂
Beim Vorgang zum Rückgängigmachen wurde ein Kontext gefunden, der sich vom Kontext des entsprechenden Set-Vorgangs unterschied. Möglicherweise war der Kontext für den Thread "Set" und wurde nicht zurückgesetzt (rückgängig gemacht).

ich glaube das ganze ist "Serverseitig Singleton" (bin mir nicht 100%ig sicher).
Wie/Wo arbeite ich mit mehreren Threads? Ich hätte jetzt gesagt, dass es nicht so sei.

Visit me @ www.beremote.net

3.728 Beiträge seit 2005
vor 16 Jahren
Remote-Objekt

Du solltest wissen, wie Deine eigene Anwendung funktioniert. Ist das Remote-Objekt nun Singelton oder nicht?

Ohne Code zu sehen, werde ich Dir nicht weiterhelfen können.

Ist die Klasse des Remote-Objekts von MarshalByRef oder von ContextBoundObject abgeleitet (oder ist es vielleicht eine ServicedComponent)?
Wie sieht die Klasse des Remote-Objekts aus?

Der Client sollte nicht die Implementierung des Remote-Objekts kennen, sondern nur dessen Schnittstelle. Das ist sicherheitstechnisch sinnvoll und erleichert die Versionierung der Anwendung.
Du solltest Deine Anwendung so schreiben, dass die Remoting-Kommunikation mit Schnittstellen und nicht mit Klassen gemacht wird. Trotzdem müsste es auch mit Klassen funktionieren.

Machst Du die Remoting-Konfiguration per App.config oder per Code? Wie sieht die Remoting-Konfiguration von Client und Host aus?

Das mit den Client-Threads habe ich wohl falsch verstanden. Mit mehreren Threads hast Du trotzdem zu tun, denn jede Client-Anfrage an Dein Singleton-Objekt (wenn es denn wirklich Singleton aktiviert ist) geht nämlich in einem separaten Thread ein. Wenn dieses Objekt Daten über mehrere Zugriffe hinweg speichert, musst Du den Zugriff der einzelnen Threads auf diese Daten synchronisieren (z.B. mit lock-Blöcken).

193 Beiträge seit 2005
vor 16 Jahren

Ist die Klasse des Remote-Objekts von MarshalByRef oder von ContextBoundObject abgeleitet (oder ist es vielleicht eine ServicedComponent)?

  • von MarshalByRef

Der Client sollte nicht die Implementierung des Remote-Objekt kennen.

  • tut er auch nicht, da es über Schnittstellen läuft

  • Die remoting konfig läuft über den code nicht über App.config

Ich hab mal ein bisschen rumprobiert.
Wenn ich den Wert sofort zurückgebe (also return(0)), dann kommt der Fehler nicht.
Der Fehler kommt nur, wenn ich die Form aufrufe, in der der User noch etwas angeben soll.

Visit me @ www.beremote.net

3.728 Beiträge seit 2005
vor 16 Jahren
Forms-Host

Ist der Remoting-Host eine Windows.Forms-Anwendung? Öffnest Du einen Dialog innerhalb eines entfernten Methodenaufrufs?

193 Beiträge seit 2005
vor 16 Jahren

ja, aber das ganze wird aus einer klasse raus aufgerufen und ja

Visit me @ www.beremote.net

193 Beiträge seit 2005
vor 16 Jahren

keine Idee?

Visit me @ www.beremote.net

3.728 Beiträge seit 2005
vor 16 Jahren
Dialog

Du solltest innerhalb von Methoden, die entfernt aufgerufen werden, keine Dialoge öffnen, die auf Benutzereingabe warten (ShowDialog). Es ist nicht absehbar, wie lange der Benutzer braucht um eine Eingabe zu machen (Vielleicht lässt er den Dialog ja uach offen und geht einen Kaffee trinken). Der entfernte Client wird irgendwann denken, dass sein Server nicht mehr reagiert (Timeout).

Der Client sollte dem Server lieber einen Objektverweis übergeben, damit der Server "zurückrufen" kann, wenn der Benutzer seine Eingaben gemacht hat.

193 Beiträge seit 2005
vor 16 Jahren

das wäre ne idee, mal ausprobieren.

Danke für deine Hilfe.

Visit me @ www.beremote.net

193 Beiträge seit 2005
vor 16 Jahren

Also ich hab das jetzt gemacht


public void MessageboxQuestionList(requestToClientInformationClass ret, field _field, int x, int y)
        {
            MsgBox msg = new MsgBox();
            msg.MsgCaption = "Heimatsiedlung des Gebäudes wählen";
            msg.MsgText = ret.Message;
            msg.MsgList = ret.List;            

            if (msg.ShowDialog() == DialogResult.OK)
            {
                string[] answ = msg.strResponse.Split(' '); //answ = "xxx - yyy"
                int[] home = new int[2];
                home[0] = int.Parse(answ[0]);
                home[1] = int.Parse(answ[2]);
                _field.Hometown = home;

                networkModule.tryBuild(_field, x, y);
            }
        }

Der Server wartet nun also nicht mehr auf die antwort, sondern die antwort kann gegeben werden, wann sie will.
Trotzdem kommt der Fehler.

Visit me @ www.beremote.net

193 Beiträge seit 2005
vor 16 Jahren

noch eine Frage:
Weiß jemand zufällig wie diese besch**** Fehlermeldung auf englisch heißt?
Auf deutsch findet man überhauptnix sinnvolles dazu.

Visit me @ www.beremote.net

3.728 Beiträge seit 2005
vor 16 Jahren
Geändert?

Sorry, aber für mich sieht Dein Code aus wie vorher. Was hast Du denn geändert? Ich sehe in Deinem irgends, dass dem Server ein Objekt für den "Rückruf" übergeben wird. Du darfst die Methode die form.ShowDialog enthält überhaupt nicht über Remoting zugänglich machen.

193 Beiträge seit 2005
vor 16 Jahren

Der Unterschied ist, dass vorher der Server auf die Antwort gewartet hat (Rückgabewert war ein int).
Jetzt wartet der Server nicht mehr auf die Antwort (gibt keinen Rückgabewert), sondern es wird eine Komplett neue Anfrage gesendet, wenn die Eingabe erfolgt ist.
Somit tritt das, was du mir n paar Beiträge vorher gesagt hast, nicht mehr auf (das der Server auf die Antwort wartet und der benutzer am Client n Kaffee holen geht).

Wie soll ich die Userabfrage sonst bekommen, wenn ich die Form nicht zugänglich machen darf?

Visit me @ www.beremote.net