Batch file called from Javascript/XPCOM doesn't show command prompt window - javascript

I am calling a batch file from Javascript in this fashion:
function runBatch(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("C:\\test.bat");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["hi"];
run.run(false, parameters,parameters.length);
}
my test batch file is:
echo on
echo %1
pause
exit
Each time I call a batch file, however, the command prompt is not displayed, as it would be if I simply ran the batch file from the desktop. How can I remedy this and display a command prompt for the batch file?
Edit
To be clear, the cmd.exe process is launched - I can see it in the task bar. But no window gets displayed. This snippet behaves similarly:
function runCmd(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("C:\\WINDOWS\\system32\\cmd.exe");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
run.run(false, null,0);
}

The only solution I've heard so far (that should work, although I haven't done it yet, comes from Mook in the Mozilla xulrunner IRC channel:
create a temporary batch file, writing in the batch file to call and arguments to pass it. then execute the temporary batch file.
e.g psuedocode:
f = fopen("temp.bat");
fprintf(f, "other.bat 1 2 3 4 5");
fclose(f);
exec("temp.bat");
not very elegant but it should work.

Did you try using the launch method of nsiLocalFile?
function runBatch(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("C:\\test.bat");
exe.launch();
}
This should have "the same effect as if you double-clicked the file."

This code snippet seems to work fine. Of course, you have to change D:\Windows\system32\ to path to cmd.exe in your operation system.
const FileFactory = new Components.Constructor("#mozilla.org/file/local;1","nsILocalFile","initWithPath");
var str_LocalProgram = "D:\\Windows\\system32\\cmd.exe";
var obj_Program = new FileFactory(str_LocalProgram);
var process = Components.classes["#mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
process.init(obj_Program);
var args = ["/C", "regedit.exe"];
process.run(true, args, args.length);

I had to launch a batch file and pass in an argument. This is how I did it:
let file = uri.QueryInterface(Components.interfaces.nsIFileURL).file;
let run = Components.classes['#mozilla.org/process/util;1']
.createInstance(Components.interfaces.nsIProcess);
let path = file.path;
if(file.exists())
{
// quick security check
if(file.isExecutable())
{
// show error message
return;
}
let localfile = file.QueryInterface(Components.interfaces.nsILocalFile);
if(localfile != null)
{
if (app == "app1")
{
localfile.initWithPath("C:\\app1.bat");
}
else
{
localfile.initWithPath("C:\\app2.bat");
}
run.init(localfile);
var parameters = [path];
run.run(false, parameters, parameters.length);
}
else
{
// show error message
}
}
else
{
// show error message
}
and in my Window batch file I did:
#ECHO OFF
START "application.exe" %1
using START, allowed me to launch the application and close the command line window

You are doing right but repair this:
function runBatch(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("***C:\ \test.bat***");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["hi"];
run.run(false, parameters,parameters.length);
}
If you do this???
function runBatch(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("***C:\test.bat***");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["hi"];
run.run(false, parameters,parameters.length);
}
An put #echo off at init???
Thanks

Pfft, very ugly code..
A much nicer trick is to use Win.com to spawn a 16bit subsystem of the command prompt.
Win.com will send the console to the right virtual terminal, showing you the output.
var lPath = getWorkingDir.path + "\\..\\..\\WINDOWS\\system32\\win.com";
lFile.initWithPath(lPath);
var process = Components.classes["#mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
process.init(lFile);
var args = ["cmd.exe"];
process.run(false, args, args.length);
Nicer, and works :)

For Linux:
<script>
function callLight2()
{
netscape.security.PrivilegeManager.enablePrivilege(
'UniversalXPConnect'
);
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
// exe.initWithPath(C:\\Windows\\system32\\cmd.exe"");
exe.initWithPath("/usr/bin/gnome-terminal");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["-e", "/usr/bin/ip_connect_up.sh 2 2 3 4 5 6"];
// var parameters = ["/C", "regedit.exe"];
// var parameters = ["hi"];
run.run(true, parameters,parameters.length);
}
</script>
start

Related

After Effects Script works manually, but not on start up

I have a script (.jsx) that runs correctly when I load the project and run it myself. However, when I run it on start up by putting it in the StartUp folder it gives me this error:
"Unable to execute script at line 1. After Effects error: Unable to call "item" because of parameter 1. The range has no values"
Here is my script:
var compToBeOutput = app.project.item(35);
var comp1 = app.project.item(1);
var numGames = 7;
var p = 3;
compToBeOutput.duration = (90*numGames)/30;
compToBeOutput.layers.add(comp1);
for (i = 0; i<numGames-1; i++) {
var newComp = comp1.duplicate();
var newLayer = compToBeOutput.layers.add(newComp);
newLayer.startTime = p;
p = p + 3;
}
Thanks,
Greg
That is because item here is undefined. app.project.item is only accessible when project is open. so you need to open project first in the script itself.
For example.
var myPath = new File("you file path here");
if(myPath.exists){
app.open(myPath );
}
then paste your script underneath.

Function does not call after callback

hello all StackOverflowers :) have a good day.
I have a mouse.exe, that is a simple C++ App that receives via stdio "getmouse", "setmouse x y" and "movemouse x y", getting the mouse position and setting it respectively...
That app is working fine and can receive commands while running.
I created a 'mouse' module in a folder, with this index.js
const spawn = require('child_process').spawn;
const EXE_ROUTE = __dirname + '\\mouse.exe';
var LOADED = false;
var Mouse = function (ready) {
if(LOADED) return ready && ready();
LOADED = true;
var child = spawn(EXE_ROUTE);
var stdout = Mouse.in = child.stdout;
var stdin = Mouse.out = child.stdin;
var err = Mouse.err = child.stderr;
Mouse.out.write("movemouse 100 100"); // This Works
Mouse.out.write("setmouse 20 20"); // This too
ready && ready();
}
module.exports = Mouse;
And my entry point is app.js
var Mouse = require('./mouse');
Mouse(function () {
console.log("Im here..."); // This prints
Mouse.out.write("movemouse 100 100"); // This does not work.
});
Mouse.out.write("movemouse 100 100"); // This Works :)
Im a bit confused because the callback is called just after the Mouse() function, so it shouldnt be any difference between writing the line below the Mouse(function () { ... }); and writing it in the callback...
PD: Dont tell me "You dont need to use a callback here, bc i will do some async tasks then and i will need the cb"
Thanks for your collaboration.

