I have a really simple Powershell query:
Get-WmiObject -query 'ASSOCIATORS OF {Win32_LogicalDisk.DeviceID="C:"} WHERE AssocClass = Win32_LogicalDiskToPartition'
On a Windows 7 (64-bit) machine, running this in a Powershell correctly enumerates a single management object. However if I run the same query in an elevated Powershell I get a long pause and then no results.
I find a similar problem when trying to execute the WMI query in code (which is what I am actually trying to do) - when my program runs without elevation the code works, when it runs with elevation no results are returned. This is the simplest version of my code that shows this problem:
static void Main(string[] args)
{
var query = "ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"C:\"} WHERE AssocClass = Win32_LogicalDiskToPartition";
var searcher = new ManagementObjectSearcher(query);
foreach (var o in searcher.Get())
{
Console.WriteLine(o);
}
Console.WriteLine("DONE");
Console.ReadLine();
}
Why does this happen ? More importantly is there anyway I can ensure that this query will execute correctly when run elevated - as the final program will need to run elevated for other reasons.
I think I found the culprit - I have an encrypted drive mounted using TrueCrypt. When I dismount that drive the enumeration works correctly, when I mount it again the problem re-appears.
My best guess is that WMI is hitting a problem because the encrypted drive has no partitions - though why it works when not running elevated is another thing entirely.
Related
Microsoft points you to use PowerShell to manage and query VMM. There is so many examples out there for using PS within c#, none point out what is the latest or for what version of PS the example code is for. We are using what we found to work with VMM from our code, but it seems to be VERY poor when it comes to memory consumption. Doing a simple list of VMs in a cloud causes the app to grow almost 300mb! And it just sits there, sometimes grows to even more. We are making all the proper dispose and object clean up as much as we can. I just believe that using PS from within c# does not seem to be a very good approach. I feel if there where a SDK with API calls it would use much less ram.
With the current version of PS released today, 4.0, what is the most efficient way of calling PS commands from within C#? If you want a sample PS command we call as an example, you can use the below: This lists all VMs within a cloud.
Get-SCVMMServer -ComputerName 'vmmserver' -Credential $cred | Get-SCCloud -Name 'cloud name' | Get-VM
I am currently wrestling with the exact same memory issue. However, I think I have a solution -- currently in the mists of putting it through its paces right now so I am not sure if the posted solution is the best or final solution. My environment is Windows 2012 R2 Datacenter and SCVMM 2012 R2.
Here are my findings
Given the following script running in a loop or just being called over-and-over
If (-Not(Get-Module -Name VirtualMachineManager)) {
Import-Module -Name VirtualMachineManager
}
[Hashtable]$return = #{}
$vmm = Get-VMMServer -ComputerName $vmmHostname
If ($vmm -eq $null) {
$return.Code = 1
$return.Text = "VMM server '$vmmHostname' cannot be accessed."
$return
Exit
}
Get-SCVirtualMachine -Name $hostname
I see the following
Memory consumption in the client system can continue to grow without ever releasing memory. (Even after the current script finishes executing.)
The number of established network connections to the VMM server (observed by running netstat -an on the VMM server) also continues to go up.
Eventually, new connections take a very long time to established (30-60 seconds) or they just stop altogether.
However, after all my research and testing, I see that the following script works fine and both keeps memory consumption low and the number of network connections small (1 or 2). (As a matter of fact, the connection is established, used, and cleaned up fast enough that netstat usually does not even show it in the list.)
If (-Not(Get-Module -Name VirtualMachineManager)) {
Import-Module -Name VirtualMachineManager
}
Get-SCVirtualMachine -VMM $vmmHostname -Name $hostname
The key, for some unknown reason at this point, seems to be specifying the VMM server on the individual cmdlets as opposed to establishing the connection at the top of the script.
As far as executing the script from C# goes, my Framework 4.5.1 code currently looks like this
using (var rs = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace()) {
rs.Open();
using (var ps = PowerShell.Create()) {
ps.Runspace = rs;
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var scriptName = "LabManagement.Scripts.GetVirtualMachineStatus.ps1";
using (var s = assembly.GetManifestResourceStream(scriptName)) {
using (var reader = new System.IO.StreamReader(s)) {
var script = reader.ReadToEnd();
ps.AddScript(script);
}
}
ps.Runspace.SessionStateProxy.SetVariable("vmmHostname", this.ServerName);
ps.Runspace.SessionStateProxy.SetVariable("hostname", hostname);
var output = ps.Invoke();
...
// process output and errors
...
rs.Close();
}
else {
rs.Close();
}
}
}
However, I am not sure if I need to explicitly create/open/close the Runspace object. Originally I was just creating the PowerShell object. (Since a PS script ultimately runs in a RunSpace, part of my memory consumption hunt was testing if not explicitly creating the RunSpace caused it to "stick around" after the PowerShell object was cleaned up.) As a follow up I will be looking to see if this affects code after I apply the cmdlet parameter fix to all my scripts.
Anyone else had to determine who the currently logged on user is remotely in a Windows 7 environment? I am using .NET 4.0 and C#. The environment is mixed XP and 7.
WMI queries involving sessions result in all active sessions, but not the session that is interactive.
UserName from ComputerSystem (WMI) returns null exception if user is connected via Remote Desktop, which is common enough that this method cannot be used.
PsLoggedOn takes too long for my purposes (yes, 300 ms is too long) and is surprisingly not accurate 100% of the time
Using p/invoke for WTSGetActiveConsoleSessionID or LsaEnumerateLogonSessions is too complicated and prone to memory leaks (from what I've read)
tasklist /S <computername> will return information for XP systems, but Windows 7 won't be agreeable thanks to that lovely UAC.
HKCU (win registry) is inaccessible remotely due to permissions restrictions, HKU is accessible, but Volatile Environment doesn't appear to have a tag for "active"
So far, the most reliable way is using PsExec to remotely execute qwinsta from the command line and traverse the output to text remotely. This is annoying and takes time (more than PsLoggedOn), but I'm running out of ideas here for reliability. Reliability before speed, but speed is very important in terms of cost benefit.
Third party tools are not an option, has to be a script, preferably WMI and C#. I delved into hitting the DC using Principal objects, but I'm afraid I might have confused myself more. Also, all user accounts are administrators.
I've done a lot of research over Google, but I am thinking that maybe I am looking in the wrong place.
Any takers?
you can achieve this by browsing Win32_ComputerSystem class's UserName Propperty :
ConnectionOptions con = new ConnectionOptions();
con.Username = "Administrator";
con.Password = "********";
ManagementScope scope = new ManagementScope(#"\\" + strIPAddress + #"\root\cimv2", con);
scope.Connect();
//check for scope.IsConnected, then process
ManagementObjectSearcher searcher =new ManagementObjectSearcher(#"\\" + strIPAddress + #"\root\cimv2", "SELECT * FROM Win32_ComputerSystem");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("-----------------------------------");
Console.WriteLine("Win32_ComputerSystem instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("UserName: {0}", queryObj["UserName"]);
}
I need to get installed software list under restricted user.
I use this code:
string fullString = string.Format("EXPORT \"{0}\\{1}\" \"{2}\" /y", hiveString, keyPath, Path.GetTempFileName());
Log(fullString);
var p = Process.Start(new ProcessStartInfo("reg.exe", fullString) {RedirectStandardOutput = true, UseShellExecute = false,WorkingDirectory = Directory.GetCurrentDirectory()});
Log("Output: " + p.StandardOutput.ReadToEnd());
p.WaitForExit();
On my dev machine I see normal output:
operation completed successfully
No matter what account I use - admin or restricted user.
Then I ran this app on Windows XP under restricted user. And see next in log:
"Output: "
Empty line, yes.
When I run similiar query in cmd - it works fine. I can not understand, what I'm doing wrong.
Why doesn't reg.exe write anything?
You can't call reg.exe without admin privileges. At least not unless you are on Windows Millennium (that's why it doesn't even appear on MSFT Web Site). Imagine what one could do if it were possible...
Is there any way (in C#, using WMI classes) to find out that how many times a particular software has been installed and uninstalled?
I want to run it on remote computer. I am getting software list by following code:
ManagementScope scope = new ManagementScope(#"\\" + ipAddress + #"\root\cimv2");
ObjectQuery query = new ObjectQuery("Select * from Win32_Product");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
Console.Write(m["Caption"]+"\t");
Console.WriteLine(m["installDate"]);
}
Normally not.
When a program will be uninstalled every bit of the program should be removed from the machine like it was never their. Unfortunately nearly every program doesn't make a perfect job at this point leaving some artifacts on the machine.
Nevertheless the desired behavior is that after a uninstall everything is gone (including some kind of counter) so that it is only possible to check if a program is currently installed or not.
On the other site nothing permits a program to save somewhere some counter (e.g. registry) which will increased everytime a installation is started, but that's something specific for each program and no common mechanism exists where this counter should reside.
i have copied the exe file and it was no problem, useing the following code, but now i want to run it, can anyboyd help me on this.
NOTE: i have the access to servers through remote desktop, but cant do this manually, coz there are dozens of them, cant get a program running on it like psex or whatever.
WindowsIdentity wi = new WindowsIdentity(token);
//Next I set the WindowsImportsonationContext
WindowsImpersonationContext impctx = wi.Impersonate();
System.IO.File.Copy("C:\\output.html", "\\\\PW42\\c$\\output1.html", true);
System.Diagnostics.Process p = new System.Diagnostics.Process();
try
{
System.Diagnostics.Process.Start(#"\\PW42\c$\txt.bat");
//runFile();
}
catch
{
Console.WriteLine("error");
}
Depending on what access you have on the server you can use a program like psexec or using WMI to launch the file remotely.
A sample psexec command would be
psexec \\computername -u remoteusername filepath(on remote computer) arguments
Psexec can copy the file beforehand if requested and can run against a list of computers instead (replacing \\computername with #computer-list.txt). With WMI you need to connect to the Win32_Process class and Create a new object to start it. The second post in this thread could work.
Unfortunately both of these options require multiple firewall rules (like RPC and WMI) to be available from the running workstation. If your company only has RDP access enabled through the firewall, neither of these will probably work.