Showing posts with label COM. Show all posts
Showing posts with label COM. Show all posts

Thursday, October 20, 2011

Query Installed Apps a Different Way

For whatever reason, in some environments, a WMI query of Win32_Product is God-awful slow.  I've seen this on Windows 7 and Windows 7 SP1 clients, as well as on Windows Server 2008 and 2008 R2.  The symptom can be seen from WIM script, WBEM, and using WMIC from a command console with very similar results:  The query hangs for 20-25 seconds and then begins executing in spurts.  Other Win32 classes work fine, from what I've seen, it's just Win32_Product for some reason.  One workaround is to dump a registry output file, and scrub it to make a "clean" output file.  You can port this to PowerShell or KiXtart if you want (or whatever you prefer, I really don't care as long as you're happy and that makes me happy so we're all happy. yay!)

'****************************************************************
' Filename..: installedApps.vbs
' Author....: David M. Stein aka Scriptzilla aka dipshit
' Date......: 10/20/2011
' Purpose...: save query of installed applications to local file
'****************************************************************

Const strInputFile  = "c:\regoutput.txt"
Const strOutputFile = "c:\installedApps.txt"

Const ForReading = 1
Const ForWriting = 2
Const adVarChar = 200

cmd = "reg query hklm\software\microsoft\windows\currentversion\uninstall /s >" & strInputFile

On Error Resume Next

