How can I detect a Windows service at project build - windows

I'm working on a solution that contains a Windows Service and a WinForms client that interacts with that service.
In my pre-build and post-build events, I have some net start and net stop commands to start and stop the service, but there are times when this causes a problem (file's not found, service is already stopped, etc.).
Is there a way to test if a service is running or installed prior to issuing net start?
I'd like to put this test in .cmd file and run it in the pre-build event for the project.

You actually don't need to install, start and stop service every time. Instead, consider adding a command-line key to your service executable so that when it's specified, is runs as a service (that is, performs usual ServiceBase.Run() stuff), and when this key is absent it runs as a regular console application. You'll get an added benefit of being able to dump logger output directly to the console so that debugging will be a whole lot easier.
if(args.GetLength(0) == 1 && args[0].ToUpper() == "/SERVICE")
{
ServiceBase[] services = new ServiceBase[] { new MyService() };
ServiceBase.Run(services);
} // if
else
{
InitAndStartWhateverIsNecessaryToRunServer();
Console.ReadLine();
} // else

Stick this into a vb script file and add to the pre and post build events.
strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colRunningServices = objWMIService.ExecQuery _
("SELECT * FROM Win32_Service WHERE Name = 'someService'")
Set objService = colRunningServices(0)
If objService.State <> "Running" And objService.State <> "Starting" Then
objService.StartService()
End If

Related

How to run WMI commands on non-domain-joined server from script running using domain admin credentials

I have a windows script running from a scheduled task, set to run with domain credentials.
It checks the disk space on all of my domain joined servers using WMI.
Set wmi = GetObject("winmgmts:\\" & hostname & "\root\cimv2")
...
Set wmiresults = wmi.ExecQuery("SELECT * FROM Win32_LogicalDisk WHERE Name = '" & _
UCase(diskletter) & ":'")
For Each wmiresult In wmiresults
ptotalspace = Round(CDbl(wmiresult.Size) / 1073741824, 2)
pfreespace = Round(CDbl(wmiresult.Freespace) / 1073741824, 2)
Next
This script works fine for all domain joined servers. But I need to include a non-domain-joined server into this checking, and it fails as the script lacks the necessary permissions on the target server.
I have tried the hacky method of creating a local user account with the same name and password as the domain admin acct, and adding it to local admins, but this didn't work.
Is there a way to allow the script permission to check the server's disks without joining it to the domain, and without having to create a duplicate or version of this script that runs using a local account with permission to perform this check?
The solution is here: Connecting to WMI Remotely with VBScript
Look at option 2 under the heading "To connect to a remote system using VBScript". You can use the SWbemLocator.ConnectServer method to pass different credentials to the connection. It looks like this:
strComputer = "Computer_B"
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set objSWbemServices = objSWbemLocator.ConnectServer(strComputer, _
"Root\CIMv2", _
"fabrikam\administrator", _
"password")
Just replace "fabrikam\administrator" and "password" with a username and password that has permission on that machine. Then you can use that objSWbemServices object the same way you were using your wmi object.
That should work if you run it from a machine inside the domain too... I think. Give it a try.

SendKeys doesn't work from background task

Initial Problem:
I use an external keyboard at the office, so I want the NumLock ON. But when I'm at home I just use the laptop keyboard, so then I get numbers instead of letters and I have to turn NumLock OFF.
Initial Solution:
The below script detects one or two keyboards and turns NumLock ON or OFF as appropriate.
New Problem:
This works perfectly from the command line, but I want it to trigger when I log in and happen automatically. When I run it from Task Scheduler in the background, this line doesn't work:
Shell.SendKeys "{NUMLOCK}"
It fires but doesn't toggle the lock. No errors reported.
UPDATE: If I schedule it to run under my account "only when user is logged on" then it works but shows the cmd window. If I run it under my account or under the SYSTEM account with "whether user is logged in or not" the window goes away nicely but it doesn't work.
Whether from cmd or run as a scheduled task, I get this output when it should toggle the lock:
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.
Found HID Keyboard Device
Found HID Keyboard Device
numLock is OFF
Toggling Numlock
So the script itself is working correctly.
UPDATE2: Looks like it might have something to do with not having a windows station when running as a background task. Turns out that DetectNumlockConsole.exe isn't working either. That is a simple c# app that returns the results of this line
numLock = Control.IsKeyLocked(Keys.NumLock);
Again, this works when run "only when user is logged on" but not "whether user is logged in or not."
--------- vbs script -----------
set OUT = WScript.StdOut
Set Shell=CreateObject("Wscript.Shell")
Dim KeyCount
KeyCount = 0
Computer = "."
'set NumLock = CheckState
Set WMIService = GetObject("winmgmts:\\" & Computer & "\root\cimv2")
Set Devices = WMIService.ExecQuery ("Select * From Win32_USBControllerDevice")
For Each Device in Devices
DeviceName = Device.Dependent
Quotes = Chr(34)
DeviceName = Replace(DeviceName, Quotes, "")
DeviceNames = Split(DeviceName, "=")
DeviceName = DeviceNames(1)
Set USBDevices = WMIService.ExecQuery ("Select * From Win32_PnPEntity Where DeviceID = '" & DeviceName & "'")
For Each USBDevice in USBDevices
'OUT.WriteLine USBDevice.Description ' Write description to command line to see what to look for
If InStr( LCase( USBDevice.Description ), "keyboard" ) <> 0 Then
KeyCount = KeyCount + 1
OUT.WriteLine "Found " & USBDevice.Description
End If
Next
Next
dim numLock
numLock = Shell.Run("DetectNumlockConsole.exe",0,True)
If (numLock = 0) Then
OUT.WriteLine "numLock is OFF"
Else
OUT.WriteLine "numLock is ON"
End If
' If we have a keyboard, and numlock is OFF
' Or we don't have a keyboard, and numlock is ON
' Then toggle it
If (((KeyCount > 1) AND (numLock = 0)) OR ((KeyCount = 1) AND (numLock = 1))) Then
Shell.SendKeys "{NUMLOCK}" ' *** Problem here, doesn't toggle **
OUT.WriteLine "Toggling Numlock"
End If
That is how windows security works. It is nothing to do with sendkeys per se, but tasks under different security contexts cannot affect other tasks.
As you can see it works when run under the same security context as only run when user logged in does.
It's called process isolation and the principal is that no one can mess with the interactive user for both security and UI principals.

Getting errors when checking framentation status

I am trying to execute the following script on Win7 (x64) to check if any volumes need to be defragmented.
Set VolumeList = GetObject("winmgmts:").ExecQuery("Select * from Win32_Volume")
For Each objVolume in VolumeList
errResult = objVolume.DefragAnalysis(blnRecommended, objReport)
If errResult = 0 then
Wscript.Echo "Used space: " & objReport.UsedSpace
Wscript.Echo "Volume name: " & objReport.VolumeName
Wscript.Echo "Volume size: " & objReport.VolumeSize
If blnRecommended = True Then
Wscript.Echo "This volume should be defragged."
Else
Wscript.Echo "This volume does not need to be defragged."
End If
Wscript.Echo
Else
MsgBox errResult
End If
Next
I have tried to run this script on two different Win7 systems.
On the first, I get an OUT OF MEMORY error on GetObject("winmgmts:").ExecQuery("Select * from Win32_Volume").
On the second, I get no OUT OF MEMORY error on GetObject, but I get error 11 (Unknown Error) in errResult (output of DefragAnalysis-method).
Both Win7 systems have been installed and configured in the same way.
Perhaps this is not important, but when I check the WMI properties, it says "Connected to <Local Computer>" and not (as in Win XP) "SUCCESSFULLY connected to <Local Computer>".
Code works just fine for me, but perhaps it'll help when you explicitly connect to the right namespace:
Set wmi = GetObject("winmgmts://./root/cimv2")
Set VolumeList = wmi.ExecQuery("SELECT * FROM Win32_Volume")
Also I'd recommend restricting the query to just local disks that have a drive letter assigned to them:
SELECT * FROM Win32_Volume WHERE DriveType = 3 AND DriveLetter IS NOT NULL
Use WBEMTest or WMIDiag to check if your WMI connection is working at all. Check the Application and System eventlogs for errors and warnings, too.
The reason for the error 11 was that the script was not run with elevated privileges. Once it was run as administrator, it worked fine. Thanks

Reset password for renamed Administrator account

I need to create a .VBS script to reset the Windows local administrator password on a large group of computers. My problem is that some of our sites have renamed the administrator account for security reasons. Does anyone have a script which changes the password of the administrator account based on the SID of the original Administrator account?
Using the fact that local admin's SID always ends with -500:
strComputer="." ' local computer by default
Set objUser=GetObject("WinNT://" & strComputer & "/" & GetAdminName & ",user")
objUser.SetPassword "New local admin password"
objUser.SetInfo
Function GetAdminName
'This function was written using information from Table J.1 from the Windows XP resource Kit
'http://www.microsoft.com/resources/documentation/Windows/XP/all/reskit/en-us/Default.asp?url=/resources/documentation/Windows/XP/all/reskit/en-us/prnc_sid_cids.asp
Set objNetwork = CreateObject("Wscript.Network") 'get the current computer name
objComputerName = objNetwork.ComputerName
Set objwmi = GetObject("winmgmts:{impersonationLevel=impersonate}!//" & objComputerName)
qry = "SELECT * FROM Win32_Account where Domain = '" & cstr(objComputerName) & "'"
'set query, making sure to only look at local computer
For Each Admin in objwmi.ExecQuery(qry)
if (left(admin.sid, 6) = "S-1-5-" and right(admin.sid,4) = "-500") then 'look for admin sid
GetAdminName = admin.name
end if
next
end Function
There's a tool floating around somewhere called LookupAccountName (with source!) that given the SID of the builtin adminitrator will give you its name.
You're probably going to end up writing C++ code to pull this one off reasonably well.
Like Joshua says, I don't think you can do this with windows scripting host only, you could use it download something and execute it:
A custom app that calls LookupAccountSid(S-1-5-domain-500 SID or enum admin group)+NetUserSetInfo to reset the password (Needs to run this as admin)
http://home.eunet.no/pnordahl/ntpasswd/ (Reset at boot)
Dump the SAM hashes and crack the password (Cain,John the Ripper,L0phtCrack etc)
#DmitryK's answer is good, and I didn't know any of that stuff. But I do know that this sort of thing is usually cleaner in PowerShell, so I ported it.
For example, the whole GetAdminName function can be written:
$adminName = (gwmi win32_account | ? { $.SID.StartsWith( 'S-1-5-' ) -and $.SID.EndsWith( '-500' ) }).Name
(Add the -ComputerName option to the gwmi call to do this on a server.)
The rest becomes:
$user = ([ADSI]"WinNT://$($env:COMPUTERNAME)/$adminName,User")
$user.SetPassword( 'xxx' )
$user.SetInfo()
(applying the appropriate computer name as needed, of course.)

End win32 process vbscript

I've got the following code to end a process, but I still receive an error code 2 (Access Denied).
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colProcessList = objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'MSSEARCH.exe'")
For each objProcess in colProcessList
wscript.echo objProcess.processid
intrc = objProcess.Terminate()
if intrc = 0 then wscript.echo "succesfully killed process" else wscript.echo "Could not kill process. Error code: " & intrc End if
It's quite legitimate to get "access denied" for ending a program. If it's a service (which I'm guessing mssearch.exe is), then it is probably running as the "SYSTEM" user, which has higher privileges than even the Administrator account.
You can't log on as the SYSTEM account, but you could probably write a service to manage other services...
As a non-privileged user, you can only end processes you own. In a multiuser environment this can bite you in the ankle, because WMI would return equally named processes from other users as well, unless you write a more specific WQL query.
If your process is a service, and your script runs under a privileged account, you may still need to take "the regular route" to stop it, for example using WScript.Shell to call net stop or sc.exe, or, more elegantly, using the Win32_Service class:
Set Services = objWMIService.ExecQuery _
("SELECT * FROM Win32_Service WHERE Name = '" & ServiceName & "'")
For Each Service In Services
Service.StopService()
WSCript.Sleep 2000 ' wait for the service to terminate '
Next
If you look on this page: http://msdn.microsoft.com/en-us/library/aa393907(VS.85).aspx you would see that error code 2 is access denied instead of file not found

Resources