Entwickler-Ecke

C# - Die Sprache - Aufruf eines Interface-Members


C#David - Di 12.12.17 12:11
Titel: Aufruf eines Interface-Members
Hey Leute,

ich möchte auf die über die "TCatSysManagerLib.dll" bereitgestellten Funktionen zugreifen. Dazu habe ich mir ein Beispielprogramm von der Firma Beckhoff angeschaut. Beim Debuggen des Codes (Visual Studio 2012) erhalte ich immer den Fehler: "MissingMethodException wurde nicht behandelt..Für dieses Objekt wurde kein parameterloser Konstruktor definiert"

Womit hängt dies zusammen? Eigentlich müssen Interface‘s von einer Klasse ja immer implementiert werden. Code zum Implementieren des Interface’s konnte ich nicht in den Beispielprogramm nicht finden.

Code:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using TCatSysManagerLib;
using System.IO;
using System.Reflection;

namespace LinkVariablesTC2
{
    

    class Program
    {
        private static string _tsmPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\Templates\\Sample.tsm";
        private static TcSysManager _sysManager;      
     
        
        static void Main(string[] args)
        {
            _sysManager = new TcSysManager();
            _sysManager.OpenConfiguration(_tsmPath);
            

            //_sysManager.LinkVariabls("TIPC^Sample^Standard^Outputs^MAIN.bOutput", "TIID^Device 1 (CX1100)^Box 1 (CX1100-BK)^Term 2 (KL2404)^Channel 1^Output");
            _sysManager.SaveConfiguration(_tsmPath);          
           
        }
    }
}


Vielen Dank für eure Hilfe!

Moderiert von user profile iconChristian S.: C#-Tags hinzugefügt


Th69 - Di 12.12.17 13:26

Hallo und :welcome:

aus deinem Code alleine läßt sich der Fehler nicht ableiten. Bei der Exception schau dir am besten mal den Stacktrace mittels "Details anzeigen" an (d.h. welche Methode genau liefert diese Exception?) sowie evtl. die InnerException.

Ich sehe jetzt auch nicht, wo du hier einen "Interface-Member" aufrufst. :gruebel:


C#David - Di 12.12.17 14:01

Im Anhang der Fehler als Bild. Schaue mir gleich dann auch den Stacktrace an.
Na ich will eine in den Interface implementierte Funktion aufrufen....ich dachte, dass man in den Fall von einen Interface-Member sprechen kann?


Th69 - Di 12.12.17 14:13

TcSysManager ist aber wohl eine Klasse (und kein Interface), denn sonst könnte man ja gar nicht mit new ein neues Objekt davon erstellen (es mag aber sein, daß diese Klasse ein Interface implementiert, aber das spielt hier für den Fehler wohl keine Rolle - denn sonst gäbe es schon einen Compiler-Fehler und nicht, wie hier, einen Laufzeitfehler).


Palladin007 - Di 12.12.17 14:53

Also ich würde erst einmal davon ausgehen, dass die Fehlermeldung genau das Problem ist.
Irgendeine Klasse, die Du direkt oder indirekt instanziierst, hat keinen parameterlosen Konstruktor, obwohl er erwartet wird.

Woran das genau liegt, wird dir hier wohl kaum einer sagen können, aißer es kennt sich jemand mit der DLL aus.
Ich persönlich würde daher erst einmal nach dem Source suchen oder die DLL decompilen um nachzuvollziehen, was das Problem ist.
Der StackTrace wird da vermutlich sehr hilfreich sein.

Wenn Du ganz frech sein willst, schau dir mal DNSpy [https://github.com/0xd4d/dnSpy] an.
Ein einfach ausgedrückt verdammt geniales Tool. Damit kannst Du jede .NET-DLL öffnen, Verändern, Debuggen und wieder speichern.
Damit hab ich ein sehr altes Projekt von uns, was wir nur mit sehr erheblichen Aufwand neu kompilieren können, gerettet.


Ralf Jansen - Di 12.12.17 15:49

Laut Doku benutzen die Beispiele eine bestimmte Version der COM Bibliothek.
Siehe
https://infosys.beckhoff.com/content/1031/tc3_automationinterface/72057594280852491.html?id=1450325230053563045
Ist das auch die Version die du im System registriert hast?

Zitat:
Damit kannst Du jede .NET-DLL öffnen, Verändern, Debuggen und wieder speichern.


In einer Interop Dll könnte man da eigentlich nur das GUID's zu Klassen/Methoden-mapping ändern also eher nichts zielführendes ;)


Palladin007 - Di 12.12.17 16:32

Zitat:
In einer Interop Dll könnte man da eigentlich nur das GUID's zu Klassen/Methoden-mapping ändern also eher nichts zielführendes ;)


Hab ich auch gerade fest gestellt, in der mscorlib sieht da nämlich genau so aus, wie Du sagst :/
Aber trotzdem ein geniales Tool


Ralf Jansen - Di 12.12.17 16:53

Zitat:
Aber trotzdem ein geniales Tool


Theoretisch ;) Praktisch sollte aber jede relevante Assembly eigentlich signiert sein und ändern nur zu einer ungültigen Datei führen. Ansonsten wäre das ganze Codesigninggeschäft nur Schlangenöl.


Palladin007 - Di 12.12.17 16:56

Wobei man - zumindest mit Mono.Cecil - den Key auslesen bzw. setzen kann.
Wie das genau funktioniert und ob man damit eine Assembly trotz Signatur ändern kann, weiß ich aber nicht.

Aber ich glaube wir schweifen ab ^^


C#David - Di 12.12.17 23:12

Vielen Dank für eure schnelle Hilfe. Der Verweis zu den "COM-Objekt Beckhoff TwinCAT XAE Base" (Beckhoff TCatSysManager ) ist in meinen Projekt enthalten. Ich habe das MSDN-Beispiel zum Auslesen des StackTrace 1 zu 1 in das Beispielprojekt kopiert und den Screenshot aus den Konsolenfenster für euch in den Anhang gepackt. Könnt ihr damit etwas anfangen?

Hier mein Code:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

using TCatSysManagerLib;
using System.IO;
using System.Reflection;

namespace LinkVariablesTC2
{    

    class Program
    {
        private static string _tsmPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\Templates\\Sample.tsm";
        private static TcSysManager _sysManager;      
     
        
        static void Main(string[] args)
        {            
            //_sysManager.OpenConfiguration(_tsmPath);
            //_sysManager.LinkVariabls("TIPC^Sample^Standard^Outputs^MAIN.bOutput", "TIID^Device 1 (CX1100)^Box 1 (CX1100-BK)^Term 2 (KL2404)^Channel 1^Output");
            //_sysManager.SaveConfiguration(_tsmPath);

            Program Hauptprogram = new Program();
            try
            {
                Hauptprogram.MyPublicMethod();
                _sysManager = new TcSysManager();
            }
            catch (Exception)
            {
                StackTrace st = new StackTrace(true);
                for (int i = 0; i < st.FrameCount; i++)
                {                    
                    StackFrame sf = st.GetFrame(i);
                    Console.WriteLine();
                    Console.WriteLine("High up the call stack, Method: {0}", sf.GetMethod());
                    Console.WriteLine("High up the call stack, Line Number: {0}", sf.GetFileLineNumber());                    
                }
            }          
        }

        public void MyPublicMethod()
        {
            MyProtectedMethod();
        }

        protected void MyProtectedMethod()
        {
            MyInternalClass mic = new MyInternalClass();
            mic.ThrowsException();
        }

        class MyInternalClass
        {
            public void ThrowsException()
            {
                try
                {
                    throw new Exception("A problem was encountered");
                }

                catch (Exception e)
                {
                    StackTrace st = new StackTrace(true);
                    string stackIndent = "";
                    for (int i = 0; i < st.FrameCount; i++)
                    {
                        StackFrame sf = st.GetFrame(i);
                        Console.WriteLine();
                        Console.WriteLine(stackIndent + "Method: {0}, sf.GetMethod(0)");
                        Console.WriteLine(stackIndent + "File: {0}, sf.GetFileName()");
                        Console.WriteLine(stackIndent + "Line Number: {0}, sf.GetFileLineNumber()");                        
                    }
                }
            }
        }
    }
}


Sicher könnte ich nochmal überprüfen, ob die Typbibliotheksversion vom "Beckhoff TCatSysManager" zu meiner TwinCATversion passt... Ansonsten bin ich euch sehr dankbar für weitere Hilfe ;-)

