DOpus-Scripting-Extensions project

All right, i just use UPX to reduce the size of attachment. :slightly_smiling_face:
I have removed the upx dll.

1 Like

I found a bug:
Run Regsvr32 /u DOpusScriptingExtensions.dll
Then Run it again, I get this error:

@bytim,

Thank you for the bug report.
I have looked into it. Wow, that is an interesting bug.
It seems that it is an intended behaviour:
similar issue in ShellAnything

So yeah, "DllUnregisterServer() returns error code 0x8002801c because there is nothing to unregister" is most probably true.

The interesting thing is that it took ShellAnything more code to workaround this issue than the whole DOpusScriptingExtensions project :slight_smile:
ShellAnything's workaround

You can't unregister a DLL that is already unregistered. Unfortunately, there is no specific error code for this use case, and thus I can't determine if the failure occurs because of a genuine error or if the DLL is simply already unregistered.

I prefer to keep it as it is. This issue is present in any COM DLL.

By the way, you will not have this issue if you use the .msi installer, because it only registers and unregisters the DLL once.

1 Like

Hi,

Until now, I had no issue using the COM dll for many usage, especially the ProcessRunner.

Tonight, I realized that none of my script are working anymore: the stdout part of the result is always empty.

From the tests I did with several scripts, it looks like the process are actually run, but I cant’ seem to capture the output anymore.

I tried to update to the latest version (install closed Opus btw), but that does not change anything.

Is it just me or anyone else encounters the same issue ?

Thanks.

Interesting issue.
No, it doesn't happen to me. Additionally, there is a test suite that runs from GitHub Actions, which tests that ProcessRunner works properly.

Separate your core code into a test.js file and run it from a command line using cscript test.js. Use WScript.Echo("message") to print to the output.
Will you have the same problem?

Thanks for the quick answer and for the tip.

I did not think of trying outside of Opus. It works from command line. I’ll try and investigate with even more minimal test cases in Opus.

EDIT: I can’t get it to work from within Opus. (I tried updating to 13.17 just in case, but no change).

This is working from cscript in a terminal window:

	var processRunner;
	try {
		processRunner = new ActiveXObject("DOpusScriptingExtensions.ProcessRunner")
	}
	catch (e) {
		WScript.Echo("Error ActiveX Object 'DOpusScriptingExtensions.ProcessRunner' not found on the system ...");
		WScript.Echo("Install from https://github.com/PolarGoose/DOpus-Scripting-Extensions");

		var vResult = { returncode: -1 , stdout: "", stderr: "DOpusScriptingExtensions.ProcessRunner not installed" };
		exit();
	}

	var res = processRunner.Run("C:/WINDOWS/system32/cmd.exe", ["/c", "echo TOTO" ]);
	WScript.Echo("res=" + res.ExitCode);
	WScript.Echo("out=" + res.StdOut);
	WScript.Echo("err=" + res.StdErr);

But his is not from an Opus script button:

It even yields an error on execution (ExitCode = 1) but it executes the action somehow (I tried witch launching an app that way, and the application is actually launched even though return code is 1).

EDIT: Button code

Button code
function OnClick(clickData)
{
	var processRunner;
	try {
		processRunner = new ActiveXObject("DOpusScriptingExtensions.ProcessRunner")
	}
	catch (e) {
		DOpus.Output("Error ActiveX Object 'DOpusScriptingExtensions.ProcessRunner' not found on the system ...");
		DOpus.Output("Install from https://github.com/PolarGoose/DOpus-Scripting-Extensions");

		var vResult = { returncode: -1 , stdout: "", stderr: "DOpusScriptingExtensions.ProcessRunner not installed" };
		exit();
	}

	var res = processRunner.Run("C:/WINDOWS/system32/cmd.exe", ["/c", "echo TOTO" ]);
	DOpus.Output("res=" + res.ExitCode);
	DOpus.Output("out=" + res.StdOut);
	DOpus.Output("err=" + res.StdErr);
}

Wow, this is interesting.
It indeed doesn't work from OnClick method.
I'm looking into it.

2 Likes

I have fixed the bug. I have released a new version, v19.0.
@PassThePeas, could you please check? It works on my machine.

Description of the bug:
It was a tricky one. I think I was fortunate that I found the root cause so quickly. The child process was inheriting DOpus stdin because it is a default behaviour of boost::process library. It seems that something has changed in the DOpus implementation, and now the stdin is not valid. This is why the code worked with cscript. Having invalid stdin breaks many console applications, because they don't expect reading from stdin to fail with a weird error.
The fix for this issue is very simple. I specify that the child should get a valid NUL stdin instead of inheriting it from DOpus process.

1 Like

Do you know which Opus version that changed in? I don't think we intentionally made a change that would do that, but it might be a side-effect of another change or refactoring.

1 Like

Many thanks !

I just left home for holidays, so I won't be able to test for the next 10 days or so, but will report back as soon as I can.

I just updated to 13.17.

