Laden...

Rainbird Zyan: Exception

Erstellt von caldicot vor 13 Jahren Letzter Beitrag vor 12 Jahren 4.607 Views
C
caldicot Themenstarter:in
51 Beiträge seit 2010
vor 13 Jahren
Rainbird Zyan: Exception

Hi,

ich bin's schon wieder 😃
Ich bin über ein neues Problem gestolpert und weiß nicht weiter.

Mittels Zyan veröffentliche ich eine Klasse die eine eigene Exception werfen kann.

Die Exception schaut so aus:


    [Serializable()]
    public class UserWrongPasswordException : ArgumentException
    {
        public UserWrongPasswordException()
            : base()
        {
        }

        public UserWrongPasswordException(string message)
            : base(message)
        {
        }

        protected UserWrongPasswordException(SerializationInfo info, StreamingContext sctxt)
            : base(info, sctxt)
        {
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }
    }

Wenn auf dem Client die Methode der Klasse aufgerufen wird, die diese Exception wirft, bekomme ich eine TargetInvocationException: "Ein Aufrufziel hat einen Ausnahmefehler verursacht".
In der InnerException steht "Der Wert liegt außerhalb des erwarteten Bereichs".
Ich kann aber mit der Fehlermeldung nichts anfangen. Welcher Wert / Bereich ist denn gemeint?

Mittels Debug sehe ich, dass die Exception Klasse aufgerufen wird, und erst danach der Fehler zustande kommt.

Wenn ich Google zu Exception und Remoting frage, finde ich nur den Hinweis, dass die Klasse Serializable sein muss, Konstuktor und GetObjectData beinhalten muss.

Hab ich irgendwas falsch gemacht?
Oder funktionieren auch Exception nicht so leicht via Remoting?

Danke für Eure Hilfe.
Caldi

3.728 Beiträge seit 2005
vor 13 Jahren

Hallo caldicot,

das ist kein Zyan-Bug, sondern eine besonderheit von Remoting. Exceptions müssen bestimmte Methoden für die Serialisierung haben, damit sie korrekt übers Netzwerk übertragen werden können. Du hast da ein paar Kleinigkeiten vergssen.

So sollte eine remotingfähige Exception eigentlich aussehen:


[Serializable]
public class UserWrongPasswordException : Exception, ISerializable
{	
	public UserWrongPasswordException()
	{
	}
		
	public UserWrongPasswordException(string message) : base(message)
	{
	}

	public UserWrongPasswordException(string message, Exception innerException) : base(message, innerException)
	{
	}

	protected UserWrongPasswordException(SerializationInfo info, StreamingContext context) : base(info, context)
	{
	}

	public override void GetObjectData(SerializationInfo info, StreamingContext context)
	{
		base.GetObjectData(info, context);
	}
}

Wenn es trotzdem nicht klappt, dann poste bitte den Stack Trace.

Du weisst aber schon, dass Zyan ein fertiges Authentifizierungssystem mitbringt, oder?
Du kannst entweder fertige Authentifizierungsanbieter verwenden, oder mit wenigen Handgriffen eigene Authentifizierungsanbieter schreiben und verwenden.

Ein eigener Authentifizierungsanbieter muss nur die Schnittstelle Zyan.Communication.Security.IAuthenticationProvider implementieren, welche nur eine einzige Methode namens Authenticate hat.

Hier ein grober Überblick über Authentifizierungsanbieter bei Zyan: Zyan - Documentation

C
caldicot Themenstarter:in
51 Beiträge seit 2010
vor 13 Jahren

Hallo Rainbird,

und wieder herzlichen Dank für Deine schnelle und ausführliche Antwort.

Ich habe die Klasse entsprechend deinem Vorbild angepasst.
Die schaut jetzt so aus:


    [Serializable]
    public class UserWrongPasswordException : Exception, ISerializable
    {
        public UserWrongPasswordException()
        {
        }

        public UserWrongPasswordException(string message)
            : base(message)
        {
        }

        public UserWrongPasswordException(string message, Exception innerException)
            : base(message, innerException)
        {
        }

        protected UserWrongPasswordException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }
    }

Funktioniert leider immer noch nicht.

Stack Trace:

