0

I'm trying to execute a VBScript from a C++ ATL console application (non a VBS file, but simply a script embedded in my code) and get back a string from it.

I tried something like this:

HRESULT hr = S_OK;
CComVariant result;
EXCEPINFO ei = { };

LPCOLESTR sCmd = L"s = \"something\"\r\n"
"WScript.Echo s\r\n";

hr = spVBScriptParse->ParseScriptText(sCmd, NULL, NULL, NULL, 0, 0, 0, &result, &ei);

std::cout << "got:" << result.cVal << std::endl;

See also this question for how to set this up:

How to load & call a VBScript function from within C++?

If I run this, VBScript complains about the non existant "WScript" object.

I tried replacing WScript.Echo with MsgBox and the code works fine (but of course doesn't give back the string).

I tried to use this:

LPCOLESTR sCmd = L"s = \"something\"\r\n"
                  "Set fso = CreateObject (\"Scripting.FileSystemObject\")\r\n"
                  "Set stdout = fso.GetStandardStream (1)\r\n"
                  "stdout.WriteLine s\r\n"

This writes my string to the console where the C++ app was launched, but don't return back the string to my code.

How do I get the value of s out of the Script into my Host process?

Just to clarify: I don't want to save the VBS, not the string to a temporary file.

The goal is to keep everything in memory without disk access.

I know I can leverage registry, but I'd like to avoid elevation. Don't know if some memory sharing is avalable to vbscript.

Other idea was named pipes, but I've no idea on how to share one between c++ and vbscript.

Jens Mühlenhoff
  • 14,565
  • 6
  • 56
  • 113
Massimo
  • 90
  • 1
  • 8
  • 1
    The WScript object is not part of the VBScript language. It’s injected by Microsoft’s “Windows Scripting Host” when WSH hosts the scripts. Here, your program is the new host instead of WSH, therefore there is no WScript object. If you want to provide a host object similar to WScript, you are going to have to write one and provide it yourself from your host program (sorry, I don’t have knowledge of *how* that is done) – Euro Micelli Mar 08 '19 at 14:10
  • That is actually not a bad idea as well. You can expose your own objects from C++ into the scripting environment. This is done with `IActiveScript::AddNamedItem()`, etc. See Remys answer here: https://stackoverflow.com/a/7492025/426242 – Jens Mühlenhoff Mar 08 '19 at 19:27

1 Answers1

0

Just add a second call to ParseScriptText that evaluates your string variable as an expression.

You have to pass SCRIPTTEXT_ISEXPRESSION as the dwFlags argument so that the scripting engine knows that you want to get the value of an expression.

That is also in the official documentation for ParseScriptText:

https://learn.microsoft.com/en-us/scripting/winscript/reference/iactivescriptparse-parsescripttext

I tested it in Delphi and it works like a charm:

procedure TestActiveScripting;
const
  SCRIPTTEXT_ISEXPRESSION = $00000020;
var
  hr: HResult;
  ScriptSite: IActiveScriptSite;
  VBScript: IActiveScript;
  VBScriptParse: IActiveScriptParse;
  res: Variant;
  ei: TExcepInfo;
begin
  // Initialize
  ScriptSite := TSimpleScriptSite.Create as IActiveScriptSite;

  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 := VBScriptParse.ParseScriptText('s = "something"', nil, nil, nil, 0, 0, 0, @res, ei);
  hr := VBScriptParse.ParseScriptText('s', nil, nil, nil, 0, 0, SCRIPTTEXT_ISEXPRESSION, @res, ei);
  ShowMessage(res);
end;
Jens Mühlenhoff
  • 14,565
  • 6
  • 56
  • 113
  • Sorry Jens, didn't understand what you mean. If you mean that I already have the value of "s", I say that the VBS code here is just for testing purpose. The real VBS code is huge script that produce a string as a result of different operations. The final goal of this is to write a C++ wrapper for a huge VBS scripts codebase. – Massimo Mar 08 '19 at 08:58
  • putting just "s" as the last line of the script, raises a vbscript syntax error. So I tryed to put something like ss = s. The scripts complete successfuly, but I'm not sure if no value is returned or I'm not able to get it back. I tried to use result.bstrVal, but apparently points to empty BSTR. Can you elaborate a little more what you mean with "it returns the value as its last expression" ? – Massimo Mar 08 '19 at 14:56
  • Jens: yes, the key is a second call with SCRIPTTEXT_ISEXPRESSION flag. Works like a charme in C++ also. Thanks. Massimo. – Massimo Mar 15 '19 at 13:04
  • I have deleted my comments and updated the answer to clean things up a little. – Jens Mühlenhoff Mar 15 '19 at 13:19