.NET Runtime Path of Other Win32 Services? - service

var machineservices = ServiceController.GetServices( Environment.MachineName ).ToList();
I am able to enumerate the Windows Services installed with that code. However, I need to get the full executable path of each Service, and cannot seem to find the "Path" property. Can anyone clue me in?
Using C#, .NET 4.5.2.
Thanks.

ServiceController does not provide that information directly. To access that you need to use the registry or preferably WMI using a ManagementObjectSearcher selecting for Win32_Service entities.
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service");
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject obj in collection) {
string pathName = obj["PathName"] as string;
}

Related

Get installed software product context using powershell

I can easily get all installed software products on a machine using
Get-WmiObject -Class Win32_Product
Now I'd like to also fetch the Product Context. How can I access this information for every installed product using PowerShell.
In VB I did that by using the WindowsInstaller COM-Object and then querying the information. In essence this:
Set Com = CreateObject('WindowsInstaller.Installer')
Set Products = Com.ProductsEx(vbNullString,"S-1-1-0",7)
For Each P in Products
context = P.Context
Which I dont not manage to replicate in PowerShell
I realize this question is a bit stale, but I disagree with what seems to be the prevailing notion that working with Windows Installer in PowerShell is somehow a "pain" and more complicated than working with it in VBScript (this post is just one of many).
I have found that VBScript Windows Installer code translates quite literally to PowerShell, which means there are numerous examples of VBScript Windows Installer scripts that can be adapted to PowerShell and used to learn how to work with Windows Installer in PowerShell.
For this specific question of install context, the PowerShell code is quite similar to the VB code the OP gave.
# code must be run with admin rights to use "S-1-1-0" SID
enum InstallContext {
FirstVisible = 0 # product visible to the current user
None = 0 # Invalid context for a product
UserManaged = 1 # user managed install context
UserUnmanaged = 2 # user non-managed context
Machine = 4 # per-machine context
All = 7 # All contexts. OR of all valid values
AllUserManaged = 8 # all user-managed contexts
}
$Installer = New-Object -ComObject WindowsInstaller.Installer
foreach ($P in $Installer.ProductsEx("", "S-1-1-0", 7)) {
[InstallContext]$P.Context()
}
NOTE: I used Enums (about Enum - PowerShell | Microsoft Docs) with PowerShell here since tagMSIINSTALLCONTEXT is an enum in the msi.h file.
It's a pain to use that com object in powershell. I would use vbscript instead and save the text output to a powershell variable, or find an msi powershell module. That com object doesn't have a "type library" or support "IDispatch". The Windows Powershell in Action appendix for 2nd edition goes into it, but even there it's not pretty. That vbscript code has errors.

InternetExplorer.Application com object and windows 2012 in powershell