System.Reflection.TargetInvocationException wurde nicht behandelt.
Message=Ein Aufrufziel hat einen Ausnahmefehler verursacht.
Source=Zyan.Communication
StackTrace:
Server stack trace:
bei Zyan.Communication.ComponentInvoker.Invoke(String interfaceName, ArrayList outputPinCorrelationSet, String methodName, Object[] args)
bei System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
bei System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)
Exception rethrown at [0]:
bei System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
bei System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
bei Zyan.Communication.IComponentInvoker.Invoke(String interfaceName, ArrayList outputPinCorrelationSet, String methodName, Object[] args)
bei Zyan.Communication.ZyanProxy.InvokeRemoteMethod(IMethodCallMessage methodCallMessage)
Exception rethrown at [1]:
bei System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
bei System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
bei Shared.Userverwaltung.IUserverwaltung.login(String userName, String password)
bei Login.LoginForm.checkLogin() in C:\Users...\Login\LoginForm.cs:Zeile 79.
bei Login.LoginForm.bt_login_Click(Object sender, EventArgs e) in C:\Users...\Login\LoginForm.cs:Zeile 110.
bei System.Windows.Forms.Control.OnClick(EventArgs e)
bei System.Windows.Forms.Button.OnClick(EventArgs e)
bei System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
bei System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
bei System.Windows.Forms.Control.WndProc(Message& m)
bei System.Windows.Forms.ButtonBase.WndProc(Message& m)
bei System.Windows.Forms.Button.WndProc(Message& m)
bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
bei System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
bei System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
bei System.Windows.Forms.Application.ComponentManager.System.Windows.Forms. UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
bei System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
bei System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
bei System.Windows.Forms.Application.RunDialog(Form form)
bei System.Windows.Forms.Form.ShowDialog(IWin32Window owner)
bei System.Windows.Forms.Form.ShowDialog()
bei Login.LoginForm.ShowDialog() in C:\Users...\Login\LoginForm.cs:Zeile 46.
bei Client.Form1..ctor() in C:\Users...\Client\Form1.cs:Zeile 25.
bei Client.Program.Main() in C:\Users...\Client\Program.cs:Zeile 18.
bei System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
bei System.Threading.ThreadHelper.ThreadStart()
InnerException: Shared.Userverwaltung.UserWrongPasswordException
Message=Eine Ausnahme vom Typ "Shared.Userverwaltung.UserWrongPasswordException" wurde ausgelöst.
Source=Server
StackTrace:
bei Server.Userverwaltung.Userverwaltung.login(String userName, String password) in C:\Users...\Server\Userverwaltung\Userverwaltung.cs:Zeile 135.
InnerException:

