How to terminate processes from a wmi permanent event consumer - vbscript

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.

Related

Adding multiple CC recipients in VBScript; Recipient.Type not working

I am working on a jsx (extendscript project) in which I am calling a VBS snippet with app.doScript. I cannot get the email_cc and email_cc2 recipients to show up as cc; they are stuck in the main "To" sending. I will not know in advance whether they exist in the users' address book, so I am adding them as recipients than trying to set their Type.
var vbs = 'Dim objOutl\r';
vbs += 'Set objOutl = CreateObject("Outlook.Application")\r';
vbs += 'Set objMailItem = objOutl.CreateItem(olMailItem)\r';
vbs += 'objMailItem.Display\r';
vbs += 'strEmailAddress = "' + email_address + '"\r';
vbs += 'objMailItem.Recipients.Add strEmailAddress\r';
vbs += 'strSubject = "' + the_subject + '"\r';
vbs += 'objMailItem.Subject = strSubject\r';
vbs += 'objMailItem.Body = "' + the_bodytext + '"\r';
if (email_cc && email_cc != "") {
vbs += 'Set cc1Recipient = objMailItem.Recipients.Add ("' + email_cc + '")\r';
if (email_cc2 && email_cc2 != "") {
vbs += 'Set cc2Recipient = objMailItem.Recipients.Add ("' + email_cc2 + '")\r';
vbs += 'cc1Recipient.Type = olCC\r';
vbs += 'cc2Recipient.Type = olCC\r';
}
else {
vbs += 'cc1Recipient.Type = olCC\r';
}
}
if (has_attachment) {
vbs += 'objMailItem.Attachments.Add "' + pdf_file + '"\r';
}
Check out the following points:
Try to use the fully qualified name in the code with the enum name, for example:
OlMailRecipientType.olCC
Use the Resolve method which attempts to resolve a recipient object against the address book. It returns true if the recipient was resolved.
Call the Save method to apply changes made through the OOM. Sometimes it makes sense to close the item, switch to another Outlook item, and then come back to check out results. Outlook caches changes and doesn't propagate changes made using the OOM immediately.
Read more about these methods and properties in the How To: Fill TO,CC and BCC fields in Outlook programmatically article.

Need to pass object and operation in a function that executes it

