How to call QProcess with schtask - winapi

I'd like to know what's the correct command for my the code .
My code isn't working and can't create a Task.
QString TaskName= "Task Name";
QString Create = "/create /tn";
QString Path = " /tr c:\Control.exe";
QString sch = "/sc daily /st 1:57:00 /ed 08/02/2011";
QString Program ="schtasks";
int i = QProcess::execute(Program,QStringList() <<Create<<TaskName<<Path<<sch);

I've solved it , You need to put the time as 00:00:00 .
Thanks

Related

How to zip a folder in my Win32 Visual C++ app using a system call

I am trying to zip a folder in my Win32 Visual C++ app using a system call. On my computer, I can do this by the cmd command:
PowerShell -Command "Compress-Archive -Path C:\Users\ttyler\Desktop\Software\Folder2\RUN -DestinationPath C:\Users\ttyler\Desktop\Software\Folder2\RUN"
Where "Try" is a folder.
I am trying to do this on my app using a System call, but in my app, I have the "Path" and the "DestinationPath" stored in variables (because the destination path is being pulled in by the user). I'm not sure I'm using the variables correctly in my system call because this is giving me the error: "'+': cannot add two pointers" and "expression must have integral or unscoped enum type".
TCHAR path = L"C:\Users\ttyler\Desktop\Software\Folder2\RUN";
TCHAR DestinationPath = L"C:\Users\ttyler\Desktop\Software\Folder2\RUN.zip";
system("Compress-Archive -Path" + path[0] + "-DestinationPath" + DestinationPath[0]);
What am I doing wrong when using the variables in my system call?
Your code won't work for many reasons:
you are trying to assign wide string literals to single-char variables. You need to use arrays or pointers instead, eg:
TCHAR path[] = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN";
TCHAR DestinationPath[] = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN.zip";
LPCTSTR path = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN";
LPCTSTR DestinationPath = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN.zip";
you cannot concatenate string literals with array/single-chars the way you are trying to do. You will have to allocate new arrays and copy/format the various substrings into it, eg:
WCHAR path[] = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN";
WCHAR DestinationPath[] = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN.zip";
WCHAR cmd[(MAX_PATH*2)+50] = {};
swprintf(cmd, L"Compress-Archive -Path \"%s\" -DestinationPath \"%s\"", path, DestinationPath);
// use cmd as needed...
Or, just use std::wstring instead, eg:
std::wstring path = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN";
std::wstring DestinationPath = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN.zip";
std::wstring cmd = L"Compress-Archive -Path \"" + path + L"\" -DestinationPath \"" + DestinationPath + L"\"";
// use cmd as needed...
system() takes a const char* pointer as input. But you are using TCHAR strings, which will only work correctly for your wide string literals if TCHAR is WCHAR. Which means you can't pass your concatenated string to system() anyway. You will have to use _wsystem() instead, eg:
_wsystem(cmd); // when using WCHAR[]/LPWSTR
_wsystem(cmd.c_str()); // when using std::wstring
system()/_wsystem() runs an instance of cmd.exe /C <command-line>, where <command-line> is the specified input string. But PowerShell.exe is its own app with its own command-line parameters. PowerShell.exe commands ARE NOT valid cmd.exe commands, ie you are trying to execute this:
cmd.exe /C Compress-Archive -Path <path> -DestinationPath <dest>
Which is not a valid command, as cmd.exe does not know what Compress-Archive is. You would need to execute this instead:
cmd.exe /C PowerShell -Command "Compress-Archive -Path \"<path>\" -DestinationPath \"<dest>\""
WCHAR cmd[(MAX_PATH*2)+70] = {};
swprintf(cmd, L"PowerShell -Command \"Compress-Archive -Path \\\"%s\\\" -DestinationPath \\\"%s\\\"", path, DestinationPath);
std::wstring cmd = L"PowerShell -Command \"Compress-Archive -Path \\\"" + path + L"\\\" -DestinationPath \\\"" + DestinationPath + L"\\\"";
But, you really should use CreateProcess() instead to run PowerShell.exe directly, don't use cmd.exe at all, eg:
STARTUPINFO si = {sizeof(STARTUPINFO), 0};
PROCESS_INFORMATION pi = {};
if (CreateProcess(NULL, cmd/*cmd.data()*/, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
Otherwise, put your PowerShell commands into a .bat script, eg:
#echo off
PowerShell -Command "Compress-Archive -Path \"%0\" -DestinationPath \"%1\""
And then you can use system()/_wsystem() to run that script, eg:
WCHAR cmd[(MAX_PATH*2)+25] = {};
swprintf(cmd, L"myscript.bat \"%s\" \"%s\"", path, DestinationPath);
_wsystem(cmd);
std::wstring cmd = L"myscript.bat \"" + path + L"\" \"" + DestinationPath + L"\"";
_wsystem(cmd.c_str());

JScript: identifying whether double quotes are passed to a WSH script

There are situations when it is important to identify whether double quotes are passed as arguments to a WSH script. For example because they should be passed to another executable to be run.
The standard parsing functions/objects:
objArgs = WScript.Arguments;
for (i = 0; i < objArgs.length; i++)
{
WScript.Echo(objArgs(i));
}
do not differentiate between:
cscript foo.js "bar"
and
cscript foo.js bar
Is it possible with some other approach?
Note: I also tried to sort of escape them with several combinations like:
cscript foo.js '"bar"'
It seems that they are simply stripped away.
Following #Ekkehard.Horner suggestions:
Solution
// parseArgs.js
// Parsing jscript script arguments verbatim
var Shell = new ActiveXObject("WScript.Shell"),
wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2"),
guid = (new ActiveXObject("Scriptlet.TypeLib")).GUID.substring(0,38),
windir=Shell.ExpandEnvironmentStrings("%WinDir%"),
winver="\"" + windir + "\\System32\\winver.exe\" " + guid,
pcol, pid, cmd;
// Run winver.exe hidden and get this script ID as its ParentProcessId
winver=winver.replace(/\\/g, "\\\\");
Shell.Run("winver " + guid, 0);
pcol = new Enumerator (wmi.ExecQuery(
"SELECT * From Win32_Process WHERE CommandLine='"+ winver + "'",
"WQL", 32));
for (; !pcol.atEnd(); pcol.moveNext()){
var prc = pcol.item();
pid=prc.ParentProcessId;
prc.Terminate;
}
// Get the command line for the found PID
pcol = new Enumerator (wmi.ExecQuery(
"SELECT * From Win32_Process WHERE ProcessID="+ pid,
"WQL", 32));
for (; !pcol.atEnd(); pcol.moveNext()){
var prc = pcol.item();
cmd =prc.CommandLine;
}
WScript.Echo(cmd);
// Parse command line for arguments
var ags,
parseCmd=function(cmd){// WMI trims initial spaces
var p = new Object(),
re =/^"/.test(cmd) ? /"[^"]+" */ : /\S+\s*/;
p.nxt=re.test(cmd) ? cmd.match(re)[0] : ""; // extract next token
p.rst=cmd.replace(re, "") ; // remainder
return(p);
}
// Strip c/wscript path
ags=parseCmd(cmd).rst
//WScript.Echo(ags);
// Remove WSH "//xxx" options
ags=ags.replace(/\/\/\w+ +/g, "")
//WScript.Echo(ags);
// Strip script name and get arguments
ags=parseCmd(ags).rst
WScript.Echo(ags);
// Loop args and store as an array
var i=1, aags=[];
while(ags != ""){
var p =parseCmd(ags);
ags=p.rst;
aags.push(p.nxt.replace(/ +$/, ""));
WScript.Echo(i, p.nxt);
i++;
}
WScript.Echo(aags);
Test
Running parseArgs.js gives:
> cscript //nologo parseArgs.js "hello" world
cscript //nologo parseArgs.js "hello" world
"hello" world
1 "hello"
2 world
"hello",world
The line:
> parseArgs.js "hello" world
gives similar results.
Comments
Do we need such a convoluted script? Short answer: no. Long: depends.
In general, assuming you know the name of your script when it is run, you could query WMI for it.
Anyway, when you deploy your script, you do not normally have control on the deploy directory. So, if there is another script running under the same name, you can't know for sure which one is yours.
Another not so edge case is when there are two or more instances of your script running.
The strategy here is to run some dummy standard Windows executable (winver.exe) hidden, passing to it a GUID. In this way, it is safe to identify winver.exe command line by the unique GUID and consequently your script as the parent of winver.exe.
winver.exe does not require arguments, but does not protest if you pass some to it.

Visual Studio Console Application Installer - Schedule Windows Task

Does anyone know how to create an installation project using Visual Studio 2010 that creates a Windows Scheduler task? I'm building an installer for a Console Application that needs to run every X minutes, and it would be nice for the customer not to have to schedule it manually.
Thanks!
in Wix (.wixproj) you can do it in a CustomAction, written in Jscript, that invokes Schtasks.exe .
I don't know about VS2010's support of WIX, I think it is built-in.
The custom action module (the .js file) should have a function to run a schtasks command, something like this:
function RunSchtasksCmd(command, deleteOutput) {
deleteOutput = deleteOutput || false;
var shell = new ActiveXObject("WScript.Shell");
var fso = new ActiveXObject("Scripting.FileSystemObject");
var tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder);
var tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName());
var windir = fso.GetSpecialFolder(SpecialFolders.WindowsFolder);
var schtasks = fso.BuildPath(windir,"system32\\schtasks.exe") + " " + command;
// use cmd.exe to redirect the output
var rc = shell.Run("%comspec% /c " + schtasks + "> " + tmpFileName, WindowStyle.Hidden, true);
if (deleteOutput) {
fso.DeleteFile(tmpFileName);
}
return {
rc : rc,
outputfile : (deleteOutput) ? null : tmpFileName
};
}
And then, use that from within the custom action function itself, something like this
var r = RunSchtasksCmd("/Create Foo bar baz");
if (r.rc !== 0) {
// 0x80004005 == E_FAIL
throw new Error("exec schtasks.exe returned nonzero rc ("+r.rc+")");
}
var fso = new ActiveXObject("Scripting.FileSystemObject");
var textStream = fso.OpenTextFile(r.outputfile, OpenMode.ForReading);
// Read from the file and parse the results.
while (!textStream.AtEndOfStream) {
var oneLine = textStream.ReadLine();
var line = ParseOneLine(oneLine); // look for errors? success?
}
textStream.Close();
fso.DeleteFile(r.outputfile);
Some people say writing CA's in script is the wrong thing to do, because they are hard to maintain, hard to debug, or it's hard to do them right. I think those are bad reasons. CA's implemented correctly in script, work well.
WIX has its own custom action for creating windows task and scheduling them.
<CustomAction Id="CreateScheduledTask" Return="check" Directory="Application" ExeCommand=""[SystemFolder]SCHTASKS.EXE" /CREATE /SC DAILY /TN "My Task" /ST 12:00:00 /TR "[INSTALLLOCATION]Apps\File.exe" /RU [%USERDOMAIN]\[LogonUser]" />
Above command will create a task with name 'My Task' which will execute everyday at 12 AM.
SCHTASKS command is used to create and schedule a windows task.

How to terminate processes from a wmi permanent event consumer

I am trying to create a permanent wmi event consumer that will wait for a process to be created with a specific commandline parameter then terminate it.
So far I can get my event handler to fire when expected and write to a test log file.
I can even access parameters from the WMI event by using the TargetEvent.TargetInstance. However when i try to call terminate on it, it fails.
I am also having trouble creating instances of objects like wscript.shell or wscript.network which fail to create an instance. I believe this might be because this script is not actually running in the windows script host.
So my question is how can I get the terminate method to work on my instance of Win32_Process or is there a way to call an external command (given I can't use wscript.shell object).
I got most of the details on how to create my mof file from here:
http://www.codeproject.com/KB/system/PermEvtSubscriptionMOF.aspx?display=Print
My Setup Mof File is the following:
#pragma namespace("\\\\.\\root\\subscription")
instance of __EventFilter as $EventFilter
{
Name = "My Test Filter";
EventNamespace = "Root\\Cimv2";
Query = "Select * From __InstanceCreationEvent Within 2 "
"Where TargetInstance Isa \"Win32_Process\" "
"And Targetinstance.Name = \"notepad.exe\" "
"And Targetinstance.CommandLine LIKE \"%test.txt%\"";
QueryLanguage = "WQL";
};
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "MyTestConsumer";
ScriptingEngine = "VBScript";
ScriptText =
"On Error Resume Next\n"
"'Set WshShell = WScript.CreateObject(\"WScript.Shell\")\n"
"Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\n"
"Set objFile = objFSO.OpenTextFile(\"c:\\log.txt\", 8, True)\n"
"objFile.WriteLine Time & \" \" & \" notepad started \" & TargetEvent.TargetInstance.Handle \n"
"objFile.Close\n"
"TargetEvent.TargetInstance.Terminate()\n";
};
instance of __FilterToConsumerBinding
{
Filter = $EventFilter;
Consumer = $Consumer;
};
My removal mof file is:
#pragma namespace("\\\\.\\root\\subscription")
#Pragma deleteInstance("__EventFilter.Name=\"My Test Filter\"",FAIL)
#Pragma deleteInstance("ActiveScriptEventConsumer.Name=\"MyTestConsumer\"",FAIL)
#pragma deleteinstance ("__FilterToConsumerBinding.Consumer="
"\"\\\\\\\\.\\\\root\\\\subscription:ActiveScriptEventConsumer.Name=\\\"MyTestConsumer\\\"\","
"Filter=\"\\\\\\\\.\\\\root\\\\subscription:__EventFilter.Name=\\\"My Test Filter\\\"\"", FAIL)
I have no idea what is the reason for this, but I have never managed to make it work either. At first glance it should - TargetEvent.TargetInstance.Name returns the process name, etc. But when calling a method, an error is written to wbemess.log:
Scripting engine says: Microsoft VBScript runtime error: Object doesn't support this property or method: 'TargetEvent.TargetInstance.Terminate'
(Wed Apr 13 19:44:54 2011.15735734) : Dropping event destined for event consumer ActiveScriptEventConsumer="TestConsumer" in namespace //./root/subscription
Here is my workaround:
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "New Process Instance Filter";
Query = "Select * From __InstanceCreationEvent Within 2"
"Where TargetInstance Isa \"Win32_Process\" "
"And Targetinstance.Name = \"notepad.exe\" ";
QueryLanguage = "WQL";
};
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "TargetEventConsumer";
ScriptingEngine = "VBScript";
ScriptText =
"Set objWmi = GetObject(\"winmgmts:\")\n"
"\n"
"Set objProcess = objWmi.Get(\"Win32_Process.Handle='\" _\n"
" & TargetEvent.TargetInstance.Handle & \"'\")\n"
"\n"
"objProcess.Terminate\n";
};
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
In the script I use SWbemServices.Get() to get the created process instance and then Terminate works. Just pass TargetEvent.TargetInstance.Handle to SWbemServices.Get().
You failed to use WshShell object because you attempted to create it with WScript.CreateObject and WScript is not available to the ActiveScriptConsumer VBScript engine. It should work if you use the VBScript CreateObject() function instead. Same with WshNetwork.