Dass Zyan ein Authentifizierungssystem dabei hat, habe ich gelesen.
Leider habe ich es nicht verstanden X(
Zu meiner Verteidung muss ich sagen, dass ich erst im 3. Semester bin 😉

In der Klasse die, die registrierte Benutzer speichert, habe ich das IAuthenticationProvider implementiert und die Methode Authenticate() implementiert.

Ich hab nicht verstanden wie ich mit dem AuthRequestMessage umgehen muss.
Das Objekt beinhaltet ja die vom User angegebenen User-Daten?
Muss der Client in den HashTable User und Password setzten?
Z.B. so:


authMessage.Credentials["name"] = "myUserName";
authMessage.Credentials["pw"] = "myPassword";

entsprechend dann serverseitig die Überprüfung...

Sorry ist bestimmt die absolute n00b Frage. Ich sehs einfach nicht...

Im Moment gebe ich bei erfolgreichem Login das MyUser Objekt des eingeloggten Users zurück.
Damit ich auf dem Client die Berechtigungsstufe abfragen kann.
Wird sowas in echt anders gelöst oder wie könnte ich das mit Zyan realisiseren?

Ich steh wie immer auf dem Schlauch 🙂
Danke für Deine Hilfe UND Deine Geduld.
caldi

3.728 Beiträge seit 2005
vor 13 Jahren

Hallo caldicot,

ich habe Deinen Exception-Code grade bei mir in ein Testprojekt eingefügt. Bei mir wird die Exception einwadfrei geworfen und kommt, wie erwartet beim Client an.

Im Stack Trace fehlen die Zeilennummern. Ohne die ist es schwer, das Problem einzugrenzen. Im Anhang findest Du eine DEBUG-Version von Zyan. Bitte verwende mal die und stelle sicher, dass auch die Datei Zyan.Communication.PDB immer mitkopiert wird (Die PDB-Datei enthält die Debug Symbole).
Dann solltest Du Zeilennummen im Stack Trace sehen. Wenn Du mir den Stack Trace mit Zeilennummern postest, weiss ich sofort, wo es geknallt hat, und ob es womöglich ein Bug in Zyan ist.

Was passiert genau in LoginForm.cs:Zeile 79 ?

Tipp: Mit [Strg] + [G] kannst Du in Visual Studio direkt zu einer bestimmten Zeilennummer springen.

Ich hab nicht verstanden wie ich mit dem AuthRequestMessage umgehen muss.

Im Client musst Du damit gar nichts tun. Beim Herstellen einer Verbindung mit ZyanConnection wird automatisch eine AuthRequestMessage erzeugt und der internen Logon-Methode des Hosts übergeben. Dieser leitet sie weiter an die hinterlegte AuthenticationProvider-Implementierung (also z.B. Deine eigene). Die AuthRequestMessage enthält eigentlich nur eine Hashtable mit Credentials (z.B. Benutzername und Passwort). Die fragst Du in der Authenticate-Methode Deines AuthenticationProviders ab und machst damit die Anmeldeprüfung. Wenn Deine Benutzer und Passwörter z.B. in einer SQL Datenbank liegen, kannst Du die Credentials vom Client z.B. einfach mit einer SELECT-Abfrage gegen die Datenbank prüfen.

Ein Hello Word-AuthenticationProvider sieht z.B. so aus:


public class HelloWorldAuthProvider : IAuthenticationProvider
{
    public AuthResponseMessage Authenticate(AuthRequestMessage authRequest)
    {
        if (!authRequest.Credentials.ContainsKey("username"))
            throw new System.Security.SecurityException("Kein Benutzername angegeben.");

        if (!authRequest.Credentials.ContainsKey("password"))
            throw new System.Security.SecurityException("Kein Kennwort angegeben.");

        string userName = (string)authRequest.Credentials["username"];
        string password = (string)authRequest.Credentials["password"];

            
        if (userName=="Hello" && password=="World")
        {
            // Erfolgreiche Anmeldung zurückmelden
            return new AuthResponseMessage() 
            {
                Success=true, // Anmeldung erfolgreich
                AuthenticatedIdentity = new System.Security.Principal.GenericIdentity("Hello")
            };
        } 
           
        // Fehlgeschlagene Anmeldung zurückmelden
        return new AuthResponseMessage() 
        {
            Success=false, // Anmeldung nicht erfolgreich
            ErrorMessage="Benutzername oder Password sind ungültig"
        };            
    }
}

Bei erfolgreicher Anmeldung muss der zurückzugebenden AuthResponseMessage ein Identity-Objekt übergeben werden. Das ist ein Standardobjekt aus dem .NET Framework, welches eine Benutzeridentität beschreibt. Am einfachsten ist die Verwendung des vorgefertigten GenericIdentity, welches einfach nur den Benutzernamen speichert.

Das Objekt beinhaltet ja die vom User angegebenen User-Daten?

Ja genau.

Muss der Client in den HashTable User und Password setzten?

Ja muss er. Und zwar wird die Hashtable dem Konstruktor der ZyanConnection übergeben (Parameter credentials).

Du musst das Login-Formular also starten, bevor Du die ZyanConnection erzeugst.

Nach erfolgter Anmeldung wird auf dem Server eine Sitzung erzeugt. Diese kann z.B. innerhalb von veröffentlichten Serverkomponenten abgefragt werden (ServerSession.CurrentSession). So kannst Du in Deinen Serverkomponenten ganz leicht prüfen, von welchem Benutzer sie gerade aufgerufen wurden.

Im Moment gebe ich bei erfolgreichem Login das MyUser Objekt des eingeloggten Users zurück.
Damit ich auf dem Client die Berechtigungsstufe abfragen kann.
Wird sowas in echt anders gelöst oder wie könnte ich das mit Zyan realisiseren?

Du schreibst eine Serverkomponente, welche die Sicherheitsstufe des angemeldeten Benutzers prüft. Diese kannst Du am Client aufrufen, um die Sicherheitsstufe abzurufen. So würde die Hello World-Version davon aussehen:


public enum PermissionLevel
{
    None = 0,
    User,
    PowerUser,
    Admin
}

public interface IPermissionManager
{
    public PermissionLevel CheckPermissionLevel();
}

public class PermissionManager: IPermissionManager
{
    public PermissionLevel CheckPermissionLevel()
    {
        // Benutzername des aufrufenden Benutzers ermitteln
        string userName = ServerSession.CurrentSession.Identity.Name;

        switch (userName)
        {
            case "Fritz": return PermissionLevel.User;
            case "Annegret": return PermissionLevel.PowerUser;
            case "Caldicot": return PermissionLevel.Admin;                
        }
        return PermissionLevel.None;
    }
}

Am Client musst Du dafpr nur einen Proxy erzeugen lassen und CheckPermissionLevel aufrufen. Durch die Sitzungsverwaltung weiss die Serverkomponente automatisch welcher Benutzer die Anfrage gestellt hat.

C
caldicot Themenstarter:in
51 Beiträge seit 2010
vor 13 Jahren

Hallo Rainbird,

Ich kann mich gar nicht genügend bedanken 😃
Wirklich toll, wie Du mich unterstützt.

Nachdem Zyan ein besseres Authentifizierungssysteme integriert hat, als ich jemals selber zusammen stümpern könnte, habe ich gleich versucht dieses zu integrieren. Mit Erfolg.

Sowohl das Session Managment als auch das Permission Managment wird jetzt mit Zyan behandelt.
Danke für Deine sehr hilfreiche Beschreibung.

Ein kleines Problem hab ich noch:
Wenn Zyan eine WebException schmeißt, weil der Server nicht erreichbar ist, dann fange ich die ab und die LoginForm wird mit dem Fehler Hinweis erneut aufgerufen.

Wenn der User jetzt die LoginForm schließt, dann wird bei dem FormClosing-Event Enviroment.Exit(0) aufgerufen.
Allerdings stürzt hier das Programm ab, mit dem Hinweis, dass die WebException nicht behandelt wurde.

Ich verstehe nicht ganz warum, weil die ja nur beim Instanziieren von Zyan ausgelöst wird, falls kein Server zu erreichen ist?

Danke
caldi

P.S.
Dein Notification System habe ich noch nicht geschafft zu testen, mache ich im Laufe des Tages...

3.728 Beiträge seit 2005
vor 13 Jahren

Hallo caldicot,

schön dass es mit der AUthentifizierung geklappt hat.

Environment.Exit ist was für Konsolen-Anwendungen. In einer Windows.Forms-Anwendung solltest Du stattdessen System.Windows.Forms.Application.Exit verwenden.

Ich könnte Dir besser weiterhelfen, wenn Du näher beschreiben würdest, wie Dein Anmeldeformular aufgebaut ist.

P.S.: Danke für die Blumen.

E
180 Beiträge seit 2010
vor 12 Jahren
ähnliches Problem

bisher lief alles super mit Zyan, doch jetzt müssen Listen übertragen werden, welche zu dem selben Fehler führen wie beim Vorredner. Hab das Problem jetzt definitiv auf die generic Listen zurückführen können. Frage: Wie kann man Listen mit zyan übertragen?

Gruß


    [Serializable]
    public class BenutzerListenElemente {        
        #region Attribute
        private List<BenutzerListenElement> Liste;
        #endregion
        #region Konstruktor
        public BenutzerListenElemente() {
            this.Liste = new List<BenutzerListenElement>();
        }
        #endregion
        #region Properties
        public List<BenutzerListenElement> Get_Liste {
            get { return this.Liste; }
        }
        #endregion
    }
    [Serializable]
    public class BenutzerListenElement {
        #region Attribute
        private uint ID;
        private string Name;
        #endregion
        #region Konstruktor
        public BenutzerListenElement(uint ID,string Name) {
            this.ID = ID;
            this.Name = Name;
        }
        #endregion
        #region Properties
        public uint Get_ID {
            get { return this.ID; }
        }
        public string Get_Name {
            get { return this.Name; }
        }
        #endregion
    }

   Interfacemethode: 
   BenutzerListenElemente Verwaltung_BenutzerListe(uint UserID,string SessionID);

Fehler: Ein Aufrufziel hat einen Ausnahmefehler verursacht.

3.728 Beiträge seit 2005
vor 12 Jahren
Probleme mit Generics

Hallo Equilibrium,

welche Version von Zyan hast Du im Einsatz?
Falls Du noch min Zyan 1.x arbeitest, solltest unbedingt auf Zyan 2.0 umstellen. Der Verdrahtungscode wurde dort komplett überarbeitet.

Zyan 2.0 bekommst Du hier: http://zyan.codeplex.com/releases/view/62104

Falls das Problem mit Zyan 2.0 auch auftritt, versuche ich es bei mir zu reproduzieren und werde Dir Bescheid geben, wenn es eine Lösung gibt.

Edit: Habe eben eine kleine Testanwendung geschrieben, konnte den Fehler aber nicht reproduzieren (Verwendete Zyan Version: 2.0). Das BenutzerListenElemente-Objekt wurde bei mir ohne Probleme serialisiert. Code der Testanwendung findest Du in der ZIP-Datei im Anhang.

Du solltest folgendes Überprüfen:*Liegen auf Client und Server verschiedene Versionen der Shared-Assembly? (Beide Kommunikationspartner müssen über die selben gemeinsamen Assemblies verfügen; Also wirklich identische Version) *Tritt das Problem in Verbindung mit Callbacks oder Events auf? *Welche .NET Framework-Version verwendest Du?

Gruß

Raibird

E
180 Beiträge seit 2010
vor 12 Jahren
Hi

war mein fehler, saß schon 9h dran an dem code und irgendwie den fehler vor lauter zeilen nicht mehr abends gesehen, lag am server selber (Sqlite Syntax fehler), hatte also nix mit zyan zutun, es lief wunderbar. am nächsten morgen sprang mir das problem dann förmlich in die augen 😃

trotzdem thx für die schnelle antwort, super projekt übrigens das zyan. spart echt viel arbeit was die kommunikation angeht und vorallem mit events, was viele probleme vom tisch fegt 😃

Gruß