I am trying to access the document of an internet explorer com object with windows 2012. The code works great in windows 2008 but as soon as I try to run it on windows 2012 (fresh install, tried on more than one server), the same code stops working. In other words, $ie.document.documentHtml returns as null.
Below is the code:
$ie = new-object -com "InternetExplorer.Application"
$ie.navigate2("http://www.example.com/")
while($ie.busy) {start-sleep 1}
$ie.document.documentHtml.innerhtml
Has the interexplorer com object changed in windows 2012? and if yes, how do I do I retrieve the document contents in windows 2012?
Thanks in advance
edit: Added a bounty to sweeten things up. Invoke-WebRequest is nice but it works only on windows 2012 but I need to use internet explorer and have it work both on windows 2008 and windows 2012. I have read somewhere that installing microsoft office solves the issue. It is not an option either.
edit2: as I need to remotely invoke the script on multiple windows server (both 2008 and 2012), I would prefer not to copy files manually
It's a know bug: http://connect.microsoft.com/PowerShell/feedback/details/764756/powershell-v3-internetexplorer-application-issue
An extract from the workaround:
So, here's a workaround:
Copy Microsoft.html.dll from a location where it is installed (eg: from C:\Program Files(x86)\Microsoft.NET\Primary Interop Assemblies to your script's location (can be a network drive)
Use the Load-Assembly.ps1 script (code provided below and at: http://sdrv.ms/U6j7Wn) to load the assembly types in memory
eg: .\Load-Assembly.ps1 -Path .\microsoft.mshtml.dll
Then proceed as usual to create the IE object etc. Warning: when dealing with the write() and writeln() methods use the backward compatible methods: IHTMLDocument2_write() and IHTMLDocument2_writeln().
$ie.document.documentHtml.innerhtml
The bigger question is how this ever could have worked. The Document property returns a reference to the IHTMLDocument interface, it does not have a "documentHtml" property. It is never that clear what you might get back when you use late binding as was done in this code. There is an old documentHtml property supported by the DHTML Editing control, that has been firmly put to the pasture. Admittedly rather a wild guess.
Anyhoo, correct syntax is to use, say, the body property:
$ie = new-object -com "InternetExplorer.Application"
$ie.navigate2("http://www.example.com/")
while($ie.busy) {start-sleep 1}
$txt = $ie.document.body.innerhtml
Write-Output $txt
If you still have problems, Powershell does treat null references rather undiagnosably, then try running this C# code on the machine. Ought to give you a better message:
using System;
class Program {
static void Main(string[] args) {
try {
var comType = Type.GetTypeFromProgID("InternetExplorer.Application");
dynamic browser = Activator.CreateInstance(comType);
browser.Navigate2("http://example.com");
while (browser.Busy) System.Threading.Thread.Sleep(1);
dynamic doc = browser.Document;
Console.WriteLine(doc.Body.InnerHtml);
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
}
As far as I can tell, on Windows Server 2012 to get the full html of a page:
$ie.document.documentElement.outerhtml
There is also an innerhtml property on the documentElement, which strips off the root <html> element.
Of course, if all you want to do is get the raw markup, consider using Invoke-WebRequest:
$doc = Invoke-WebRequest 'http://www.example.com'
$doc.Content
Get any PC with Office installed and copy Microsoft.mshtml.dll to your script location.
c:\program files (x86)\Microsoft.net\primary interop assemblies\Microsoft.mshtml.dll
add-Type -Path Microsoft.mshtml.dll
Script works.

How do I get a list of all tfs alert subscriptions via powershell (get .Net assembly via PowerShell?)

I'm trying to move my tools to powershell, can this be done in PowerShell? the bit I'm really interested in is:
IEventService es = tfs.GetService(typeof(IEventService)) as IEventService;
List<Subscription> ls = es.GetAllEventSubscriptions().ToList();
Edit: what I really want to do might be using a .NET assembly from powershell and this might then be a duplicate of Using .NET library from PowerShell
Here is a TFS API in PowerShell function that I found on a blog long ago that will get you started. I've posted it to a GitHub Gist. Basically you ensure you've loaded up the TFS assemblies into the AppDomain and then you can add any TFS Service Interfaces you want to the object and just operate on them just as you would in any c# application, etc.
https://gist.github.com/3288447
Once you have the TFS object returned from the method in the Gist above, you can operate on the loaded services like so:
#use work item service
$tfs = get-tfs $env:TFSSERVERURL -silent
$project = $tfs.wit.Projects | ?{ $_.Name -eq $projectName}
#todo - replace with text for query or file read -- this is deprecated
$query = $project.StoredQueries | ?{ $_.Name -eq 'Active Bugs' }
$queryText = $query.QueryText.Replace("#project","'$projectName'")
$results = $tfs.wit.Query($queryText)
#do something with the results...
In your request above you can just alter the get-tfs method to add your service interface to the set loaded and then operate on the .NET methods much like I do in the example above.

IIS6 List the version of .net an application pool is running

I want to enumerate the application pools running on a server. In IIS7 i am able to pull up the .net version via WMI WIn32_Process but it is not there in IIS6. How can I get the version of .net that a worker process/application pool is running in?
For instance:
DefaultAppPool v2.0.50727
In IIS6 .Net is set in the Virtual Directories under the ASP.NET tab and the Application Pool is setup under the Virtual Directory tab. FYI: I'm running Windows 2003 SP2 IIS6.
The problem with IIS6 Application Pools is that unlike IIS7 they have no knowledge of what version of the .NET Framework is being loaded into them.
IIS6 websites determine which .NET Framework runtime is loaded into a pool by the version of ASP.NET that is pointed to by a site or sub application's script maps. A worker process will just blindly load the requisite ISAPI DLL that is mapped to an extension (or whatever is defined in the wildcard mapping).
This old method is often the cause of much grief where two different sites allocated to the same pool may be configured to run different versions of ASP.NET and you get the infamous:
...and the following event is logged to the Windows Application Log:
Event Type: Error
Event Source: ASP.NET 2.0.50727.0
Event Category: None
Event ID: 1062
Date: 12/01/2011
Time: 12:31:43
User: N/A
Computer: KK-DEBUG
Description:
It is not possible to run two different versions of ASP.NET in the same
IIS process. Please use the IIS Administration Tool to reconfigure your
server to run the application in a separate process.
The only way to determine what .NET version an application pool is configured to run is to walk every site that is assigned that pool and check the raw scriptmaps.
The only problem with this is where (for example), for whatever reason, you have a misconfigured site (or sub application) that is no longer in use that is setup to use a different version of ASP.NET, e.g.:
Site .NET Version Application Pool
============================================================
WebSite1 ASP.NET 4.0 AppPool1
WebSite2 (no longer used) ASP.NET 2.0 AppPool1
In this case you have to decide which site takes precedence to determine the framework version.
This PowerShell script might help you ascertain what ASP.NET version is being used in each pool:
# Walk sites
$allsites = ([adsi]"IIS://Localhost/W3SVC").children | where { $_.SchemaClassName -eq "IIsWebServer" }
$pools = #()
foreach($site in $allsites)
{
$path = "IIS://Localhost/W3SVC/" + $site.Name + "/root"
$siteRoot = [adsi]$path
$sitePool = $siteRoot.AppPoolId
$aspx = $siteRoot.ScriptMaps | where { $_.StartsWith(".aspx") }
if( $aspx.Contains("v1.1")) {
$runtime = "1.1"
} elseif ($aspx.Contains("v2.0")) {
$runtime = "2.0"
} elseif( $aspx.Contains("v4.0")) {
$runtime = "4.0"
} else {
$runtime = "Unknown"
}
$v = #{AppPool = $siteRoot.AppPoolId; RunTime = $runtime; SiteId = $site.Name}
$pools += $v
}
$pools | Sort-Object { $_.AppPool } | % { Write-Host $_.AppPool $_.SiteId $_.RunTime }
It only walks the sites at the root level and doesn't recursively walk into each one to identify sub applications.
I think you can execute the following statements:
$computer = "LocalHost"
$namespace = "root\MicrosoftIISv2"
Get-WmiObject -class IIsApplicationPoolSetting -computername $computer -namespace $namespace
and use the ManagedRuntimeVersion property in the returned object.
Edit: As Andy said in comment below, this doesn't work. However, if you are on WS2003 you might try this and parse the result returned:
C:\windows\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe -lk
Knowing on what OS you are running would be nice.

Use PowerShell to Create a List of All Projects on a CCNet Server

I have been tasked with auditing build servers using CruiseControl.NET. There are quite a few of them, and I was wondering if there was a way to programmatically / automagically compile a list of the projects on each of them.
Is there a way to do this?
My first instinct is to use PowerShell (v2.0), but I am not sure how to begin writing a script that does what I require. Should I be using PowerShell, or some other method? What would those other methods be?
I would use the Thoughtworks.CruiseControl.Remote.dll and load it into powershell
Create ICruiseManagerFactory managerFactory;
and then you could iterate through a list of servers you have and Create the Uri for the server like:
ServerUri = #"tcp://" + Server + ":" + Port + #"/CruiseManager.rem"
then get the list of Projects and statuses from that server using:
ProjectStatus[] currentStatuses = managerFactory.GetCruiseManager(ServerUri).GetProjectStatus();
then iterate through the list:
foreach (ProjectStatus projectStatus in currentStatuses)
{
string name = projectStatus.Name;
string status = projectStatus.Status;
}
Powershell can read xml files really easily. You should use this to interrogate the CC.NET builds.
Start here and here
If you can access the ccnet.config files, you can just:
([xml](Get-Content ccnet.config)).cruisecontrol.project | Select name, artifactDirectory # or whatever
Thanks to everyone for their answers. :-) One thing I failed to mention is that all of the build servers have the same installation structure for CruiseControl.NET on them, and the installation directory is directly accessible through a folder share.
Putting it all together, here is my script for creating a list of projects on a particular build server:
function Get-Projects
{
param([string]$BuildServer = $(Throw "You must specify the name of the build server containing the projects you want to list"))
$BuildServer = $BuildServer.ToUpper()
$ConfigFilePath = [string]"\\$BuildServer\CruiseControl.NET\server\ccnet.config"
$ValidPath = Test-Path -Path "$ConfigFilePath"
if (!$ValidPath)
{
$InvalidPathErrorMessage = [string]"Path $ConfigFilePath does not exist!"
Write-Host $InvalidPathErrorMessage
$InvalidPathErrorMessage
return
}
$ConfigXml = [xml](Get-Content $ConfigFilePath)
$Projects = #($ConfigXml.SelectNodes("cruisecontrol/project"))
if (!$Projects)
{
$ErrorMessage = [string]"No projects on $BuildServer!"
Write-Host $ErrorMessage
$ErrorMessage
return
}
$Projects
}
Then, assuming this script is accessible within your PowerShell session, you can simply select the data you want, as Jaykul suggested:
Get-Projects <BuildServer> | Select-Object name, queue, category | Sort-Object category
I hope this is found to be helpful!