Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Software-Projekte der Mitglieder (https://www.delphipraxis.net/26-software-projekte-der-mitglieder/)
-   -   Windows Scripting Host (WSH) (https://www.delphipraxis.net/214004-windows-scripting-host-wsh.html)

himitsu 5. Nov 2023 10:48


Windows Scripting Host (WSH)
 
Liste der Anhänge anzeigen (Anzahl: 3)
Windows Scripting Host (WSH)
https://de.wikipedia.org/wiki/Windows_Script_Host

https://learn.microsoft.com/en-us/pr...ectedfrom=MSDN
https://community.embarcadero.com/bl...lication-36013


Da dieses in abgeschlossene Windows-API ist (Entwicklung abgeschlossen und Interfaces als volltändig/ausreichend definiert)
läuft es natürlich nur unter Windows. Schön ist aber, dass sich jeder im Windows registrieren und weitere Engines hinzufügen kann.

Standardmäßig sind JavaScript und VBScript vorhanden.
Vielleicht kennt ihr es vom FinalBuilder, welcher Pyphon und PowerShell zusätzlich drin hat. (sie nutzen aber fertige C#-Komponenten dafür, wie z.B. Bei Google suchenIronPython)

Ich hatte vor 'ner Weile mal geschaut, wie man das nutzt (ein paar wenige "ältere" Delphi-Komponenten inzwischen auch gefunden)
und soooooo schlimm sieht es garnicht aus .... in Minimal kommt man "nativ" mit paar Zeilen Code aus (siehe unten).
Und das dann angefangen in eine TComponent zu packen.


https://de.wikipedia.org/wiki/Windows_Script_Host
https://en.wikipedia.org/wiki/Windows_Script_Host
im Englischen Wiki eine schöne Auflistung, was es z.B. für Sprachen gibt.
* Perl, LUA, PHP, Ruby, Tcl, Huskell, Cobol :lol:, Fortan, C++ und C# (als Script) und ja, auch Delphi/Pascal, sowie vieles Mehr.

Wie und wo man die Installationen dafür findent, hab ich "noch" nicht geschaut.

Bezüglich Python hatte ich aber schonmal gesucht und :wall:
Es gibt eigentlich das Projekt Bei Google suchenActivePython und die Version 2.7 (mit Python 2.7) gibt es als Community Edition zu finden.
* https://www.heise.de/download/produc...-edition-37561
* die 64 Bit-Version ist auch nur die 32 Bit, also funktioniert es nur für 32 Bit-Delphi-Programme
* die Version 3.x gibt es nirgendwo mehr
* und der Entwickler hat die Community Edition eingestellt https://www.activestate.com/blog/goo...tate-platform/
* die aktuelle kostenlose Version des ActiveState hatte ich getestet und es nicht hinbekommen, dass sie sich im Windows registriert (weder 32 noch 64 Bit)
Achtung, beim Login via GitHub, wollen die lese/schreibzugriffe auf alle eure "privaten" Repos haben ... hab's also via eMail gemacht.

ActivePython ist dieses ominöse "Python.AXScript.2", was man in den Demos findet. (funktioniert auch, wenn es installiert ist)
https://wiki.python.org/moin/ActivePython




Quellcode ... joar, kommt irgendwann.
Aktuell hab ich mir mal 'ne Test-Anwendung wild zusammengeklöppelt, welche alle bisher implementierten Funktionen und ein paar Demo enthält. (PS: siehe Hints überall)

* Achtung, die Ausgabeumleitung fehlt noch. (hoffe auf eine andere Lösung, falls man es in einer ConsolenAnwendung nutzen wöllte)
* Expressions sind nutzbar, da als Funktion-Result aus der Funktion raus kommen
* beim ExecuteScript gibt es eigentlich auch ein Result, aber da steht nichts drin
* da es eine GUIAnwendung ist, hat sie "eigentlich" keine Console, wo die Scriptausgabe vom StdOut landen
* rechts in der TestEcke sind zwei Knöpfe (einmal zum Generieren eines Consolenfensters und das Andere mit einer Umleitung der StdIn/StdOut/StdErr ... was ich aber "noch" nicht in meine Komponente integriert hab)


* paar Varianten eigene Objekte/Variablen/Funktionen aus Delphi reinzugeben existieren schon (in 3 Varianten, siehe ScriptingMain.pas -> TPropTest und Co.)
* wobei die Letzte nicht direkt auf IDispatch aufsetzt, sondern IDispatchEx nutzt und dann via RTTI auf Delphi-Code zufreift, also public Objekt-Felder/Variablen, Property und Methoden

* Ja, SynEdit aus'm GetIt könnte man noch benutzen
* externer Zugriff auf aktuelle Variablen/Objekte/Funktionen, des Scripts, fehlt auch noch
* die Debugging-API hab ich "noch" nicht implementiert
* CodeInsight/CodeCompletion wäre noch interessant, also mit LiveDaten aus der ScriptingEngine
* die PythonScript.dll ist noch ohne Funktion (PythonScript)
* und die ConsoleScript.dll (CMD/CommandScript, PowerShell und WSL, falls installiert)

* CMD/PS/SWL hängt noch etwas, da ich dort noch etwas mit der neuen PseudoConsole (ConPTY) kämpfe.
https://www.delphipraxis.net/214002-...doconsole.html

* innerhalb der Engines die Consolen-Ausgabe umzuleiten, wie es z.B. Python4Delphi macht .... naja
* einmal müsste ich das dann jeweils in der aktuellen Scriptsprache machen, falls es dort überhaupt möglich ist, deren Ausgabefunktionen zu überschreiben
* also wäre schon eine allgemeine Umsetzung angebracht




Ach ja, meine ActiveX-Komponenten ConsoleScript.dll und PythonScript.dll
* sorry, mit dem ActiveX-Experten von Delphi bin ich noch nicht klargekommen (und die Tutorials/Videos :kotz:), also hab ich mir's erstmal selbsgemacht
* aktuell rufe ich sie direkt auf (SafeLoadLibrary+DllGetClassObject)
* SideBySide-Loading will ich noch ausprobieren
* registrieren sollten sie sich im Windows lassen, aber ob der Aufruf funktioniert oder noch was fehlt ... hab ich nicht getestet




So, und hier noch ein winziger nativer Ansatz:
https://stackoverflow.com/questions/...-from-within-c
Delphi-Quellcode:
[deleted]
Müsste jemand das Script aber nochmal testen ... kann sein, dass SetScriptSite nötig ist und man sich noch ein kleines IActiveScriptSite basteln muß.

himitsu 5. Nov 2023 17:16

AW: Windows Scripting Host (WSH)
 
Ach ja, alles natürlich auch für Win64.
Viele Beispiele im Internet funktionieren hier nicht, da zwar die Interfaces "gleich" sind, aber Viele eine andere GUID besitzen.

Auch muß beachtet werden, dass installierte Fremd-Sprachen in der jeweiligen Bittigkeit (oder Beides) installiert sein müssen. (zumindestens bei den In-Process-Servern)



So, und hier noch ein winziger nativer Ansatz:
https://stackoverflow.com/questions/...-from-within-c
Delphi-Quellcode:
uses
  System.SysUtils, System.Win.ComObj, System.Types, System.Variants,
  Winapi.ActiveX, h5u.ActiveScripting.Interfaces;

var
  ClassID: TGUID;
  Engine: IActiveScript;
  ASite:  IActiveScriptSite;
  Parser: IActiveScriptParse;
  ErrInfo: EXCEPINFO;
  Flags:  DWORD;
  OResult: OleVariant;
  Name:   WideString;
  Disp:   IDispatch;
  HR:     HRESULT;
begin
  // https://stackoverflow.com/questions/7491868/how-to-load-call-a-vbscript-function-from-within-c

  //CoInitializeEx(nil, COINIT_MULTITHREADED);

  OleCheck(CLSIDFromProgID(PWideChar('VBScript'), ClassID));
  Engine := CreateComObject(ClassID) as IActiveScript;

  ASite := TDummyNonInteractiveScriptSite.Create; // ODER: ASite := TDummyActiveScriptSite.Create(Self.Handle)
  OleCheck(Engine.SetScriptSite(ASite));
  OleCheck(Engine.QueryInterface(IActiveScriptParse, Parser));
  OleCheck(Parser.InitNew);

  //OleCheck(Parser.AddScriptlet(nil, PWideChar('...'), nil, nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, Name, ErrInfo));
  {or}
  //OleCheck(Parser.ParseScriptText(PWideChar('...'), nil, nil, nil, 0, 0, ..., nil, ErrInfo));
  {or}
  //if Failed(Parser.ParseScriptText(PWideChar('...'), nil, nil, nil, 0, 0, ..., nil, ErrInfo)) then
  //  raise Exception.CreateFmt('Error $%x: %s', [ErrInfo.scode, ErrInfo.bstrDescription]);
  {or}
  //HR := Parser.ParseScriptText(PWideChar('...'), nil, nil, nil, 0, 0, ..., nil, ErrInfo);
  //if Failed(HR) then
  //  raise Exception.CreateFmt('Error $%x %s'#10'$%x: %s', [HR, SysErrorMessage(Cardinal(HR)), ErrInfo.scode, ErrInfo.bstrDescription]);

  //HR := Parser.ParseScriptText(PWideChar('WScript.Echo "abc"'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, @OResult, ErrInfo);
  HR := Parser.ParseScriptText(PWideChar('set fso = CreateObject("Scripting.FileSystemObject")'#10'set stream = fso.GetStandardStream(1)'#10
    + 'stream.WriteLine("This will go to standard output.")'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, @OResult, ErrInfo);
  if Failed(HR) then
    raise Exception.CreateFmt('Error $%x %s'#10'$%x: %s', [HR, SysErrorMessage(Cardinal(HR)), ErrInfo.scode, ErrInfo.bstrDescription]);

  HR := Parser.ParseScriptText(PWideChar('a = 123'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, @OResult, ErrInfo);
  if Failed(HR) then
    raise Exception.CreateFmt('Error $%x %s'#10'$%x: %s', [HR, SysErrorMessage(Cardinal(HR)), ErrInfo.scode, ErrInfo.bstrDescription]);
  //
  HR := Parser.ParseScriptText(PWideChar('a * 2 + 3'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISEXPRESSION, @OResult, ErrInfo);
  if Failed(HR) then
    raise Exception.CreateFmt('Error $%x %s'#10'$%x: %s', [HR, SysErrorMessage(Cardinal(HR)), ErrInfo.scode, ErrInfo.bstrDescription]);
  Writeln('Result = ' + VarToStrDef(OResult, '(NULL)'));
  //
  HR := Parser.ParseScriptText(PWideChar('MsgBox "Hello World! The current time is " & Now'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, @OResult, ErrInfo);
  if Failed(HR) then
    raise Exception.CreateFmt('Error $%x %s'#10'$%x: %s', [HR, SysErrorMessage(Cardinal(HR)), ErrInfo.scode, ErrInfo.bstrDescription]);

  // Engine.GetScriptDispatch(nil, Disp);

  //CoUninitialize;
end;
Müsste jemand das Script aber nochmal testen ... kann sein, dass SetScriptSite nötig ist und man sich noch ein kleines IActiveScriptSite basteln muß.
Ja, ein IActiveScriptSite muß bereitgestellt werden, aber die Funktionen können quasi leer sein (einfach nur überall Result jeweils mit S_OK, S_FALSE usw. beglücken)
Hab dafür zwei Dummy-Interfaces in die Interface-Unit gepackt und rechts noch Knöpfe "Native-API" in die Demo-/Testanwendung.

Sollen die Scripte auch Interaktiv sein dürfen, dann muß auch IActiveScriptSiteWindow implementiert sein (und OK sagen), sonst raucht es z.B. bei einem
Delphi-Quellcode:
MsgBox "peng"
mit einer Exception "Erlaubnis verweigert".

TiGü 6. Nov 2023 09:08

AW: Windows Scripting Host (WSH)
 
Bei mir kommt er bis zur Zeile 'a = 123' als Win64 Konsolenprogramm, dann steigt das Programm mit EOleSysError: Ungültiger Zeiger aus.

Delphi-Quellcode:
program ScriptingTest;

{$APPTYPE CONSOLE}

{$R *.res}


uses
    System.SysUtils,
    System.Win.ComObj,
    System.Types,
    System.Variants,
    Winapi.ActiveX,
    h5u.ActiveScripting.Interfaces;

procedure Main;
var
    ClassID: TGUID;
    Engine: IActiveScript;
    ASite: IActiveScriptSite;
    Parser: IActiveScriptParse;
    ErrInfo: EXCEPINFO;
    Flags: DWORD;
    OResult: OleVariant;
    Name: WideString;
    Disp: IDispatch;
    HR: HResult;
begin
    // https://stackoverflow.com/questions/7491868/how-to-load-call-a-vbscript-function-from-within-c

    OleCheck(CLSIDFromProgID(PWideChar('VBScript'), ClassID));
    Engine := CreateComObject(ClassID) as IActiveScript;

    ASite := TDummyNonInteractiveScriptSite.Create; // ODER: ASite := TDummyActiveScriptSite.Create(Self.Handle)
    OleCheck(Engine.SetScriptSite(ASite));
    OleCheck(Engine.QueryInterface(IActiveScriptParse, Parser));
    OleCheck(Parser.InitNew);

    OleCheck(Parser.ParseScriptText(PWideChar('WScript.Echo "abc"'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, nil, ErrInfo));
    //
    HR := Parser.AddScriptlet(nil, PWideChar('a = 123'), nil, nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, Name, ErrInfo);
    OleCheck(HR); // <---- EOleSysError: Ungültiger Zeiger
    OleCheck(Parser.ParseScriptText(PWideChar('a * 2 + 3'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISEXPRESSION, @OResult, ErrInfo));
    Writeln(VarToStrDef(OResult, '(NULL)'));
    //
    OleCheck(Parser.ParseScriptText(PWideChar('MsgBox "Hello World! The current time is " & Now'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE,
      nil, ErrInfo));

    // Engine.GetScriptDispatch(nil, Disp);
end;

begin
    try
        CoInitializeEx(nil, 0);
        try
            Main;
        finally
            CoUninitialize;
        end;
    except
        on E: Exception do
            Writeln(E.ClassName, ': ', E.Message);
    end;
    Readln;
end.

himitsu 6. Nov 2023 20:07

AW: Windows Scripting Host (WSH)
 
32 Bit knallt auch.

Hmmmm, CoInitialize ist da ... daran liegt es nicht. (macht die VCL ja von selbst)

hier nur nochmal eine erweiterte Fehlerbehandlung.
Delphi-Quellcode:
  HR := Parser.AddScriptlet(nil, PChar('a = 123'), nil, nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, Name, ErrInfo);
  if Failed(HR) then
    raise Exception.CreateFmt('Error $%x %s'#10'$%x: %s', [HR, SysErrorMessage(Cardinal(HR)), ErrInfo.scode, ErrInfo.bstrDescription]);
// Cardinal, weil sonst die Bereichsprüfung knallt Integer<>Cardinal
// und die umgekehrte Variante von HResultFromWin32 auf die Schnelle nicht gefunden
Was mir beim Schreiben grade auffiel, dass ich AddScriptlet garnicht einzeln geteste hatte. (ist dort auskommentiert)

Und was noch komisch ist, dass das erste WScript.Echo ohne Fehler durch läuft.
Das müsste knallen, da WScript hier unbekannt sein sollte. und außerdem macht das Echo nicht -> nichts wird ins Konsolenfenster geschrieben.

Ansonsten ist es quasi fast der selbe Code, wie ich ihn gestern getestet hatte.
Delphi-Quellcode:
procedure TScriptingTestForm.btNativeExecuteClick(Sender: TObject);
var
  ClassID: TGUID;
  Engine: IActiveScript;
  ASite:  IActiveScriptSite;
  Parser: IActiveScriptParse;
  ErrInfo: EXCEPINFO;
  Flags:  DWORD;
  OResult: OleVariant;
  //Name: WideString;
  //Disp: IDispatch;
begin
  if Trim(edScriptlet.Text) = '' then begin
    cbScriptingEngine.Text := 'VBScript';
    edScriptlet.Text := 'MsgBox "Hello World! The current time is " & Now';
  end;

  // https://stackoverflow.com/questions/7491868/how-to-load-call-a-vbscript-function-from-within-c

  OleCheck(CLSIDFromProgID(PChar(ActiveScripting.ScriptingEngine), ClassID)); // e.q. 'VBScript'
  Engine := CreateComObject(ClassID) as IActiveScript;

  if cbNativeInteractiv.Checked then
    ASite := TDummyActiveScriptSite.Create(Self.Handle)
  else
    ASite := TDummyNonInteractiveScriptSite.Create;
  OleCheck(Engine.SetScriptSite(ASite));
  OleCheck(Engine.QueryInterface(IActiveScriptParse, Parser));
  OleCheck(Parser.InitNew);

  //OleCheck(Parser.ParseScriptText(PChar('WScript.Echo "abc"'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, nil, ErrInfo));
  //
  //OleCheck(Parser.AddScriptlet(nil, PChar('a = 123'), nil, nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, Name, ErrInfo));
  //OleCheck(Parser.ParseScriptText(PChar('a * 2 + 3'), nil, nil, nil, 0, 0, SCRIPTTEXT_ISEXPRESSION, @OResult, ErrInfo));
  //ShowMessage(VarToStrDef(OResult, '(NULL)'));
  OleCheck(Parser.ParseScriptText(PChar(edScriptletInit.Text), nil, nil, nil, 0, 0, SCRIPTTEXT_ISVISIBLE, nil, ErrInfo));
  Flags := IfThen(Sender = btNativeExpression, SCRIPTTEXT_ISEXPRESSION, 0) or SCRIPTTEXT_ISVISIBLE;
  if Failed(Parser.ParseScriptText(PChar(edScriptlet.Text), nil, nil, nil, 0, 0, Flags, @OResult, ErrInfo)) then
    raise Exception.CreateFmt('Error $%x: %s', [ErrInfo.scode, ErrInfo.bstrDescription]);
  edResult.Text := VarToStrDef(OResult, '(NULL)');

  //Engine.GetScriptDispatch(nil, Disp);
end;
Komm grade aus der Sauna und entspanne mich erstmal (viele Grüße von der EKON) ... schau später, ob ich den Fehler finde.

himitsu 7. Nov 2023 07:06

AW: Windows Scripting Host (WSH)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Es knallt nicht nur im VBScript, sondern auch beim JScript.

Die Signatur sieht aber OK aus.
https://learn.microsoft.com/en-us/do...4.addscriptlet
https://embedded.vbsedit.com/active/006/001.asp
https://www.jb51.net/shouce/script56...eScriptParse__
https://www.jb51.net/shouce/script56...ml/engines.htm

Obwohl ich denke, dass ItemName und EventName leer sein können, auch gefüllt versucht, aber nee.

Mir fiel auf, dass ich beim ersten Testcode das AddScriptlet drin hatte, es aber dennoch nie benutzt wurde, auch nicht in den wenigen, inzwischen angesammelten, TestCodes war es nicht drin.
Und ich fand im Internet auch praktisch nichts, wo es genutzt wird. :shock:



hmmmmmmm ..... booaaaarrrrr .... jaaaaa .....
Aktuell keine Ahnung, aber mit ParseScriptText ist ja erstmal alles so weit nutzbar. :stupid:
Beispiel in #2 aktualisiert, sowie siehe Anhang.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:58 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz