I am not able to read specific registry values using PowerShell in C#. Here is the code:
Calling function:
public static string UserDisplayName()
{
// PowerShell Command: (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData\1').LoggedOnDisplayName
return GetPowerShellOutputString(#"(Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData\1').LoggedOnDisplayName");
}
Function definition:
private static string GetPowerShellOutputString(string PsCmd)
{
try
{
string PsOut = string.Empty;
Debug.Write("PsCmd: " + PsCmd + "; ");
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
Pipeline pipeline = rs.CreatePipeline();
pipeline.Commands.AddScript(PsCmd);
Collection<PSObject> results = pipeline.Invoke();
rs.Close();
foreach (PSObject obj in results)
if (obj != null) PsOut += obj.ToString() + ", ";
PsOut = (PsOut == string.Empty) ? strUnavailableString : PsOut.TrimEnd(',', ' ');
Debug.WriteLine("PsOut: " + PsOut);
return PsOut;
}
catch (Exception ex)
{
Debug.WriteLine("! " + ex.Message + ex.InnerException + "\n");
return strUnavailableString;
}
}
However, the same Function definition is working perfectly if I am trying to read any other registry value e.g.:
public static string UserOUPath()
{
try
{
if (UserDomain() == SystemInformation.ComputerName) return strUnavailableString; // Non-domain account
//For consistant performance, grab OU from registry instead of AD.
string userSID = WindowsIdentity.GetCurrent().User.ToString();
string ouPath = #"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\" + userSID;
string ou = GetPowerShellOutputString("(Get-ItemProperty -Path '" + ouPath + "').'Distinguished-Name'");
ou = ou.Remove(0, ou.IndexOf(",", 0) + 1); // Drop leading CN stuff
ou = ou.Remove(ou.IndexOf(",DC=", 0), ou.Length - ou.IndexOf(",DC=", 0)); // Drop trailing DC stuff
ou = ou.Replace(",OU=", "/");
ou = ou.Replace("OU=", "/");
ou = FlipOU(ou);
if (ou == null) throw new NullReferenceException();
return ou;
}
catch
{
return strUnavailableString;
}
}
For the first call (UserDisplayName()) when I did Debug mode, the results object is returning null. However if I run the same PowerShell Command in PowerShell window it is giving the value.
I am stumbled upon this as I am not able to get why and what is happening?
A couple of things. First I agree with all the comments about just using the Microsoft.Win32.Registry* classes directly in C#. However to answer your question about doing this from PowerShell, I believe what you are running into a registry virtualization issue. If your C# project is AnyCPU and run under Visual Studio it is likely running 32-bit and that registry path will be virtualized to the SysWow64 registry node. So your reg path won't exist. That can be fixed by making the exe compile as x64. Another option is to use the .NET Registry.OpenBaseKey() method will allows you to specify which reg hive you want to view (32-bit or 64-bit).
Second, you can simplify your code by using the PowerShell class e.g.:
var ps = PowerShell.Create();
ps.AddScript(PsCmd);
var results = ps.Invoke();
foreach (PSObject obj in results)
if (obj != null) PsOut += obj.ToString() + ", ";
Third, with the PowerShell class, after the Invoke() call, check the Error stream for errors like so:
foreach (var error in ps.Streams.Error)
Console.Error.WriteLine(error);
Related
Here is a part of a simple web page to show a selector list of the available log files on an attached drive:
public void OnGet()
{
StringBuilder logContent = new StringBuilder();
string[] files = null;
string directory = #"Q:\logs";
string reason = "Undefined";
try
{
files = Directory.GetFiles(directory, "*.csv");
}
catch (Exception ex)
{
reason = ex.Message;
}
if (files != null && files.Length > 0)
{
logContent.Append(#"<select>");
foreach (string file in files)
{
string aFile = file.Substring(file.LastIndexOf(Path.DirectorySeparatorChar) + 1);
logContent.Append("<option value=\"" + aFile + "\">" + aFile + "</option>");
}
logContent.Append(#"</select>");
...
else
{
logContent.Append("<h3>An Exception occurred: " + reason + "</h3>");
}
Message = logContent.ToString();
}
When the page runs, I get the exception
"Cannot find a part of the path Q:\logs.
The drive is there and if I do a dir Q:\logs in the command prompt from C drive it displays the contents.
What am I missing here?
What are you using IIS?
If so, then the user that is running the application (application pool in IIS) has to be you. Otherwise, you need to map the drive for that user, since it sounds like Q is a network mapped drive.
I have written a console application wherein I have called a powershell script from the console. In the powershell script I have written hello world as a return variable and it is running as expected but next time when I change the string from hello world to How are you it is not displaying the changed string. I cannot figure out myself what needs to be done to clear the pipeline or cache.
I have used the below namespace apart from default namespaces
using System.Management;
using System.Management.Automation;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
static void Main(string[] args)
{
string _str = string.Empty;
_str= RunScript(#"C:\Powershell_Scripts\Test.ps1");
Console.WriteLine("Input String is =" + str);
Console.Read();
}
private static string RunScript(string scriptText)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript("Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted");
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script
// output objects into nicely formatted strings
// remove this line to get the actual objects
// that the script returns. For example, the script
// "Get-Process" returns a collection
// of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
Collection <PSObject> results = pipeline.Invoke();
pipeline.Streams.ClearStreams();
// close the runspace
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
Powershell Script i.e. Test1.ps1
sleep 3
$a=""
$a = "Hello word"
return $a
Here is a sample how to use the PowerShell in a Runspace
using System;
using System.Collections.Generic;
using System.Text;
using System.Management;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create a runspace.
using (Runspace myRunSpace = RunspaceFactory.CreateRunspace())
{
myRunSpace.Open();
using (PowerShell powershell = PowerShell.Create())
{
// Create a pipeline with the Get-Command command.
powershell.AddScript("Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted");
powershell.AddScript(#"C:\Users\you\Desktop\a.ps1");
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects
powershell.AddCommand("Out-String");
// execute the script
var results = powershell.Invoke();
powershell.Streams.ClearStreams();
powershell.Commands.Clear();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
Console.WriteLine(stringBuilder.ToString());
}
}
}
}
}
Further reference: Creating a constrained runspace
Here is a code which runs as expected
enter code here
try
{
if (System.IO.File.Exists(#"c:\disk\op.txt") && (new FileInfo(#"c:\disk\op.txt").Length != 0))
{
System.IO.File.Move(#"c:\disk\op.txt", #"c:\disk\Previous_op_" + DateTime.Now.ToString("dd_MM_yyyy hh mm") + ".txt");
log.Info("Previous Output File Successfully Renamed");
}
// TODO: Add delete logic here
log.Info("Input ActionResult - Server " + Server);
log.Info("Input ActionResult - Volume " + Volume);
log.Info("Input ActionResult - Size " + size);
string userID = "dir\\" + Session["Uname"].ToString();
string userpassword = Session["Upwd"].ToString();
log.Info("username " + userID);
StringBuilder stringBuilder = new StringBuilder();
var con = new WSManConnectionInfo();
log.Info("Pushing username in PSCredential- " + userID.ToString().Trim());
con.Credential = new PSCredential(userID.ToString().Trim(), userpassword.ToString().Trim().ToSecureString());
Runspace runspace = RunspaceFactory.CreateRunspace(con);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript("Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted");
string _str = #"-Server " + Server + " -Volumeletter " + Volume + ": -deltasize " + size + " -Logfile c:\\disk\\op.txt -username " + userID + " -password " + userpassword;
log.Info("Parameter string format- " + _str.Substring(0, _str.IndexOf("-password") + 9));
pipeline.Commands.AddScript(#"C:\disk\diskerr.ps1 " + _str.ToString());
pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
pipeline.Commands.Add("Out-String");
var results = pipeline.Invoke();
runspace.Close();
runspace.Dispose();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
log.Info("Output from powershell: " + obj.ToString());
}
if (System.IO.File.Exists(#"c:\disk\op.txt") && (new FileInfo(#"c:\disk\op.txt").Length != 0))
{
fileStream = new FileStream(#"c:\test\op.txt", FileMode.Open, FileAccess.Read);
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
_consoleOutput = streamReader.ReadToEnd();
}
_output = Regex.Replace(_consoleOutput, #"\r\n?|\n", "<br />");
}
return Content(_output);
}
catch (Exception ex)
{
log.Info("Error stackTrace inside Input ActionResult " + ex.StackTrace.ToString());
log.Info("Error Message inside Input ActionResult " + ex.Message.ToString());
return View();
}
You need to change the directory path and the powershell file name.
So basically im running a script to receive the IP adress of computer, date and time an account is locked out at work and to do this im making a small gui so all the network technicians can use it, my code Currently goes
private void btnSearch_Click(object sender, EventArgs e)
{
var username = textBox1.Text;
using (var powerShellInstance = PowerShell.Create())
{
try
{
powerShellInstance.AddScript(
"Get-WinEvent -ComputerName COMPUTERNAMEHERE -FilterHashtable #{logname=LOGNAMEHERE;id=IDHERE;data=" +
username + "} |" +
"Select-Object -Property timecreated, " +
"#{label='computername';expression={$_.properties[6].value.Split(':')[3]}} " +
"Get-WinEvent -ComputerName COMPUTERNAMEHERE -FilterHashtable #{logname=LOGNAMEHERE;id=IDHERE;data=" +
username + "} | " +
"Select-Object -Property timecreated, " +
"#{label='computername';expression={$_.properties[6].value.Split(':')[3]}} " +
"Get-WinEvent -ComputerName COMPUTERNAMEHERE -FilterHashtable #{logname=LOGNAMEHERE;id=IDHERE;data=" +
username + "} | " +
"Select-Object -Property timecreated, " +
"#{label='computername';expression={$_.properties[6].value.Split(':')[3]}} ");
// invoke execution on the pipeline (collecting output)
Collection<PSObject> PSOutput = powerShellInstance.Invoke();
// loop through each output object item
foreach (PSObject outputItem in PSOutput)
{
// if null object was dumped to the pipeline during the script then a null
// object may be present here. check for null to prevent potential NRE.
if (outputItem != null)
{
listView1.Items.Add(OUTPUTHERE);
}
}
}
catch
{
listView1.Items.Add("Failed");
}
}
but it currently crashes on the part of code where i invoke it, I've never worked on power shell before so any help would be appreciated and im really sorry if this is a stupid question xD
I created a website that does remotely execute powershell to specific server. I would like to know how it work to pass 2 user selected values to commandline from dropdownlist on asp.net website along with powershell script file?
I already have working code with powershell script file but now adding 2 arguments in script file.The powershell arguments is,
[string]$ContainerIn=$args[0]
[int]$ips2get=$args[1]
Here a C# working codes,
//These 3 input varaibles will pass to powershell script to get specific results
string env = "";
string container = "";
string numIPs = "";
//assign dropdown selected value to variable to pass to script
container = DropDownListContainer.SelectedValue;
numIPs = DropDownListIP.SelectedValue;
if (container == "H02" || container == "H07" || container == "H08")
{
env = "Prod";
}
else
{
env = "NonProd";
}
// Create a Powershell
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
Pipeline pipeline = runSpace.CreatePipeline();
Command invokeScript = new Command("Invoke-Command");
RunspaceInvoke invoke = new RunspaceInvoke();
//Add powershell command/script functions into scriptblock
//Somewhere on this codes that it need to add command line to go with Get-FreeAddress.ps1 file script
ScriptBlock sb = invoke.Invoke(#"{D:\Scripts\Get-FreeAddress.ps1}")[0].BaseObject as ScriptBlock;
//ScriptBlock sb = invoke.Invoke("{" + PowerShellCodeBox.Text + "}")[0].BaseObject as ScriptBlock;
invokeScript.Parameters.Add("scriptBlock", sb);
invokeScript.Parameters.Add("computername", TextBoxServer.Text);
pipeline.Commands.Add(invokeScript);
Collection<PSObject> output = pipeline.Invoke();
//splitting results in new lines
foreach (PSObject psObject in output)
{
str = str + psObject + "\r\n";
//str = psObject + "\r\n";
//str += "\n" + psObject;
//str = str + Environment.NewLine + psObject;
}
if (str == "")
{
str = "Error";
ResultBox.ForeColor = System.Drawing.ColorTranslator.FromHtml("#FF0000");
}
//print out powershell output result
ResultBox.Text = str;
}
I finally made this work,
I just need to modify to
ScriptBlock sb = invoke.Invoke(#"{D:\Scripts\Get-FreeAddress.ps1 '"+container+"' "+numIPs+"}")[0].BaseObject as ScriptBlock;
The powershell script argument will get container and numIPs variables.
how to check if OpenOffice is installed programatically using c#
public bool isOpenofficeInstalled()
{
//The registry key:
string SoftwareKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(SoftwareKey))
{
bool flag = false;
//Let's go through the registry keys and get the info we need:
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
//If the key has value, continue, if not, skip it:
// if (((sk.GetValue("DisplayName")).ToString() == "OpenOffice.org 3.2"))
if((sk.GetValue("DisplayName")).ToString() == "OpenOffice.org 3.2")
{
flag = true;
////install location ?
//if (sk.GetValue("InstallLocation") == null)
// Software += sk.GetValue("DisplayName") + " - Install path not known\n"; //Nope, not here.
//else
// Software += sk.GetValue("DisplayName") + " - " + sk.GetValue("InstallLocation") + "\n"; //Yes, here it is...
}
}
catch (Exception)
{
}
}
}
return flag;
}
}
Here is a solution that gets the startup location of the default program to open a odt file. As long as the file association has not been changed this works regardless of what version is installed.
(this is VB.NET)
Dim odt = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(".odt")
Dim linkedValue = odt.GetValue("")
Dim linkedKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(linkedValue)
Dim openWith = linkedKey.OpenSubKey("Shell\Open\Command").GetValue("")
Dim O As String = CStr(openWith)
If O.Contains("swriter.exe") Then
// proceed with code
Else
// error message
End If
Same as in any other language? Search the known locations on the file system for the executable that launches open office? Check for libraries? Parse the output of "which openoffice"?
There are lots of options, and I'd say that most of them would not be reliable.