How can I move files to the Recycle Bin in a Windows batch script or Perl?

I've got a Windows XP batch script which cleans some directories, but I would like to move the deleted files to trash instead of using plain del. How is this done?
It looks like the only languages I can use for this is plain batch or Perl.
use Win32::FileOp qw(Recycle);
Recycle(#ARGV);
Write a VBS script (Original Link) then call it with MyDelScript.vbs
function main()
{
if (WScript.Arguments.length != 1)
{
WScript.Echo("<Insert informative error message here>");
return;
}
var Path = WScript.Arguments(0);
var Shell = WScript.CreateObject("Shell.Application");
var Item = Shell.Namespace(0).ParseName(Path);
Item.InvokeVerb("delete");
}
The Win32::FileOp module has a Recycle function. From the docs:
Recycle #filenames
Send the files into the recycle bin. You will not get any confirmation dialogs.
Returns true if successful.
It can be done like this with plain batch and embedded VBScript. Put the following code into a file called recycle.cmd:
<!-- : Begin batch script
#echo off
if "%1"=="" (
echo Usage: %~nx0 FILE_TO_RECYCLE[...]
echo This script puts files into the recycle bin
exit /b 1
)
cscript //nologo "%~f0?.wsf" %*
exit /b %errorlevel%
----- Begin embedded wsf script --->
<job><script language="VBScript">
Set app = WScript.CreateObject("Shell.Application")
Set fso = CreateObject("Scripting.FileSystemObject")
For Each arg In WScript.Arguments
If fso.FileExists(arg) Then
Set file = fso.GetFile(arg)
Set folderItem = app.Namespace(0).ParseName(file.Path)
folderItem.InvokeVerb("delete")
Else
WScript.Echo "File not found: " & arg
End If
Next
</script></job>
Example:
echo This file is dirt.> dirt.txt
echo This file is trash.> trash.txt
recycle dirt.txt trash.txt
As you can see the script allows recycling multiple files with one command.
It does not suppport the wildcards * and ? though.
The idea of embedding VBScript inside a batch file is taken from dbenham's answer to Is it possible to embed and execute VBScript within a batch file without using a temporary file? (scroll down to UPDATE 2014-04-27).
You could use the "recycle" utility which is part of CmdUtils from MaDdoG Software. From the page listing -
Recycle, a safe replacement for the DEL command, that sends files to the recycle bin instead of deleting them. Recycle is also more flexible than DEL; you can specify multiple files at once (or use wildcards), and you can recycle whole directories at once (be careful!)
I would suggest you try its various switches before you incorporate it into your script - there is quite a bit of deviation from the default behaviour of the "del" command.
UPDATE: Contrary to my original claim that the following code does not work, it indeed seems to work. I just forgot that the file I wanted to delete was not in $ENV{TEMP} but a subdirectory of $ENV{TEMP}. The problem is, the file does not go to the Recycle Bin.
The right solution is to use Win32::FileOp but I am going to leave this script here as an example of how to use Win32::API and Win32::API::Struct. I would appreciate it if anyone can point out what I am doing wrong. For your reference:
SHFileOperation: http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx
LPSHFILEOPSTRUCT: http://msdn.microsoft.com/en-us/library/bb759795(VS.85).aspx
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec::Functions qw( catfile );
use Win32::API;
Win32::API::Struct->typedef(
SHFILEOPSTRUCT => qw(
HWND hwnd;
UINT wFunc;
LPCTSTR pFrom;
LPCTSTR pTo;
FILEOP_FLAGS fFlags;
BOOL fAnyOperationsAborted;
LPVOID hNameMappings;
LPCTSTR lpszProgressTitle;
)
);
Win32::API->Import(
shell32 => q{ int SHFileOperation( LPSHFILEOPSTRUCT lpFileOp ) }
);
my $op = Win32::API::Struct->new( 'SHFILEOPSTRUCT' );
$op->{wFunc} = 0x0003; # FO_DELETE from ShellAPI.h
$op->{fFlags} = 0x0040; # FOF_ALLOWUNDO from ShellAPI.h
my $to_delete = catfile( $ENV{TEMP}, "test.file" );
$op->{pFrom} = $to_delete . "\0\0";
my $result = SHFileOperation( $op );
if ( $result ) {
warn sprintf "The operation failed: %4.4X\n", $result;
}
else {
if ( $op->{fAnyOperationsAborted} ) {
warn "Operation was aborted\n";
}
else {
warn "The operation succeeded\n";
}
}
__END__

Resources