I think I was in 13.16.5 before and since I stayed for a while on it, I guess I would have noticed if it already didn't work there.

Unfortunately 13.16 had many sub-releases so it might not be that easy to track down.

@Leo,

I think ProcessRunner implementation never worked. I have checked older DOpus versions: 13.16, 13.15, 13.14, 13.13. All of them have this problem.
My theory: @PassThePeas ran executables that don't care about stdin and thus didn't experience this problem. I personally never used ProcessRunner from DOpus, I only tested it from cscript.
Thus, I think nothing has changed in DOpus. It is a bug that has existed from the beginning. There is nothing wrong with the DOpus code. Inheriting stdin was asking for trouble.

Something changed somewhere (Opus ? ProcessRunner ? Windows ?) at some point. Scripts that used to work stopped working.

It's not that big a deal if it works again now. When I get back home I'll check my scripts log files to try and narrow it down.

I can't think of anything that could have changed at our end. Opus is a Windows app, not a console app, so I don't think it even has a stdin/stdout normally.

2 Likes

Use it in autohotkey, get an ERROR:

; AHK v2
cmdl := "C:\Tools\es.exe"
args := ["-get-result-count", "file:ancestor:c:\windows"]

^1::{
	msgbox args[1]
	;processRunner := ComObjActive("DOpusScriptingExtensions.ProcessRunner")
	processRunner := ComObject("DOpusScriptingExtensions.ProcessRunner")
	res := processRunner.Run(cmdl, args)
	msgbox res.stdout
}

I think this is because the index of the Array type in AHK script starts from 1, not 0

++++
It sames AHK Array is different object type from jscript Array, and cannot be converted or mixed.
Could you consider another way to pass multiple parameters instead of an Array?

I couldn't anticipate this use case :slight_smile:
I'm surprised that it almost works. I would expect the code to fail earlier.

Let me see what I can do. I think it should be possible to detect if the array is a JS array or an AHK array.

In the meantime, you can use WScript.Shell, which does the same thing as DOpusScriptingExtensions.ProcessRunner. The only difference is that it shows a console window while the executable is running.

yes,So WScript.Shell is not a perfect method..
that's why I like to use "DOpusScriptingExtensions.ProcessRunner" :grinning_face_with_smiling_eyes:

@bytim FWIW, since AHK can call DLLs directly, there's a way to run a program and grab its output without showing the console. I did that back when AHK was more mainstream, though I have no idea where I put that code. But a quick Google search or a post on their forum will sort it for you. I don't see what this has to do with Opus tbh.

Unfortunately, there is no simple way to accommodate an AutoHotkey array :frowning:
@bytim,
I think a more conventional way would be to use RunWait with a temporary file, like this:

app := '"C:/Program Files/Git/usr/bin/echo.exe" arg1 arg2 arg3'
outFile := A_Temp "\stdout.txt"
errFile := A_Temp "\stderr.txt"
try FileDelete outFile
try FileDelete errFile

cmdLine := Format(A_ComSpec ' /C "{} 1>"{}" 2>"{}""', app, outFile, errFile)
exitCode := RunWait(cmdLine, , "Hide")

stdout := FileRead(outFile)
stderr := FileRead(errFile)

MsgBox "Exit: " exitCode "`n---STDOUT---`n" stdout "`n---STDERR---`n" stderr

It works well.

I tried to accommodate AHK arrays in the DOpusScriptingExtensions implementation. I thought that I could use DISPID_NEWENUM to get an enumerator and thus have a generic code that iterates through any collection. However, in all Microsoft's wisdom, JS arrays don't support it, while AHK arrays support it, but the iterator doesn't iterate :slight_smile:.

If such functionality is essential, it is better to create a separate project DLL, which can be used via DllCall without requiring COM.

Here is it :grinning_face_with_smiling_eyes:
Capture CMD output with AutoHotkey

but the author said:

Outside the temp file method, this thing actually works. Be advised, if other parts of your AHK script write to StdOut and require an open console (using DllCall("AllocConsole") for instance), those parts might not work well with this.

Maybe you can simply pass a string instead of an array to a COM Object? :grinning_face:

for example:

; AHK v2
cmdl := "C:\Tools\es.exe"
args := "-get-result-count\nfile:ancestor:c:\windows" ;// Multiple arguments separated by \n

^1::{
	msgbox args[1]
	;processRunner := ComObjActive("DOpusScriptingExtensions.ProcessRunner")
	processRunner := ComObject("DOpusScriptingExtensions.ProcessRunner")
	res := processRunner.Run(cmdl, args)
	msgbox res.stdout
}

Then, in processRunner.Run, convert the String args to an Array args

if typeof(args) == "string" {
    args = args.split("\n")
    var arr = new Array(); 
    for(i=0; i<args.length; i++){ // //I don't know if it's feasible..
        arr.push(args[i])
    }
} 
//Next, use your original processing method. Since typeof(args) == "array" now
...
...