Set objShell = CreateObject("Wscript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

wscript.echo "info: executing shell command to create temp file..."

objShell.Run "cmd /c " & cmd, 7, True

wscript.echo "info: getting temp file for input..."

If objFSO.FileExists(strInputFile) Then
 wscript.echo "info: reading temp file..."
 Set objFile = objFSO.OpenTextFile(strInputFile, ForReading)
 Set objFile2 = objFSO.CreateTextFile(strOutputFile, True)

 Set rs = CreateObject("ADODB.RecordSet")

 rs.CursorLocation = adUseClient
 rs.Fields.Append "productname", adVarChar, 255
 rs.Open

 Do Until objFile.AtEndOfStream
     strLine = objFile.Readline
     If Left(strLine, 25) = "    DisplayName    REG_SZ" Then
      strOutput = Trim(Mid(strLine, 30))
   rs.AddNew
   rs.Fields("productname").value = strOutput
   rs.Update
     End If
 Loop
 
 rs.Sort = "productname"
 
 Do Until rs.EOF
     objFile2.WriteLine(rs.Fields("productname").value)
  rs.MoveNext
 Loop
 rs.CLose
 Set rs = Nothing
 
 objFile.Close
 objFile2.Close
 wscript.echo "info: finished scrubbing input to new output file"
Else
 wscript.echo "fail: temp file not found"
End If

Set objFSO = Nothing
Set objShell = Nothing
'----------------------------------------------------------------

wscript.echo "info: processing complete!"

Tuesday, March 2, 2010

Now vs Now: VBScript and KiXtart

Date values are a very common aspect to script writing and most any programming situation in general.  How dates are stored, presented and manipulated varies widely from one programming language to another.

Let’s look at the “Now” function included with Windows Scripting Host’s VBScript.  This function returns the current date and time in “mM/dD/YYYY hH:MM:SS XM” format.

In case you’re wondering what the hell “mM” and “hH” imply: they imply zero-trim numbers.  In other words 8:01:42 AM is not stored as 08:01:42 AM. The leading zero is omitted.  Same for month (“mM”), and day (“dD”) as well.

But KiXtart, one of my all-time favorite scripting languages, handles date values quite differently.  At first it may seem almost identical, but as you pick things apart you start to see the deltas.

(kixtart)
? @date+" "+@time
>> 2010/03/02 08:20:01

(vbscript)
Wscript.echo Now
>> 3/2/2010 8:20:01 AM


When you compare the output of each result above, not only are the order of YYYY/MM/DD flipped from M/D/YYYY, but the zero-trim is not used with KiXtart.  Is this “bad” or “wrong”?  No.  Just different.  There are times when this is actually a very handy benefit to have as the default.  But also notice that the time stamp is formatted differently.  And the AM/PM suffix is not shown.


So, how can we make KiXtart do it the way VBScript does it?  This is assuming you need to make it do that, of course, which you may not.  But this is for demonstration purposes, so cut me some slack - if you will.


Below are two examples for producing a VBScript formatted “Now” result.  The first one uses a rudimentary date->string parsing technique.  The second does a cop-out and simply knocks on the door of the ScriptControl COM interface to make VBScript do the work and hand back a result.  The performance overhead is about the same at this scalar load level.  If you start piling in a lot more, the results can shift the balance of performance in either direction, depending upon the nature of what your doing (math, string, date, or object management tasks).


------------------------------------------------
Break ON

Function Now1()
$today = ""+@monthno+"/"+@mdayno+"/"+@year
$arrTime = Split(@time, ":")
$hour = $arrTime[0]
$min = $arrTime[1]
$sec = $arrTime[2]
If Int($hour) < 12
$sfx = "AM"
Else
$sfx = "PM"
EndIf
$Now1 = $today+" "+Int($hour)+":"+$min+":"+$sec+" "+$sfx
EndFunction

Function Now2()
Dim $sc, $result
$sc = CreateObject("ScriptControl")
$sc.Language = "vbscript"
$result = $sc.Eval('Now()')
$sc = 0
$Now2 = $result
EndFunction

? "Kix: "+Now1()
? "VBs: "+Now2()


The results should be identical.  Could I/you refactor the Now1() function to nest statements and further compact the code?  Sure.  Does it buy any performance gains? Not really.



Consider this variation…



Function Now1()
$today = ""+@monthno+"/"+@mdayno+"/"+@year
$arrTime = Split(@time, ":")
If Int($arrTime[0]) < 12
$Now1 = $today+" "+Int($arrTime[0])+":"+$arrTime[1]+":"+
$arrTime[2]+" AM”
Else
$Now1 = $today+" "+Int($arrTime[0])+":"+$arrTime[1]+":"+
$arrTime[2]+" PM”
EndIf
EndFunction


More compact for sure.  But there’s a hidden, even if trivial price: The repeated use of an array index request.  There are other ways to compact/refactor this of course, but for such few lines of code the pay-offs are difficult to justify beyond elegant coding form (aesthetics).  Oh well, blah blah blah.  What do I know anyway.  I just finished a huge “breakfast-for-dinner” and was then told my car repairs would cost way too much, so I’m blabbering to let off steam.  I hope you enjoyed this.

Sunday, February 28, 2010

Automate DCDiag on your Domain Controllers

I’ve been doing this for (literally) years.  About 7 years to be exact.  You can do this with NETDIAG, REPADMIN and several other “diagnostic” utilities that work from the command line.

The idea is to wrap the diagnostic operation inside a script so that you can capture the output in a text file, then turn around and open the text file to parse it for what you want.  Then you can do almost anything with that information:

  • Generate a summary report file
  • Send the results into a database table
  • Send the results as an e-mail report
  • and on and on and on…

There are several ways to set this up as well.  For this example I’m using a VBScript file, a domain user (aka “service” or “proxy”) account, and the Windows Task Scheduler on a Windows Server 2008 domain controller.  This works just fine on Windows Server 2003 and Windows Server 2008 R2 as well.

The Script:

Const logFileName = "x:\logs\dcdiag.log"
Const ForReading = 1
Const ForWriting = 2

Dim objShell, objFSO, computer, domain, cmdstr
Dim objFile, testLabel, passed, failed

Set objShell = CreateObject("Wscript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

computer = Ucase(objShell.ExpandEnvironmentStrings("%computername%"))
domain = Ucase(objShell.ExpandEnvironmentStrings("%userdnsdomain%"))

cmdstr = "cmd /c dcdiag /v >" & logFileName

objShell.Run cmdstr, 1, True

If objFSO.FileExists(logFileName) Then
passed = 0
failed = 0

Set objFile = objFSO.OpenTextFile(logFileName, ForReading)

Do Until objFile.AtEndOfStream
strLine = objFile.Readline

testLabel = Mid(strLine, 36)

If InStr(1, testLabel, computer & " passed test") > 0 Then
wscript.echo testLabel
passed = passed + 1
ElseIf InStr(1, testLabel, computer & " failed test") > 0 Then
wscript.echo testLabel
failed = failed + 1
ElseIf InStr(1, testLabel, domain & " passed test") > 0 Then
wscript.echo testLabel
passed = passed + 1
ElseIf InStr(1, testLabel, domain & " failed test") > 0 Then
wscript.echo testLabel
failed = failed + 1
Else
'
End If
Loop
objFile.Close
Set objFSO = Nothing

wscript.echo "Passed " & passed & " tests"
wscript.echo "Failed " & failed & " tests"
Else
wscript.echo "fail: log file not found"
End If

Set objFSO = Nothing
Set objShell = Nothing


The Explanation:



The top section defines the path and filename for the output file we’re going to capture and analyze.  Next we define some variables.  Then we instantiate the Shell and FileSystemObject object interfaces. 



We use the Shell object to fetch the name of the computer and the domain name. We need those to help sift through the output file and find the matching lines we want to look at.  The Shell object is also used to run the DCDIAG command via the “Run” method.



After running the shell command, we then check if the output file exists.  If it does, we open it and read through it line-by-line looking for matching strings.  Within each matching string we look for “passed” or “failed” and count them up as well as echo them to the command prompt.



At the end we mop up and then display the tally for passed and failed tests.



Important Note: This is only ONE form of doing this.  There is no limit to what you CAN do.  For example, instead of echoing the testLabel contents, we could concatenate them into a report text block and send it via CDOsys (e-mail) or stuff it into a database via ADO or XML or generate an XML or HTML report, or even stuff it directly into a Microsoft Word or Excel document.  The possibilities are endless.



If anyone is interested in variations on this just post a comment and I’ll see what I can do.  I hope this helps someone out there?

Friday, December 25, 2009

Scraping Web Pages with VBScript and KiXtart

Here’s a quick example of how to scrape HREF and IMG strings from a web page using VBScript and KiXtart with the InternetExplorer.Application COM object.  Thanks to Paul Sadowski for the basis of the VBScript example (with only very slight modifications).

VBScript Code

url = "http://www.textpad.com"

Set ie = CreateObject("InternetExplorer.Application")
ie.Navigate url

Wscript.Echo "DOCUMENT HYPERLINKS" & vbCRLF
Do Until ie.ReadyState = 4
Wscript.Sleep 2
Loop
For each link in ie.Document.Links
Wscript.Echo link, link.InnerText
Next

Wscript.Echo "------------------------------------"
Wscript.Echo "DOCUMENT IMAGE TAGS" & vbCRLF

For each img in ie.Document.Images
Wscript.Echo img.Src
Next

ie.Quit


KiXtart Code



break ON

$url = "http://www.textpad.com"

$ie = CreateObject("InternetExplorer.Application")
$ie.Navigate($url)
? "DOCUMENT HYPERLINKS"

While $ie.ReadyState <> 4
Sleep 2
Loop

For each $link in $ie.Document.Links
? $link+"="+$link.InnerText
Next

? "DOCUMENT IMAGE TAGS"

For each $pix in $ie.Document.Images
? $pix.Src
Next

$ie.Quit()

Thursday, November 19, 2009

KiXtart: Using FSO File.ReadAll()

Another example of making use of one scripting language or API from another.  In this case, I’m calling on the FileSystemObject API to invoke the ReadAll() function on a file from a KiXtart script.  Just to show that there are all sorts of things you can do like this…

function ReadFile($filepath)
$fso = CreateObject("Scripting.FileSystemObject")
if exist($filepath)
$file = $fso.OpenTextFile($filepath)
$text = $file.ReadAll()
$file.Close()
$file = 0
else
? "error: file not found"
endif
$readfile = $text
endfunction

; example...

? readfile("c:\myfile.txt")

Wednesday, October 14, 2009

So, what do you get with that PowerShell Combo?

Fries? A drink? A kids toy?  As it turns out, not much.  This is just one example of what has been a trend that really bothers me about the so-called “examples” of PowerShell being published on the net lately.  Most are focused on WMI queries (WQL), or COM marshalling using the New-Object –COM method.  This really doesn’t do anything innovative or beneficial beyond learning a “different” way to code.  All of the WQL examples are simply search/replace code chunks which look almost identical to VBscript examples.

VBScript

Set objOutlook = CreateObject("Outlook.Application")
Set Namespace = objOutlook.GetNamespace("MAPI")

For each f1 in NameSpace.Folders
wscript.echo f1.Name
Next

For each objFolder in NameSpace.Folders
For each f1 in objFolder.Folders
wscript.echo vbTab & f1.Name
Next
Next


PowerShell



$Outlook = New-Object -com Outlook.Application
$Namespace = $outlook.GetNamespace("MAPI")

"{0} Root Folders: " -f $Namespace.folders.count
foreach ($Fl in $namespace.Folders) {
" {0}" -f $Fl.Name}
""
foreach ($Folder in $Namespace.Folders) {
"`'{0}`' subfolders: " -f $Folder.Name
foreach ($fl in $Folder.Folders) {
" {0}" -f $Fl.Name
}
}


I’m not at all trying to “bash” PowerShell.  It’s a cool language, but it has it’s issues that bother me.  I don’t consider it a classic or “true” scripting language as much as programmatic macro language.  Most of the benefits it provides via brevity of code are derived from cmdlets, which are similar to making DLL’s for COM extensibility, or UDF files for KiXtart, and so on.  Blah blah blah.  whatever.  I’m no pundit, so what I have to say doesn’t really matter that much really.  I forgot what point I was trying to make.