我希望能够从我的Delphi应用程序中执行用户提供的脚本。
是否可以在我的应用程序中托管Windows脚本宿主引擎,并提供要执行的脚本?还是有更好的方法来解决这个问题?
附言:我不需要第三方组件。
这是完全可能的,而且有很多代码可以展示如何做到这一点。请查看:http://www.torry.net/pages.php?id=280
几年前,Allen Bauer发表了一篇博客文章,介绍了如何使用Active Scripting。他深入探讨了其背后的理论,并提供了在Code Central上支持VBScript和JavaScript的代码,无需第三方组件。
这是我从C++问题转换而来的完整代码示例:
您需要使用定义如下的IActiveScript等接口:
unit AscrLib;
// PASTLWTR : 1.1
// File generated on 7/27/00 12:13:00 PM from Type Library described below.
// ************************************************************************ //
// Type Lib: E:\tp\internet\temp.tlb (1)
// IID\LCID: {B1376415-E2EF-11D1-A693-00AA00125A98}\0
// Helpfile:
// DepndLst:
// (1) v2.0 stdole, (C:\WINNT\System32\STDOLE2.TLB)
// (2) v4.0 StdVCL, (z:\Iliad\Bin\STDVCL40.DLL)
// ************************************************************************ //
{$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers.
interface
uses Windows, ActiveX, Classes, StdVCL;
// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used:
// Type Libraries : LIBID_xxxx
// CoClasses : CLASS_xxxx
// DISPInterfaces : DIID_xxxx
// Non-DISP interfaces: IID_xxxx
// *********************************************************************//
const
// TypeLibrary Major and minor versions
ActiveScriptLibMajorVersion = 1;
ActiveScriptLibMinorVersion = 0;
LIBID_ActiveScriptLib: TGUID = '{B1376415-E2EF-11D1-A693-00AA00125A98}';
IID_IActiveScript: TGUID = '{BB1A2AE1-A4F9-11CF-8F20-00805F2CD064}';
IID_IActiveScriptSite: TGUID = '{DB01A1E3-A42B-11CF-8F20-00805F2CD064}';
IID_IActiveScriptError: TGUID = '{EAE1BA61-A4ED-11CF-8F20-00805F2CD064}';
IID_IActiveScriptParse: TGUID = '{BB1A2AE2-A4F9-11CF-8F20-00805F2CD064}';
IID_IActiveScriptSiteWindow: TGUID = '{D10F6761-83E9-11CF-8F20-00805F2CD064}';
// *********************************************************************//
// Declaration of Enumerations defined in Type Library
// *********************************************************************//
// Constants for enum tagSCRIPTSTATE
type
tagSCRIPTSTATE = TOleEnum;
const
SCRIPTSTATE_UNINITIALIZED = $00000000;
SCRIPTSTATE_INITIALIZED = $00000005;
SCRIPTSTATE_STARTED = $00000001;
SCRIPTSTATE_CONNECTED = $00000002;
SCRIPTSTATE_DISCONNECTED = $00000003;
SCRIPTSTATE_CLOSED = $00000004;
// Constants for enum tagSCRIPTTHREADSTATE
type
tagSCRIPTTHREADSTATE = TOleEnum;
const
SCRIPTTHREADSTATE_NOTINSCRIPT = $00000000;
SCRIPTTHREADSTATE_RUNNING = $00000001;
// Constants for enum tagSCRIPTINFO
type
tagSCRIPTINFO = TOleEnum;
const
SCRIPTINFO_IUNKNOWN = $00000001;
SCRIPTINFO_ITYPEINFO = $00000002;
// Constants for enum tagSCRIPTITEM
type
tagSCRIPTITEM = TOleEnum;
const
SCRIPTITEM_ISVISIBLE = $00000002;
SCRIPTITEM_ISSOURCE = $00000004;
SCRIPTITEM_GLOBALMEMBERS = $00000008;
SCRIPTITEM_ISPERSISTENT = $00000040;
SCRIPTITEM_CODEONLY = $00000200;
SCRIPTITEM_NOCODE = $00000400;
type
// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
IActiveScript = interface;
IActiveScriptSite = interface;
IActiveScriptError = interface;
IActiveScriptParse = interface;
IActiveScriptSiteWindow = interface;
// *********************************************************************//
// Declaration of structures, unions and aliases.
// *********************************************************************//
// wireHWND = ^Integer;
wireHWND = hWnd;
// *********************************************************************//
// Interface: IActiveScript
// Flags: (0)
// GUID: {BB1A2AE1-A4F9-11CF-8F20-00805F2CD064}
// *********************************************************************//
IActiveScript = interface(IUnknown)
['{BB1A2AE1-A4F9-11CF-8F20-00805F2CD064}']
function SetScriptSite {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_29:1}const pass: IActiveScriptSite): HResult; stdcall;
function GetScriptSite {Flags(1), (2/2) CC:4, INV:1, DBG:6}({VT_29:1}var riid: TGUID;
{VT_24:2}out ppvObject: Pointer): HResult; stdcall;
function SetScriptState {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_29:0}ss: tagSCRIPTSTATE): HResult; stdcall;
function GetScriptState {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_29:1}out pssState: tagSCRIPTSTATE): HResult; stdcall;
function Close {Flags(1), (0/0) CC:4, INV:1, DBG:6}: HResult; stdcall;
function AddNamedItem {Flags(1), (2/2) CC:4, INV:1, DBG:6}({VT_31:0}pstrName: PWideChar;
{VT_19:0}dwFlags: LongWord): HResult; stdcall;
function AddTypeLib {Flags(1), (4/4) CC:4, INV:1, DBG:6}({VT_29:1}var rguidTypeLib: TGUID;
{VT_19:0}dwMajor: LongWord;
{VT_19:0}dwMinor: LongWord;
{VT_19:0}dwFlags: LongWord): HResult; stdcall;
function GetScriptDispatch {Flags(1), (2/2) CC:4, INV:1, DBG:6}({VT_31:0}pstrItemName: PWideChar;
{VT_9:1}out ppdisp: IDispatch): HResult; stdcall;
function GetCurrentScriptThreadID {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_19:1}out pstidThread: LongWord): HResult; stdcall;
function GetScriptThreadID {Flags(1), (2/2) CC:4, INV:1, DBG:6}({VT_19:0}dwWin32ThreadId: LongWord;
{VT_19:1}out pstidThread: LongWord): HResult; stdcall;
function GetScriptThreadState {Flags(1), (2/2) CC:4, INV:1, DBG:6}({VT_19:0}stidThread: LongWord;
{VT_29:1}out pstsState: tagSCRIPTTHREADSTATE): HResult; stdcall;
function InterruptScriptThread {Flags(1), (3/3) CC:4, INV:1, DBG:6}({VT_19:0}stidThread: LongWord;
{VT_29:1}var pexcepinfo: EXCEPINFO;
{VT_19:0}dwFlags: LongWord): HResult; stdcall;
function Clone {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_29:2}out ppscript: IActiveScript): HResult; stdcall;
end;
// *********************************************************************//
// Interface: IActiveScriptSite
// Flags: (0)
// GUID: {DB01A1E3-A42B-11CF-8F20-00805F2CD064}
// *********************************************************************//
IActiveScriptSite = interface(IUnknown)
['{DB01A1E3-A42B-11CF-8F20-00805F2CD064}']
function GetLCID {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_19:1}out plcid: LongWord): HResult; stdcall;
function GetItemInfo {Flags(1), (4/4) CC:4, INV:1, DBG:6}({VT_31:0}pstrName: PWideChar;
{VT_19:0}dwReturnMask: LongWord;
{VT_13:1}out ppiunkItem: IUnknown;
{VT_29:2}out ppti: IUnknown): HResult; stdcall;
function GetDocVersionString {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_8:1}out pbstrVersion: WideString): HResult; stdcall;
function OnScriptTerminate {Flags(1), (2/2) CC:4, INV:1, DBG:6}({VT_12:1}var pvarResult: OleVariant;
{VT_3:1}var pexcepinfo: EXCEPINFO): HResult; stdcall;
function OnStateChange {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_29:0}ssScriptState: tagSCRIPTSTATE): HResult; stdcall;
function OnScriptError {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_29:1}const pscripterror: IActiveScriptError): HResult; stdcall;
function OnEnterScript {Flags(1), (0/0) CC:4, INV:1, DBG:6}: HResult; stdcall;
function OnLeaveScript {Flags(1), (0/0) CC:4, INV:1, DBG:6}: HResult; stdcall;
end;
// *********************************************************************//
// Interface: IActiveScriptError
// Flags: (0)
// GUID: {EAE1BA61-A4ED-11CF-8F20-00805F2CD064}
// *********************************************************************//
IActiveScriptError = interface(IUnknown)
['{EAE1BA61-A4ED-11CF-8F20-00805F2CD064}']
function GetExceptionInfo {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_24:1}out pexcepinfo: EXCEPINFO): HResult; stdcall;
function GetSourcePosition {Flags(1), (3/3) CC:4, INV:1, DBG:6}({VT_19:1}out pdwSourceContext: LongWord;
{VT_19:1}out pulLineNumber: LongWord;
{VT_3:1}out plCharacterPosition: Integer): HResult; stdcall;
function GetSourceLineText {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_8:1}out pbstrSourceLine: WideString): HResult; stdcall;
end;
// *********************************************************************//
// Interface: IActiveScriptParse
// Flags: (0)
// GUID: {BB1A2AE2-A4F9-11CF-8F20-00805F2CD064}
// *********************************************************************//
IActiveScriptParse = interface(IUnknown)
['{BB1A2AE2-A4F9-11CF-8F20-00805F2CD064}']
function InitNew {Flags(1), (0/0) CC:4, INV:1, DBG:6}: HResult; stdcall;
function AddScriptlet {Flags(1), (11/11) CC:4, INV:1, DBG:6}({VT_31:0}pstrDefaultName: PWideChar;
{VT_31:0}pstrCode: PWideChar;
{VT_31:0}pstrItemName: PWideChar;
{VT_31:0}pstrSubItemName: PWideChar;
{VT_31:0}pstrEventName: PWideChar;
{VT_31:0}pstrDelimiter: PWideChar;
{VT_19:0}dwSourceContextCookie: LongWord;
{VT_19:0}ulStartingLineNumber: LongWord;
{VT_19:0}dwFlags: LongWord;
{VT_8:1}out pBstrName: WideString;
{VT_29:1}out pexcepinfo: TGUID): HResult; stdcall;
function ParseScriptText {Flags(1), (9/9) CC:4, INV:1, DBG:6}({VT_31:0}pstrCode: PWideChar;
{VT_31:0}pstrItemName: PWideChar;
{VT_13:0}const punkContext: IUnknown;
{VT_31:0}pstrDelimiter: PWideChar;
{VT_19:0}dwSourceContextCookie: LongWord;
{VT_19:0}ulStartingLineNumber: LongWord;
{VT_19:0}dwFlags: LongWord;
{VT_12:1}pvarResult: Pointer;
{VT_29:1}out pexcepinfo: EXCEPINFO): HResult; stdcall;
end;
// *********************************************************************//
// Interface: IActiveScriptSiteWindow
// Flags: (0)
// GUID: {D10F6761-83E9-11CF-8F20-00805F2CD064}
// *********************************************************************//
IActiveScriptSiteWindow = interface(IUnknown)
['{D10F6761-83E9-11CF-8F20-00805F2CD064}']
function GetWindow {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_3:2}out phwnd: wireHWND): HResult; stdcall;
function EnableModeless {Flags(1), (1/1) CC:4, INV:1, DBG:6}({VT_3:0}fEnable: Integer): HResult; stdcall;
end;
implementation
uses ComObj;
end.
这是转换后的示例代码:
unit Unit2;
interface
uses
Winapi.ActiveX,
Winapi.Windows,
System.ObjAuto,
AscrLib;
const
CLSID_VBScript: TGUID = '{b54f3741-5b07-11cf-a4b0-00aa004a55e8}';
CLSID_JScript: TGUID = '{f414c260-6ac0-11cf-b6d1-00aa00bbbb58}';
SCRIPT_E_REPORTED = HRESULT($80020101);
type
TSimpleScriptSite = class(TInterfacedObject, IActiveScriptSite,
IActiveScriptSiteWindow)
private
FHwnd: wireHWND;
public
// IActiveScriptSite
function GetLCID(out plcid: LongWord): HResult; stdcall;
function GetItemInfo(pstrName: PWideChar; dwReturnMask: LongWord;
out ppiunkItem: IUnknown; out ppti: IUnknown): HResult; stdcall;
function GetDocVersionString(out pbstrVersion: WideString)
: HResult; stdcall;
function OnScriptTerminate(var pvarResult: OleVariant;
var pexcepinfo: EXCEPINFO): HResult; stdcall;
function OnStateChange(ssScriptState: tagSCRIPTSTATE): HResult; stdcall;
function OnScriptError(const pscripterror: IActiveScriptError)
: HResult; stdcall;
function OnEnterScript: HResult; stdcall;
function OnLeaveScript: HResult; stdcall;
// IActiveScriptSiteWindow
function GetWindow(out phwnd: wireHWND): HResult; stdcall;
function EnableModeless(fEnable: Integer): HResult; stdcall;
end;
procedure TestActiveScriptingOuter;
procedure TestActiveScripting;
implementation
{ TSimpleScriptSite }
function TSimpleScriptSite.EnableModeless(fEnable: Integer): HResult;
begin
Result := S_OK;
end;
function TSimpleScriptSite.GetDocVersionString(out pbstrVersion
: WideString): HResult;
begin
pbstrVersion := '1.0';
Result := S_OK;
end;
function TSimpleScriptSite.GetItemInfo(pstrName: PWideChar;
dwReturnMask: LongWord; out ppiunkItem, ppti: IInterface): HResult;
begin
Result := TYPE_E_ELEMENTNOTFOUND;
end;
function TSimpleScriptSite.GetLCID(out plcid: LongWord): HResult;
begin
plcid := 0;
Result := S_OK;
end;
function TSimpleScriptSite.GetWindow(out phwnd: wireHWND): HResult;
begin
phwnd := FHwnd;
Result := S_OK;
end;
function TSimpleScriptSite.OnEnterScript: HResult;
begin
Result := S_OK;
end;
function TSimpleScriptSite.OnLeaveScript: HResult;
begin
Result := S_OK;
end;
function TSimpleScriptSite.OnScriptError(const pscripterror
: IActiveScriptError): HResult;
begin
Result := S_OK;
end;
function TSimpleScriptSite.OnScriptTerminate(var pvarResult: OleVariant;
var pexcepinfo: EXCEPINFO): HResult;
begin
Result := S_OK;
end;
function TSimpleScriptSite.OnStateChange(ssScriptState: tagSCRIPTSTATE)
: HResult;
begin
Result := S_OK;
end;
procedure TestActiveScriptingOuter;
var
hr: HResult;
begin
hr := CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
TestActiveScripting;
CoUninitialize();
end;
procedure TestActiveScripting;
const
SCRIPTTEXT_ISEXPRESSION = $00000020;
var
hr: HResult;
ScriptSite: IActiveScriptSite;
JScript: IActiveScript;
JScriptParse: IActiveScriptParse;
VBScript: IActiveScript;
VBScriptParse: IActiveScriptParse;
res: Variant;
ei: TExcepInfo;
begin
// Initialize
ScriptSite := TSimpleScriptSite.Create as IActiveScriptSite;
hr := CoCreateInstance(CLSID_JScript, nil, CLSCTX_INPROC_SERVER, IID_IActiveScript, JScript);
hr := JScript.SetScriptSite(ScriptSite);
JScriptParse := JScript as IActiveScriptParse;
hr := JScriptParse.InitNew;
hr := CoCreateInstance(CLSID_VBScript, nil, CLSCTX_INPROC_SERVER, IID_IActiveScript, VBScript);
hr := VBScript.SetScriptSite(ScriptSite);
VBScriptParse := VBScript as IActiveScriptParse;
hr := VBScriptParse.InitNew;
// Run some scripts
hr := JScriptParse.ParseScriptText('(new Date()).getTime()', nil, nil, nil, 0, 0, SCRIPTTEXT_ISEXPRESSION, @res, ei);
hr := VBScriptParse.ParseScriptText('Now', nil, nil, nil, 0, 0, SCRIPTTEXT_ISEXPRESSION, @res, ei);
hr := VBScriptParse.ParseScriptText('MsgBox "Hello World! The current time is: " & Now', nil, nil, nil, 0, 0, 0, @res, ei);;
end;
end.
TestActiveScriptingOuter
方法。您应该会收到来自VBScript的MsgBox,并且在调试器中,您可以看到表达式如何被评估并传递给Res
变量。IActiveScript.AddNamedItem
和IActiveScriptSite.GetItemInfo
实现。使用Windows脚本宿主不需要任何第三方组件。我们已经使用它十年,并围绕它构建了一个庞大的ERP系统,其中VBScript源代码行数超过100万。
您应该使用Windows脚本控件来告诉Windows脚本宿主,或者直接通过已知接口连接。
wscript
对象是一个应用程序对象,在WSH之外运行的脚本中不可用。但是,所有实用程序子对象都可以通过CreateObject
获得。 - Noodles