Moderiert von user profile iconChristian S.: C#-Tags hinzugefügt


C#David - Di 12.12.17 23:24

Vielleicht hilft euch das auch weiter....


Ralf Jansen - Di 12.12.17 23:50

So gibst du den StackTrace aus wie du zu der Stelle gekommen bist an dem du den Code aufrufst. Wir sehen also in deiner Stackausgabe wie das Framework die Main methode aufgerufen hat. Das ist wenig hilfreich.
Der Stacktrace steckt bereits in der Exception selbst. Wenn du deiner Exception in deinem catch Handler einfach mal einen Namen gibst kannst du an dieser Exception dann einfach mal die ToString() aufrufen und das ausgeben. Da ist dann auch der Stacktrace dabei. Im Moment wird die Exception in dem gezeigten Code wohl auch nicht von der TcSysManager sondern von anderem Code geworfen. Schmeißt das alles am besten wieder raus das hilft nicht.


C#David - Mi 13.12.17 11:44

Okay...so wie gesagt, habe ich die Exception jetzt "e" genannt und konvertiere diese anschließend in einen String. Den Inhalt des StackTrace habe ich euch als Bilder in den Anhang gepackt. Ist das hilfreich? Die Version der eigenbundenen Library passt. Besten Dank!

Moderiert von user profile iconChristian S.: Beiträge zusammengefasst

Die Excepton folgt aber immer nach den Erstellen des Objektes "_sysManager"...


Th69 - Mi 13.12.17 12:46

Welche TwinCAT-Version hast du denn bei dir installiert? Da du ja die Typbibliotheksversion 3.1 verwendest, solltest du "TwinCAT 3.1 Build 4020.0 und höher" verwenden, s.a. TwinCAT Installation [https://infosys.beckhoff.com/index.php?content=../content/1031/tc3_automationinterface/72057594280852491.html&id=1450325230053563045] (Tabelle unten).


C#David - Mi 13.12.17 13:12

TwnCAT Version 3.1.4020.32 mit TwinCAT System Service v3.1.0.2211

Moderiert von user profile iconChristian S.: Beiträge zusammengefasst

Anonsten nutze ich MS Visual Studio Professional 2012 mit Microsoft .NET Framework Version 4.6.01590


Christian S. - Mi 13.12.17 13:26

Hallo C#David,

wenn Du Informationen in einem kürzlich geposteten Beitrag ergänzen möchtest, nutze dazu bitte die Editierfunktion (Stift-Symbol unterhalb des zu ändernden Beitrags). Danke! :)

Grüße
Christian


C#David - Mi 13.12.17 13:35

Ich habe noch Code entdeckt, bei welchen der Konstruktor der Klasse erst gar nicht gebraucht wird:

Codeausschnitt sinngemäß:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
...
using EnvDTE;
using TCatSysManagerLib;
using EnvDTE100;
...

namespace
{
 ...
 DTE2 dte = null;
 Solution4 solution = null;
 EnvDTE.Project pro;
 Type t = null
 ITcSysManager4 systemManager = null;  

