I ran into an issue where I needed to count the number of xml files in a directory. The problem was there are 10,000 other files in the directory. The application team didn’t care about the extra files and didn’t want to clean them out. All they wanted to know was when the number of XML files was over 15.
First I tried a basic vb script, like this.
Set objFSO=CreateObject(“Scripting.FileSystemObject”)
Set oArgs = WScript.Arguments
FolderName = oArgs(0)
Set objFolder=objFSO.GetFolder(FolderName)
Set Dirfiles = objFolder.Files
Int filecount = 0
For each file In Dirfiles
sext = objFSO.GetExtensionName(file.Path)
If LCase(sext) = “xml” Then
filecount = filecount+1
End If
Next
WScript.Echo filecount
This works fine in a directory with only a few files but my directory has 10,000 other files in it. I ran this script waited 10 minutes and then canceled it. Obviously this was not going to work as the script has to touch every file in the directory. Operations Manager would time out way before the script finishes.
So after many Google and Bing searches looking for a different vb scripts to count only the xml files, I decided to see how easy it would be in C#.
In C# it was a piece of cake. You can download the C# project file with source here.
https://www.scom2k7.com/downloads/filecount.zip
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace filecount
{
class Program
{
static void Main(string[] args)
{
string directoryPath = args[0];
string eXtension = “*.” + args[1];
int fileCount = System.IO.Directory.GetFiles(directoryPath,
eXtension).Length;
Console.WriteLine(fileCount);
}
}
}
I ran the executable that visual studio created with two parameters “directory” and “extension” and it took one second to count the number of xml files in the directory. This is what I was looking for, way faster and more efficient.
So now I have a working executable the takes two parameters. How do I get it to work with SCOM?
The easiest way was to remove the Console.WriteLine(fileCount) and set the count to the exit code.
Environment.Exit(fileCount);
Now all I need to do is wrap this executable with a vbscipt and have SCOM call it. You can download the vb script here. https://www.scom2k7.com/downloads/advanced.zip
Dim oAPI, oBag, objShell, objFSO, objFile, myCMD, bWaitOnReturn, returnCmd, oArgs
Set oAPI = CreateObject(“MOM.ScriptAPI”)
Set oBag = oAPI.CreateTypedPropertyBag(StateDataType)
Set oArgs = WScript.Arguments
If oArgs.Count < 3 Then
Call oAPI.LogScriptEvent(“FileCountCSharp.vbs”, 500, 0, “Script aborted. Not enough parameters provided.”)
WScript.Quit -1
End If
folder = oArgs(0)
extension = oArgs(1)
userCount = cint(oArgs(2))
bWaitOnReturn= True
Set objShell=CreateObject(“WScript.Shell”)
Set objFSO=CreateObject(“Scripting.FileSystemObject”)
strPath=“C:\ScomTools\filecount.exe”
If objFSO.FileExists(strPath) Then
set objFile=objFSO.GetFile(strPath)
myCMD = strPath & ” “ & folder & ” “ & extension
returnCmd = objShell.Run (myCMD,0,bWaitOnReturn)
Else
Call oAPI.LogScriptEvent(“filecount.exe”, 510, 0, “Can’t find EXE to run Script”)
WScript.Quit
End If
If returnCmd > userCount Then
strReturn = userCount
Call oBag.AddValue(“State”,“BAD”)
Call oBag.AddValue(“ret”,strReturn)
Else
Call oBag.AddValue(“State”,“GOOD”)
End If
Call oAPI.Return(oBag)
To test it I ran this command cscript c:\temp\newtest.vbs “c:\temp” “xml” 4 saying if there are more than 4 files in the directory and should return BAD state and how many files actual files are in the directory. This is what I got back.
<DataItem type=”System.PropertyBagData” time=”2009-06-17T10:58:59.3066298-04:00″
sourceHealthServiceId=”B3B5A38D-0DBE-5CA9-592D-B76333A989D8″>
<Property Name=”State”VariantType=”8″>BAD</Property>
<Property Name=”ret” VariantType=”3″>5</Property></DataItem>
Looks good now to test a good condition. cscript c:\temp\newtest.vbs “c:\temp” “xml” 25 saying if there is more than 25 xml files create an alert. There is not more than 25 files so the scom script should return good.
<DataItem type=”System.PropertyBagData” time=”2009-06-17T10:59:34.5256052-04:00″
sourceHealthServiceId=”B3B5A38D-0DBE-5CA9-592D-B76333A989D8″>
<Property Name=”State” VariantType=”8″>GOOD</Property></DataItem>
Ok now we have a good working script and c# executable. Now we just need to put the script into a monitor and copy the file to the server we want to monitor and we are done. You can follow these directions if you don’t know how to put the script into a monitor. https://www.scom2k7.com/create-a-script-based-unit-monitor-in-opsmgr2007-via-the-gui/
So now think of the possibilities. Anything that can be called from C# can now be monitored in SCOM. SDKs, APIs, Web Services, are all easily leveraged in C#. The only downside is you need the executable on the server the monitor is running on, but that could be fixed by having the script to check for the executable and if it wasn’t there you could copy it from a centralized network location.
the vbscript you used was filtering the result on the client side. of cause it is going to be slow. and the code in C# uses server side filtering, that’s why it’s much quicker.
Not true. I am copying the C# application down to the client that I running it on. They both are running on the client. The reason it is so much quicker is that in the vbscirpt I am touching every file. In the c# program I am just hitting the directory and using the System.IO.Directory.GetFiles method on the directory. This is much more efficient as it just hits the directory. Vbscrpt doesn’t have this method, but the real point was to show how to you can write c# apps and leverage them in SCOM.
hey tim – great blog & info. i don’t have a directory with enough files to process but was curious if you would have achieved better results digging through with wmi – such as:
sComputer = “.”
Set oWMIService = GetObject(“winmgmts:” _
& “{impersonationLevel=impersonate}!\\” & sComputer & “\root\cimv2”)
Set cFiles = oWMIService.ExecQuery(“Select * from CIM_DataFile where Drive = ‘c:’ and Path = ‘\\temp\\’ and Extension = ‘xml'”)
i = 0
For Each oFile in cFiles
Wscript.Echo oFile.Name
i = i + 1
Next
WScript.Echo “Count: ” & i
I would bet that will work as well. I figured there was a way to do it in VB as well. Good Stuff
Thanks
I would bet it is much easier to use powershell
[int]([object[]](dir *.xml)).length
Nice work. I prefer C# as everything is exposed and only requires the .net framework which is installed more often then powershell on our servers.
Hey Tim,
This is really helpful. I am looking to go an extra mile.
I want to do something similar like:
1. Instead of returning filecount from C# application, can i return an object to VbScript and utilize the same object into VbScript code.
2. If a list of objects is returned, what changes will be there in C# and VbScript.
3. I tried below code as well:
MOMScriptAPI MA = new MOMScriptAPI();
dynamic d = MA.CreatePropertyBag();
d.AddValue(“Key”, “Value”);
MA.Return(d);
But how to place this code in C# application and how to return the MOMScriptAPI object to SCOM.
A quick help is appreciated.
Tim,
I was doing the things same way but want to return a list of objects instead of a file.
Can you please help on this. I want to return data after connecting to a device and getting some device configuration and now i have to upload it to SCOM.
Thanks,
Mukul
There is no way to move an object from C# to vbscript. You can use c# to write a native module (this is difficult) or just write to a text file. Then have vbscript read the text file.
Ok, thanks much.
I already have the management pack which has around 50 VbScript associated with different purpose i.e. discovery, creating cache, etc.
I want to remove all these VBscript code from the management pack. I want to develop an agent for my device which is not a windows or unix box.
The agent will do all the tasks like Discovery, Fault Gathering, Event collection etc from device. VbScript is doing the task right now.
I want to migrate them to C# and develop some agent to do this. Management pack will have class defination.
Please help how to do this. Which managed module will help to do this or how to migrate this task to agent.
A help is highly appreciated as the numbers of scripts queued for execution is 50 right now and they run at regular interval of 5 to 15 minutes. But in my enterprise network where there can be 100 to 1000 devices, it will become problem for SCOM management server.
If i develop some agent or managed module, I will be able to reduce the workload by utilizing the threading capabilites of C#.
Thanks,
Mukul