I need to pass an object and its operation in a function so that each time I can call the function only and save me to write same steps for all the objects like validating the object before performing an operation. Similar way to a Register User Function in QTP/UFT.
However, Testcomplete doesn't have this feature (atleast under my knowledge, would be happy to know if there is)
This is my code that I am trying but unable to:
Call OpenPageorTab("Aliases.Admin.wndMain.toolStrip", ".Visible")
Function OpenPageorTab(obj, method)
'if obj.Exists then
execute a = obj&method
delay(1000)
OpenPageorTab = True
'Else
log.Error "Unable to find the object"
OpenPageorTab = False
'End if
using if condition as i was passing object earlier instead of string
It fails at "execute" statement and gives me VbScript runtime error when executing this statement.
my question is two fold -
How do I pass objects and its operation in a function and execute it
Also is it possible to pass an object it self instead of string for ex:
obtoolbar = "Aliases.Admin.wndMain.toolStrip"
Call OpenPageorTab(obtoolbar, ".Visible")
Appreciate any help or direction on this issue
EDIT 1
I am somewhere close to an answer however not accurately. I am able to pass the object as string - Check the code below
Call OpenPageorTab("Aliases.Admin.wndMain.toolStrip", ".Click")
Function OpenPageorTab(obj, method)
' if obj.Exists then
eobj = "" & obj & method
execute (eobj)
delay(1000)
OpenPageorTab = True
' Else
log.Error "Unable to find the object"
OpenPageorTab = False
' End if
End Function
However I still need to pass the object something like
Set oToolStrip = Aliases.Admin.wndMain.toolStrip
Call OpenPageorTab(oToolStrip, ".Click")
This is something that I'm unable to do.
EDIT 2
I have already got the answer to this problem and have posted the solution. That being said, is there any way that Function can be utilized as a method ?
Here an example of how to reference a function and pass parameteers to it, including objects.
Const forReading = 1, forWriting = 2, forAppending = 8, CreateFile = True
Set my_obj = CreateObject("Scripting.FileSystemObject").OpenTextFile("c:\temp\test.txt", forWriting, CreateFile)
Function my_function(my_obj, method, text)
command = "my_obj." & method & " """ & text & """"
ExecuteGlobal command
End Function
'make a reference to our function
Set proc = GetRef("my_function")
'and call it with parameters, the first being the method invoked
Call proc(my_obj, "WriteLine", "testing")
'cleanup'
my_obj.Close
Set my_obj = Nothing
I was able to finally formulate the solution the below function can work as makeshift register function in TestComplete
Sub test
'Set the Object
Set pToolStrip = Aliases.Admin.wndMain.toolStrip.Button("User Admin")
Call GenericOperationFunc(pToolStrip, ".Click", "N")
'if you want to perform an operation which return a value
b = GenericOperationFunc(Aliases.Admin.wndPopup.Child(2), ".Caption", "Y")
End Sub
Public Function GenericOperationFunc(obj, method, Return)
GenericOperationFunc = False
on error resume next
if obj.Exists then
if Ret = "Y" then
eobj = "NewText="&"obj" & method
execute (eobj)
GenericOperationFunc = NewText
Delay(500)
Else
eobj = "obj" & method
execute (eobj)
delay(1000)
GenericOperationFunc = True
End if
Else
log.Error "Unable to find the object"
GenericOperationFunc = False
End if
End Function
'log.error, delay, aliases, ptoolstrip(object) are testcomplete specific

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.

Can VBScript add properties to objects like JScript?

I am playing with Windows Script Host VBScript and I am curious if VBScript is capable of adding/removing properties like JScript can.
For example:
var global = this;
var test = function() {
if ('greeting' in global) {
WScript.echo (
'global has property named greeting with value: ' +
global.greeting +
'.'
);
} else {
WScript.echo('global has no property named greeting.');
}
};
test();
global.greeting = 'Hello, World!';
test();
delete global.greeting;
test();
This code determines the global scope (JScript has no initial access to global scope such as window in browsers or global in Node.js, so I have to find it myself).
The test() function checks if the global object has a key named "greeting", and reports its current state as output.
The code does an initial test to show that global object has no greeting key, then sets the greeting property, then does a second test to show that the greeting key has been added to the global object. After this, the greeting property is deleted and a third test is run to show that the key is no longer a part of the global object.
Is this possible to replicate in VBScript?
I know VBScript has Scripting.Dictionary object that can be used to store such information, but I am curious if there is a way to hook existing objects with new properties and delete such properties in VBScript, or if VBScript has no parallel to JScript's {} construct other than Scripting.Dictionary or Classes (whose properties are immutable).
Your specific example could be emulated with something like this:
Set global = CreateObject("Scripting.Dictionary")
Sub test
If global.Exists("greeting") Then
WScript.Echo "global has property named greeting with value: " & _
global("greeting") & "."
Else
WScript.Echo "global has no property named greeting."
End If
End Sub
test
global("greeting") = "Hello, World!"
test
global.Remove("greeting")
test
but in general patching objects, or even regular inheritance, is not supported in VBScript. The best you could do to extend a class is wrap it in a class of your own:
Class MyClass
Private nested_
Public Sub Class_Initialize
Set nested_ = CreateObject("Some.Other.Class")
End Sub
Public Function Foo(val) 'wrapped method
Foo = nested_.Foo(val)
End Sub
Public Function Bar(val) 'patched method
x = nested_.Bar(val)
Bar = x * 42
End Sub
End Class

Why is CF FTP transfer speed multiple times slower than standard FTP?

I have a ColdFusion application that I use to transfer files between our development and production servers. The code that actually sends the files is as follows:
ftp = new Ftp();
ftp.setUsername(username);
ftp.setPassword(password);
ftp.setServer(server);
ftp.setTimeout(1800);
ftp.setConnection('dev');
ftp.open();
ftp.putFile(transferMode="binary",localFile=localpath,remoteFile=remotepath);
ftp.close(connection='dev');
When sending a file using the above code, I cap out at just under 100KB/s(monitored via FileZilla on the receiving server). If I send the exact same file using the Windows command-line FTP tool, my speeds are upwards of 1000KB/s.
I created a brand new file with nothing but the code above and that has no effect on the transfer speed, so I know it has nothing to do with the surrounding code in the original application.
So, what could be causing these abysmally low speeds?
Edit: All tests are being done transferring files from my production server to my development server. I also tried using the <cfftp> tag instead of cfscript, and I have the same results.
Edit #2: I ended up using cfexecute, the code is as follows:
From my FTP script:
public function sendFiles(required string localpath, required string remotepath) {
this.writeFtpInstructions(localpath);
exe = "C:\Windows\system32\ftp.exe";
params = "-s:" & request.localapproot & "/" & "upload.txt";
outputfile = request.localapproot & '/ftp.log';
timeout = 120;
Request.cliExec(exe,params,outputfile,timeout);
}
public function writeFtpInstructions(required string localpath) {
instructions = request.localapproot & "/" & "upload.txt";
crlf = chr(13) & chr(10);
data = "";
data &= "open " & this.server & crlf;
data &= this.username & crlf;
data &= this.password & crlf;
data &= "cd " & request.remoteapproot & crlf;
data &= "put " & localpath & crlf;
data &= "quit";
FileWrite(instructions, data);
}
The cliExec() function(necessary to create a wrapper since there is no equivalent of cfexecute in cfscript):
<cffunction name="cliExec">
<cfargument name="name">
<cfargument name="arguments">
<cfargument name="outputfile">
<cfargument name="timeout">
<cfexecute
name="#name#"
arguments="#arguments#"
outputFile="#outputfile#"
timeout="#timeout#" />
</cffunction>
With my experience using cfftp on CF9, it was impossible to transfer larger files. If you view the active transfers on the FTP server side, you will notice that the transfers start out at top speed, but as more & more data is transmitted the speeds keep dropping. After 100 MB had been transfered, the reduction started to become very drastic, until they eventually reached a single digit crawl. Eventually the transfer timed out & failed. I was trying to work with a 330 MB file & found it impossible to transfer using cfftp. The cfexecute was not an option for me using the standard windows ftp.exe, because it doesn’t seem to support SFTP.
I ended up seeking out an external java class to use through coldfusion & settled on JSch (http://www.jcraft.com/jsch/). Ironically, CF9 appears to use a variation of this class for CFFTP (jsch-0.1.41m.jar), but the results are much different using this latest downloaded version (jsch-0.1.45.jar).
Here is the code that I put together for a proof of concept:
<cfscript>
stAppPrefs = {
stISOFtp = {
server = 'sftp.server.com',
port = '22',
username = 'youser',
password = 'pa$$w0rd'
}
};
/* Side-Load JSch Java Class (http://www.jcraft.com/jsch/) */
try {
// Load Class Using Mark Mandel's JavaLoader (http://www.compoundtheory.com/?action=javaloader.index)
/*
Add Mark's LoaderClass To The ColdFusion Class Path Under CF Admin:
Java and JVM : ColdFusion Class Path : C:\inetpub\wwwroot\javaloader\lib\classloader-20100119110136.jar
Then Restart The Coldfusion Application Service
*/
loader = CreateObject("component", "javaloader.JavaLoader").init([expandPath("jsch-0.1.45.jar")]);
// Initiate Instance
jsch = loader.create("com.jcraft.jsch.JSch").init();
}
catch(any excpt) {
WriteOutput("Error loading ""jsch-0.1.45.jar"" java class: " & excpt.Message & "<br>");
abort;
}
/* SFTP Session & Channel */
try {
// Create SFTP Session
session = jsch.getSession(stAppPrefs.stISOFtp.username, stAppPrefs.stISOFtp.server, stAppPrefs.stISOFtp.port);
// Turn Off & Use Username/Password
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(stAppPrefs.stISOFtp.password);
session.connect();
// Create Channel To Transfer File(s) On
channel = session.openChannel("sftp");
channel.connect();
}
catch(any excpt) {
WriteOutput("Error connecting to FTP server: " & excpt.Message & "<br>");
abort;
}
// Returns Array Of Java Objects, One For Each Zip Compressed File Listed In Root DIR
// WriteDump(channel.ls('*.zip'));
// Get First Zip File Listed For Transfer From SFTP To CF Server
serverFile = channel.ls('*.zip')[1].getFilename();
/* Debug */
startTime = Now();
WriteOutput("Transfer Started: " & TimeFormat(startTime, 'hh:mm:ss') & "<br>");
/* // Debug */
/* Transfer File From SFTP Server To CF Server */
try {
// Create File On Server To Write Byte Stream To
transferFile = CreateObject("java", "java.io.File").init(expandPath(serverFile));
channel.get(serverFile, CreateObject("java", "java.io.FileOutputStream").init(transferFile));
// Close The File Output Stream
transferFile = '';
}
catch(any excpt) {
WriteOutput("Error transfering file """ & expandPath(serverFile) & """: " & excpt.Message & "<br>");
abort;
}
/* Debug */
finishTime = Now();
WriteOutput("Transfer Finished: " & TimeFormat(finishTime, 'hh:mm:ss') & "<br>");
expiredTime = (finishTime - startTime);
WriteOutput("Duration: " & TimeFormat(expiredTime, 'HH:MM:SS') & "<br>");
WriteOutput("File Size: " & NumberFormat(Evaluate(GetFileInfo(ExpandPath(serverFile)).size / 1024), '_,___._') & " KB<br>");
WriteOutput("Transfer Rate: " & NumberFormat(Evaluate(Evaluate(GetFileInfo(ExpandPath(serverFile)).size / 1024) / Evaluate(((TimeFormat(expiredTime, 'H') * 60 * 60) + (TimeFormat(expiredTime, 'M') * 60) + TimeFormat(expiredTime, 'S')))), '_,___._') & " KB/Sec <br>");
/* // Debug */
channel.disconnect();
session.disconnect();
</cfscript>
Results:
Transfer Started: 09:37:57
Transfer Finished: 09:42:01
Duration: 00:04:04
File Size: 331,770.8 KB
Transfer Rate: 1,359.7 KB/Sec
The transfer speed that was achieved is on par with what I was getting using the FileZilla FTP Client & manually downloading. I found this method to be a viable solution for the inadequacy of cfftp.
I have been looking and I dont have an answer about why it is slower. But, in theory, you should be able to use cfexecute to do this through the windows command line. You might could even create a batch file and do it in one call.

Resources