 function()
 {
  try
  {
   //Typ von Visual Studio Verson auslesen
   t = System.Type.GetTypeFromProgID("VisualStudio.DTE.11.0");
   //Instanz von Visual Studio erzeugen
   dte = (EnvDTE80.DTE2)System.Activator.CreateInstance(t);

   //Eigenschaften definieren
   dte.SuppressUI = false;
   dte.MainWindow.Visible = true;
   dte.UserControl = true;

   // Solution holen
   solution = (Solution4)dte.Solution;
   string SolPath = "...";
   // vorhandene Solution öffnen
   solution.Open(SolPath);
   System.Threading.Thread.Sleep(500);
   // Projekt holen
   pro = solution.Projects.Item(1);
   // Systemmanager holen
   systemManager = pro.Object;
   ...
   //Referenz auf TreeItem-Interface
   ITcSmTreeItem plcProjectRootItem = systemManager.LookupTreeItem("TIPC^Palettenspeicher");
   }
   catch(...)
   {
   ...
   }
...
}

Was wurde hier gemacht? Sollte ich eventuell auch so vorgehen?
Vielen vielen Dank!

Moderiert von user profile iconTh69: C#-Tags hinzugefügt


Ralf Jansen - Mi 13.12.17 14:35

Da geht es um Visual Studio Integration. Wenn dein SystemManager irgendwas ist das zu einem Visual Studio Addin gehört also bereits existiert und von diesem Addin instanziiert wurde wenn Visual Studio läuft wird die bereits laufende Instanz dieser Klasse gesucht die du dann benutzen kannst. Macht aber nur Sinn wenn dein Code auch ein Designtime-Ding innerhalb von Visual Studio sein soll.


C#David - Mi 13.12.17 15:05

Gut...ich möchte mit meinen Code ein bestehendes TwinCAT-Projekt berbeiten, wobei TwinCAT in Microsoft Visual Studio integriert ist. In den Fall könnte ich die Vorgehensweise aus den meinen vorherigen Beitrag denke ich übernehmen?


Ralf Jansen - Mi 13.12.17 15:43

Klingt so ja.


C#David - Do 14.12.17 11:05

Ich versuche die Referenz "_sysManager" jetzt über eine Solution zu "holen". Meine Solution ist in den Fall ein fertiges TwinCAT-Projekt (Endung: .sln/), welches ich dann auch mit meinen C#-Programm bearbeiten möchte.
Codeabschnitt:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;

using TCatSysManagerLib;
using System.IO;

using EnvDTE;
using EnvDTE80;
using EnvDTE90;
using EnvDTE100;

namespace LinkVariablesTC2
{    

    class Program
    {
        private static string _tsmPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\Templates\\Sample.tsm";
        private static TcSysManager _sysManager;    
        
        static void Main(string[] args)
        {
            DTE2 dte = null;
            Solution4 solution = null;
            EnvDTE.Project pro;
            Type t = null;
            ITcSysManager4 sysManager = null;
            //Aufrufpfad nach der Syntax Laufwerk:\\Ordner\\Ordner...\\Dateiname.sln
            string SolPath = "Aufrufpfad";

            t = System.Type.GetTypeFromProgID("VisualStudio.DTE.11.0");
            dte = (EnvDTE80.DTE2)System.Activator.CreateInstance(t);
            dte.SuppressUI = false;
            dte.MainWindow.Visible = true;
            dte.UserControl = true;

            // Silent Mode aktivieren
            var settings = dte.GetObject("TcAutomationSettings");
            settings.SilentMode = true;

            solution = (Solution4)dte.Solution;

            try
            {
                solution.Open(SolPath);
            }

            catch (Exception e)
            {
                Console.WriteLine("Beim Aufrufen der Solution wird folgende Exception geworfen: {0}", e.Message.ToString());
            }
            System.Threading.Thread.Sleep(500);
            pro = solution.Projects.Item(1);
            _sysManager = pro.Object();
            ...
            ...
         }
     }
}

Beim Öffnen der Datei/Solution-Files erhalte ich jetzt die in Anhang befindliche Fehlermeldung. Wie kann ich diesen Fehler beheben?
Den Dateipfad habe ich schon mehrfach übeprüft...Ich hatte gelesen, dass man den Fehler auch mit früher/ später Bindung irgendwie umgehen kann? Besten Dank!!!

Moderiert von user profile iconTh69: C#-Tags hinzugefügt


C#David - Do 14.12.17 13:02

Hier steht auch noc etwas zur Vorgehensweise:

http://download.beckhoff.com/download/document/automation/twincat3/AutomationInterface_pdf_DE.pdf


C#David - Do 14.12.17 15:59

Hey Leute,

es hat tatsächlich an den Klammern im Aufrufpfad gelegen :). Trotzen vielen vielen dank für eure schnelle und kostruktive Hilfe!!

Gruß
David


Niklas95 - Di 26.11.19 19:36

Hallo, ich bin neu hier und habe genau die gleiche erste Fehlermeldung bei dem gleichen Beispuiel von Beckhoff. Ich verstehe nur nicht, wie das Problem mit dem letzten Code gelöst wurde und welche Klammern im AufrufPfad gemeint sind. Ich würde ebenfalls gerne ein bestehndes TwinCAT Projekt bearbeiten.

Über eine Antwort würde ich mich freuen.


Th69 - Mi 27.11.19 11:11

Die MissingMethodException kommt wegen falscher Verwendung, richtig ist wohl der Zugriff über die VS-Integration (wie im letzten Code - und dort wurde dann wohl ein falscher Aufrufpfad angegeben).