Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Access Violation bei TCL-Kommandoaufruf über DLL
dls
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

Access Violation bei TCL-Kommandoaufruf über DLL

beantworten | zitieren | melden

Hallo allerseits,

um die Einbindung eines externen TCL-Interpreters ("TCL84.DLL") auszuprobieren habe ich eine simple Konsolenanwendung erstellt. Das Ganze funktioniert soweit. Auch die Anmeldung eines eigenen Kommandos und die Zuweisung des Kommandonamens "test" mittels "Tcl_CreateCommand" funktioniert im Prinzip. Wenn ich "test" in der laufenden Konsole aufrufe gibt es jedoch eine Access Violation ("Es wurde versucht, im geschützten Speicher zu lesen...").


using System;
//using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        unsafe static int Main(string[] args)
        {
            string inp = "12";
            Console.WriteLine("input your TCL commands:");
            TclInterpreter interp = new TclInterpreter();
            interp.registerProc("test", new TclAPI.Tcl_ObjCmdProc(tcmd));
            string result="Interpreter result is != 0 !!!";

            // TCL test output:
            interp.evalScript("set a " + inp + ";");                            // -> a = 12
            interp.evalScript("expr {$a * 8}");                                 // "interp.Result" = a * 8 = 96
            if (interp.evalScript("expr {" + interp.Result + " + 2}") == 0)     // "interp.Result" = "interp.Result" + 2 = 98
            {                                                                   // if no error:
                result = interp.Result;                                         // then "result" is "interp.Result" = 98
            }
            Console.WriteLine(result);                                          // write line "result" = 98

            // enter & execute TCL user commands via console:
            while (1 == 1)
            {
                string s = Console.ReadLine();
                interp.evalScript(s);
                Console.WriteLine(interp.Result);
            }

            //  TCL Test per Konsole zum probieren:
            //  TCL Ausgabe Variable a: 		puts $a
            //  TCL Ausgabe Variable a: 		expr {$a}
            //  TCL Definition Variable b: 		set b 5
            //  TCL Berechnung + Ausgabe:		expr {$a * $b}

        }

        static unsafe int tcmd(IntPtr clientData, IntPtr interpr, int argc, byte** argv)
        {
            TclAPI.Tcl_AppendResult(interpr, "Hello");
            return 0;
        }

    }

    public class TclAPI
    {
        public unsafe delegate int Tcl_ObjCmdProc(IntPtr clientData,
            IntPtr interp,
            Int32 objc,
            byte** argv);
        [DllImport("tcl84.DLL", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr Tcl_CreateInterp();
        [DllImport("tcl84.DLL", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Tcl_Eval(IntPtr interp, string skript);
        [DllImport("tcl84.DLL", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr Tcl_GetObjResult(IntPtr interp);
        [DllImport("tcl84.DLL", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr Tcl_CreateCommand(IntPtr interp, String cmdName, Tcl_ObjCmdProc proc, IntPtr clientData, IntPtr deleteProc);
        [DllImport("tcl84.DLL", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr Tcl_AppendResult(IntPtr interp, String rslt);
        [DllImport("tcl84.DLL", CallingConvention = CallingConvention.Cdecl)]
        unsafe public static extern char* Tcl_GetStringFromObj(IntPtr tclObj, IntPtr length);
    }
    public class TclInterpreter
    {
        private IntPtr interp;
        private ArrayList delegates = new ArrayList();
        public TclInterpreter()
        {
            interp = TclAPI.Tcl_CreateInterp();
            if (interp == IntPtr.Zero)
            {
                throw new SystemException("can not initialize Tcl interpreter");
            }
        }
        public int evalScript(string script)
        {
            return TclAPI.Tcl_Eval(interp, script);
        }
        public void registerProc(string name, TclAPI.Tcl_ObjCmdProc proc)
        {
            // prevent cleaning delegates 
            delegates.Add(proc);
            TclAPI.Tcl_CreateCommand(interp, name, proc, IntPtr.Zero, IntPtr.Zero);
        }

        unsafe public string Result
        {
            get
            {
                IntPtr obj = TclAPI.Tcl_GetObjResult(interp);
                if (obj == IntPtr.Zero)
                {
                    return "";
                }
                else
                {
                    return Marshal.PtrToStringAnsi((IntPtr)TclAPI.Tcl_GetStringFromObj(obj, IntPtr.Zero));
                }
            }
        }
    }

}

Möglicherweise wird der Pointer auf meine Funktion nicht korrekt an die DLL übergeben. Die Fehlermeldung kommt jedenfalls mit der Programmzeile " return TclAPI.Tcl_Eval(interp, script);" (hier wird die aktuelle Zeile aus der Konsole an den Interpreter übergeben).
Hat jemand eine Idee, was ich hier machen kann?
private Nachricht | Beiträge des Benutzers
weismat
myCSharp.de - Member



Dabei seit:
Beiträge: 872
Herkunft: Frankfurt am Main

beantworten | zitieren | melden

Dein Delegate/Funktionpointer hat nur Verweise im unmanaged Bereich.
Du musst einen zusätzlichen Verweis herstellen oder ein statisches Feld nehmen, damit der Garabge Collector das delegate nicht löscht.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von weismat am .
private Nachricht | Beiträge des Benutzers
dls
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

beantworten | zitieren | melden

Hallo weismat,
Danke für die schnelle Antwort. Ich nehme an, Du meinst das Delegate "Tcl_ObjCmdProc".
Das Programm habe ich folgendermaßen geändert, um das Feld statisch zu machen:


    class Program
    {
        unsafe static TclAPI.Tcl_ObjCmdProc cbp = new TclAPI.Tcl_ObjCmdProc(tcmd);
        unsafe static int Main(string[] args)
        {
            string inp = "12";
            Console.WriteLine("input your TCL commands:");
            TclInterpreter interp = new TclInterpreter();
            //interp.registerProc("test", new TclAPI.Tcl_ObjCmdProc(tcmd));
            interp.registerProc("test", cbp);

Das Resultat ist jedoch leider das gleiche.
private Nachricht | Beiträge des Benutzers
Spook
myCSharp.de - Member



Dabei seit:
Beiträge: 241
Herkunft: Esslingen a.N.

beantworten | zitieren | melden

Hallo dls,

versuche mal die korrekte Calling-Convention für den Tcl_ObjCmdProc Delegaten zu setzen:
UnmanagedFunctionPointerAttribute

Grüße
spooky
private Nachricht | Beiträge des Benutzers
dls
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

beantworten | zitieren | melden

Hallo spooky,
die Defintion sieht jetzt so aus:


        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public unsafe delegate int Tcl_ObjCmdProc(IntPtr clientData,
            IntPtr interp,
            Int32 objc,
            byte** argv);

Damit funktioniert es! Allerdings nur, wenn ich die Applikation aus der Entwicklungsumgebung starte. Nach Start durch Doppelklick auf die .exe gibt es wieder einen Laufzeitfehler, sobald ich das angemeldete Kommando mittels "test" aufrufe. "Release" oder "Debug" ist dabei egal.

Grüße
dls
private Nachricht | Beiträge des Benutzers
Spook
myCSharp.de - Member



Dabei seit:
Beiträge: 241
Herkunft: Esslingen a.N.

beantworten | zitieren | melden

Hallo,

kannst du bitte die originale Definition der Methodensignatur (C/C++) posten?

spooky
private Nachricht | Beiträge des Benutzers
dls
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

beantworten | zitieren | melden

Tut mir leid, aber wo finde ich die Methodensignatur?
Ich dachte bisher, es handelt sich um die Namen und die Parameterdefinitionen der Methoden. Die sind doch alle im geposteten Programm enthalten.
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4.173

beantworten | zitieren | melden

Wir brauchen die C/C++ Header-Datei ("tcl.h"?) der "tcl84.dll"...
Wo hast du denn sonst die Funktionssignaturen her?
private Nachricht | Beiträge des Benutzers
dls
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

beantworten | zitieren | melden

Sorry, die Funktionssignaturen der DLL sind hier dokumentiert:
http://www.tcl.tk/man/tcl8.4/TclLib/contents.htm

Weiterhin habe ich mich an Programmbeispiele gehalten wie dieses hier:
http://wiki.tcl.tk/9563

Grüße
dls
private Nachricht | Beiträge des Benutzers