Permissions error when running another JS AppleScript from another JSAppleScript

I am trying to separate out my .applescript files into different ones to tidy things up.
I have a JS AppleScript file called Test.applescript that tries to run the JS AppleScript file Group Tracks Dependency.applescript and what I want to do is pass in a parameter into the dependency script and get a return value out of it. (It creates an array of arrays of iTunes tracks).
Test.applescript
(function() {
var app = Application('iTunes');
app.includeStandardAdditions = true;
app.doShellScript('Group Tracks Dependency.applescript');
return "Done";
})();
// For quick logging
function log(obj) {
this.console.log(obj);
}
Group Tracks Dependency.applescript
(function(selection) {
return getGroupsOfTracks(selection);
function getGroupsOfTracks(originalTracksArray) {
if (originalTracksArray == null || originalTracksArray.length == 0)
return null;
var tracks = originalTracksArray.slice();
var groups = [];
while (true) {
var group = [];
group.push(tracks[0]);
tracks = tracks.slice(1);
while (true) {
if (!tracks[0]) break;
if (tracks[0].album() != group[0].album())
break;
if (tracks[0].artist() != group[0].artist())
break;
if (tracks[0].discNumber() != group[0].discNumber())
break;
group.push(tracks[0]);
tracks = tracks.slice(1);
}
groups.push(group);
if (!tracks[0]) break;
}
return groups;
}
})();
When I try to run the Test script I get this error (line 5 is the app.doShellScript line):
Error on line 5: Error: A privilege violation occurred.
Is there any way to get around this? I should also note that I want other people to be able to download these scripts and run them on their own iTunes libraries in the future (currently it's not user-friendly though).
If there's no way to get around this then would importing another JS AppleScript file work?
I think you may be fighting a battle that you can’t win using .doShellScript.
The Apple way is to use a Script Library as defined on https://developer.apple.com/library/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/Articles/OSX10-11.html#//apple_ref/doc/uid/TP40014508-CH110-SW1
Unfortunately a script library has constraints where you can only pass simple variables.
Another way is to use require, which can be defined with code like https://github.com/dtinth/JXA-Cookbook/wiki/Importing-Scripts
I'm not sure what you are trying to accomplish, but this works for me using Script Editor 2.8.1 (183.1) on OSX 10.11.4:
Create a main JXA Script file
Create a JXA Script Library file
BOTH of these MUST be saved as compiled script files (.scpt)
It is INCORRECT that "Unfortunately a script library has constraints where you can only pass simple variables."
You can call any of the functions in the Script Library file from any JXA script.
In your MAIN script file, which I will call "Get iTunes Group Selection.scpt":
var app = Application('iTunes');
app.includeStandardAdditions = true;
var myLib = Library("My JXA Lib")
var selectionArr = app.selection() // ### Change as needed ###
var groupArr = myLib.getGroupsOfTracks(selectionArr)
groupArr
~~~~~~~~~~~~~~~~~~~~~
And then in a separate script file, saved as:
~/Library/Script Libraries/My JXA Lib.scpt
function getGroupsOfTracks(originalTracksArray) {
if (originalTracksArray == null || originalTracksArray.length == 0)
return null;
var tracks = originalTracksArray.slice();
var groups = [];
while (true) {
var group = [];
group.push(tracks[0]);
tracks = tracks.slice(1);
while (true) {
if (!tracks[0]) break;
if (tracks[0].album() != group[0].album())
break;
if (tracks[0].artist() != group[0].artist())
break;
if (tracks[0].discNumber() != group[0].discNumber())
break;
group.push(tracks[0]);
tracks = tracks.slice(1);
}
groups.push(group);
if (!tracks[0]) break;
}
return groups;
}
Well, it's been a few years...
I ran into errors with JXA and doShellScript when I tried to run with Application("Finder"). These errors went away when I instead ran the script from Application.currentApplication(). So for my script, I used const finder = Application("Finder") for Finder specific stuff, then const app = Application.currentApplication() for running the script.
For example:
//test1.scpt
function run() {
const app = Application.currentApplication()
app.includeStandardAdditions = true
app.doShellScript("osascript ~/Desktop/test2.scpt")
}
//test2.scpt
function run() {
const app = Application.currentApplication()
app.includeStandardAdditions = true
app.displayDialog("foo")
app.doShellScript("osascript -e 'display dialog \"bar\"'")
}
As expected, running test1.scpt gives me two dialogs: foo and `bar.

Get subfolders from folder with multiple levels

I have code which checks all the files in subfolders for a folder. But how can I change it to not only check on subfolder level but also the subfolders of the subfolder and so on?
This is the code i have for the folder and its subfolders:
var fso = new ActiveXObject("Scripting.FileSystemObject");
fso = fso.getFolder(path);
var subfolders = new Object();
subfolders = fso.SubFolders;
var oEnumerator = new Enumerator(subfolders);
for (;!oEnumerator.atEnd(); oEnumerator.moveNext())
{
var itemsFolder = oEnumerator.item().Files;
var oEnumerator2 = new Enumerator(itemsFolder);
var clientFileName = null;
for(;!oEnumerator2.atEnd(); oEnumerator2.moveNext())
{
var item = oEnumerator2.item();
var itemName = item.Name;
var checkFile = itemName.substring(itemName.length - 3);
if(checkFile == ".ac")
{
var clientFileName = itemName;
break;
}
}
}
On each level of subfolders I need to check all the files if it can find a .ac file.
The solution I mentioned in my comment would look something like this (I don't know much about ActiveX, so there are a lot of comments so hopefully you can easily correct any mistakes):
//this is the function that looks for the file inside a folder.
//if it's not there, it looks in every sub-folder by calling itself
function getClientFileName(folder) {
//get all the files in this folder
var files = folder.Files;
//create an enumerator to check all the files
var enumerator = new Enumerator(files);
for(;!enumerator.atEnd(); enumerator.moveNext()) {
//get the file name we're about to check
var file = enumerator.item().Name;
//if the file name is too short skip this one
if (file.length<3) continue;
//check if this file's name matches, if it does, return it
if (file.substring(file.length - 3)==".ac") return file;
}
//if we finished this loop, then the file was not inside the folder
//so we check all the sub folders
var subFolders = folder.SubFolders;
//create an enumerator to check all sub folders
enumerator = new Enumerator(subFolders);
for(;!enumerator.atEnd(); enumerator.moveNext()) {
//get the sub folder we're about to check
var subFolder = enumerator.item();
//try to find the file in this sub folder
var fileName = getClientFileName(subFolder);
//if it was inside the sub folder, return it right away
if (fileName!=null) return fileName;
}
//if we get this far, we did not find the file in this folder
return null;
}
You would then call this function like so:
var theFileYouAreLookingFor = getClientFileName(theFolderYouWantToStartLookingIn);
Again, beware of the above code: I did not test it, nor do I know much about ActiveX, I merely took your code and changed it so it should look in all the sub folders.
What you want is a recursive function. Here's a simple recursive function that iterates thru each file in the provided path, and then makes a recursive call to iterate thru each of the subfolders files. For each file encountered, this function invokes a provided callback (which is where you'd do any of your processing logic).
Function:
function iterateFiles(path, recursive, actionPerFileCallback){
var fso = new ActiveXObject("Scripting.FileSystemObject");
//Get current folder
folderObj = fso.GetFolder(path);
//Iterate thru files in thisFolder
for(var fileEnum = new Enumerator(folderObj.Files); !fileEnum.atEnd(); fileEnum.moveNext()){
//Get current file
var fileObj = fso.GetFile(fileEnum.item());
//Invoke provided perFile callback and pass the current file object
actionPerFileCallback(fileObj);
}
//Recurse thru subfolders
if(recursive){
//Step into each sub folder
for(var subFolderEnum = new Enumerator(folderObj.SubFolders); !subFolderEnum.atEnd(); subFolderEnum.moveNext()){
var subFolderObj = fso.GetFolder(subFolderEnum.item());
//Make recursive call
iterateFiles(subFolderObj.Path, true, actionPerFileCallback);
}
}
};
Usage (here I'm passing in an anonymous function that get's called for each file):
iterateFiles(pathToFolder, true, function(fileObj){
Wscript.Echo("File Name: " + fileObj.Name);
};
Now.. That is a pretty basic example. Below is a more complex implementation of a similar function. In this function, we can recursively iterate thru each file as before. However, now the caller may provide a 'calling context' to the function which is in turn passed back to the callback. This can be powerful as now the caller has the ability to use previous information from it's own closure. Additionally, I provide the caller an opportunity to update the calling context at each recursive level. For my specific needs when designing this function, it was necessary to provide the option of checking to see if each callback function was successful or not. So, you'll see checks for that in this function. I also include the option to perform a callback for each folder that is encountered.
More Complex Function:
function iterateFiles(path, recursive, actionPerFileCallback, actionPerFolderCallback, useFnReturnValue, callingContext, updateContextFn){
var fso = new ActiveXObject("Scripting.FileSystemObject");
//If 'useFnReturnValue' is true, then iterateFiles() should return false IFF a callback fails.
//This function simply tests that case.
var failOnCallbackResult = function(cbResult){
return !cbResult && useFnReturnValue;
}
//Context that is passed to each callback
var context = {};
//Handle inputs
if(callingContext != null){
context.callingContext = callingContext;
}
//Get current folder
context.folderObj = fso.GetFolder(path);
//Do actionPerFolder callback if provided
if(actionPerFolderCallback != null){
var cbResult = Boolean(actionPerFolderCallback(context));
if (failOnCallbackResult(cbResult)){
return false;
}
}
//Iterate thru files in thisFolder
for(var fileEnum = new Enumerator(context.folderObj.Files); !fileEnum.atEnd(); fileEnum.moveNext()){
//Get current file
context.fileObj = fso.GetFile(fileEnum.item());
//Invoke provided perFile callback function with current context
var cbResult = Boolean(actionPerFileCallback(context));
if (failOnCallbackResult(cbResult)){
return false;
}
}
//Recurse thru subfolders
if(recursive){
//Step into sub folder
for(var subFolderEnum = new Enumerator(context.folderObj.SubFolders); !subFolderEnum.atEnd(); subFolderEnum.moveNext()){
var subFolderObj = fso.GetFolder(subFolderEnum.item());
//New calling context that will be passed into recursive call
var newCallingContext;
//Provide caller a chance to update the calling context with the new subfolder before making the recursive call
if(updateContextFn != null){
newCallingContext = updateContextFn(subFolderObj, callingContext);
}
//Make recursive call
var cbResult = iterateFiles(subFolderObj.Path, true, actionPerFileCallback, actionPerFolderCallback, useFnReturnValue, newCallingContext, updateContextFn);
if (failOnCallbackResult(cbResult)){
return false;
}
}
}
return true; //if we made it here, then all callbacks were successful
};
Usage:
//Note: The 'lib' object used below is just a utility library I'm using.
function copyFolder(fromPath, toPath, overwrite, recursive){
var actionPerFileCallback = function(context){
var destinationFolder = context.callingContext.toPath;
var destinationPath = lib.buildPath([context.callingContext.toPath, context.fileObj.Name]);
lib.createFolderIfDoesNotExist(destinationFolder);
return copyFile(context.fileObj.Path, destinationPath, context.callingContext.overwrite);
};
var actionPerFolderCallback = function(context){
var destinationFolder = context.callingContext.toPath;
return lib.createFolderIfDoesNotExist(destinationFolder);
};
var callingContext = {
fromPath : fromPath,
toPath : lib.buildPath([toPath, fso.GetFolder(fromPath).Name]), //include folder in copy
overwrite : overwrite,
recursive : recursive
};
var updateContextFn = function(currentFolderObj, previousCallingContext){
return {
fromPath : currentFolderObj.Path,
toPath : lib.buildPath([previousCallingContext.toPath, currentFolderObj.Name]),
overwrite : previousCallingContext.overwrite,
recursive : previousCallingContext.recursive
}
}
return iterateFiles(fromPath, recursive, actionPerFileCallback, null, true, callingContext, updateContextFn);
};
I know this question is old but I stumbled upon it and hopefully my answer will help someone!

this._get is not a function - javascript oop and prototypes

I'm using something similar to NodeJS called bondi, it's build on the Firefox js engine.. Basically i'm getting this error and I believe it's due to the way i'm referencing "this" in the .Get function below.
Basically there is a tool called SFtpClient. It has the method of "Get", to list the contents of a folder, but I want to change the prototype for this with a drop in include file. I need to change it so that it
a/ retries several times when it fails, and b/ it has a recursive folder listing function.
So I used the prototype to change it - moved .Get to ._Get.
Can anyone see why I would be getting the error:
Jan 23 04:51:34 beta bondi: === this._Get is not a function --- Prio(6) Result(0x0) File(/home/nwo/approot/include/sftpclientenh
when I run the code below?
Thanks
SFtpClient.prototype._Get = SFtpClient.prototype.Get;
SFtpClient.prototype.Get = function(Folder, Retries){
//defaults
if(!Retries) Retries = 5;
if(!Folder) Folder = "~/";
//vars
var FileListing = [];
var connect = function(){
//TODO JRF 19.01.2012 : re-enable this when bondi is fixed
// this.HomeDirectory.replace(/\/?$/, "/");
FileListing = this._Get(Folder);
return true;
}
var i = 1;
do{
var res = false;
try {
res = connect();
}catch(e){
Debug.LogInfo(e.message);
}
i++;
Server.Sleep(i*2000);
} while(res==false && i < Retries);
return FileListing;
}
Try res = connect.call(this) instead of res = connect().

Categories

Resources