Get Program and Feature list - c#

Following the code in this MSDN blog, i have come up with the following code in C#
using Shell32; //Browse to C:\Windows\System32\shell32.dll
private void GetInstalledPrograms()
{
Shell shell = new Shell();
Shell objShell = shell.Application;
string folderName = #"::{26EE0668-A00A-44D7-9371-BEB064C98683}\8\" +
"::{7B81BE6A-CE2B-4676-A29E-EB907A5126C5}";
var objProgramList = objShell.NameSpace(folderName);
if (objProgramList != null)
{
MessageBox.Show(objProgramList.Items().ToString());
}
else
{
MessageBox.Show("Null");
}
}
For what ever reason, objProgramList is null. The odd thing is, with the following powershell code, I get exactly what I'm looking for! I don't know what I'm doing wrong. To me, both examples of my code are identical...
$Shell = New-Object -ComObject Shell.Application
$folderName = "::{26EE0668-A00A-44D7-9371-BEB064C98683}\8\::{7B81BE6A-CE2B-4676-A29E- EB907A5126C5}"
$folder = $Shell.NameSpace($folderName)
if($folder)
{
$folder.Items()
}

Any chance you are using Window 8? According to this answer, creating a shell like that doesn't work in Window 8.

This answer is too late but it might help others with the same problem.
Basically, the problem on your code was the shell command:
string folderName = #"::{26EE0668-A00A-44D7-9371-BEB064C98683}\8\" +
"::{7B81BE6A-CE2B-4676-A29E-EB907A5126C5}";
It should contain "shell:" in the beginning of the command, it should look like this:
string folderName = #"shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\8\::{7B81BE6A-CE2B-4676-A29E-EB907A5126C5}"
And to get info about the programs like the Name, Publisher, Installed On and etc, try this code that will enumerate all the available fields:
List<string> arrHeaders = new List<string>();
for (int i = 0; i < short.MaxValue; i++)
{
string header = list.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header))
break;
arrHeaders.Add(header);
}
foreach (Shell32.FolderItem2 item in list.Items())
{
for (int i = 0; i < arrHeaders.Count; i++)
{
//I used listbox to show the fields
listBox1.Items.Add(string.Format("{0}\t{1}: {2}", i, arrHeaders[i], list.GetDetailsOf(item, i)));
}
}

Related

Automatically find the path of the python executable

I'm doing a project that uses python as background script and C# as guy.
My problem is that I can't figure out how to cause my GUI to automatically search for the pythonw.exe file in order to run my python scripts.
Currently I'm using this path:
ProcessStartInfo pythonInfo = new ProcessStartInfo(#"C:\\Users\\Omri\\AppData\\Local\\Programs\\Python\\Python35-32\\pythonw.exe");
but I want it to auto detect the path of pythonw.exe (I need to submit the project and it won't run on others computers unless they change the code itself)
Any suggestions may be helpful.
Inspired by #Shashi Bhushan's answer I made this function for getting the Python path reliably;
private static string GetPythonPath(string requiredVersion = "", string maxVersion = "") {
string[] possiblePythonLocations = new string[3] {
#"HKLM\SOFTWARE\Python\PythonCore\",
#"HKCU\SOFTWARE\Python\PythonCore\",
#"HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\"
};
//Version number, install path
Dictionary<string, string> pythonLocations = new Dictionary<string, string>();
foreach (string possibleLocation in possiblePythonLocations) {
string regKey = possibleLocation.Substring(0, 4), actualPath = possibleLocation.Substring(5);
RegistryKey theKey = (regKey == "HKLM" ? Registry.LocalMachine : Registry.CurrentUser);
RegistryKey theValue = theKey.OpenSubKey(actualPath);
foreach (var v in theValue.GetSubKeyNames()) {
RegistryKey productKey = theValue.OpenSubKey(v);
if (productKey != null) {
try {
string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("ExecutablePath").ToString();
// Comment this in to get (Default) value instead
// string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("").ToString();
if (pythonExePath != null && pythonExePath != "") {
//Console.WriteLine("Got python version; " + v + " at path; " + pythonExePath);
pythonLocations.Add(v.ToString(), pythonExePath);
}
} catch {
//Install path doesn't exist
}
}
}
}
if (pythonLocations.Count > 0) {
System.Version desiredVersion = new System.Version(requiredVersion == "" ? "0.0.1" : requiredVersion),
maxPVersion = new System.Version(maxVersion == "" ? "999.999.999" : maxVersion);
string highestVersion = "", highestVersionPath = "";
foreach (KeyValuePair<string, string> pVersion in pythonLocations) {
//TODO; if on 64-bit machine, prefer the 64 bit version over 32 and vice versa
int index = pVersion.Key.IndexOf("-"); //For x-32 and x-64 in version numbers
string formattedVersion = index > 0 ? pVersion.Key.Substring(0, index) : pVersion.Key;
System.Version thisVersion = new System.Version(formattedVersion);
int comparison = desiredVersion.CompareTo(thisVersion),
maxComparison = maxPVersion.CompareTo(thisVersion);
if (comparison <= 0) {
//Version is greater or equal
if (maxComparison >= 0) {
desiredVersion = thisVersion;
highestVersion = pVersion.Key;
highestVersionPath = pVersion.Value;
} else {
//Console.WriteLine("Version is too high; " + maxComparison.ToString());
}
} else {
//Console.WriteLine("Version (" + pVersion.Key + ") is not within the spectrum.");
}
}
//Console.WriteLine(highestVersion);
//Console.WriteLine(highestVersionPath);
return highestVersionPath;
}
return "";
}
You can find python installation path by lookup following keys on windows machine.
HKLM\SOFTWARE\Python\PythonCore\versionnumber\InstallPath
HKCU\SOFTWARE\Python\PythonCore\versionnumber\InstallPath
for win64 bit machine
HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\versionnumber\InstallPath
You can refer this post for how to read registry using C#
How to read value of a registry key c#
Find the environment variable name in Windows, for that assembly and use Environment.GetEnvironmentVariable(variableName)
Check out How to add to the pythonpath in windows 7?
An example on how to search for Python within the PATH environment variable:
var entries = Environment.GetEnvironmentVariable("path").Split(';');
string python_location = null;
foreach (string entry in entries)
{
if (entry.ToLower().Contains("python"))
{
var breadcrumbs = entry.Split('\\');
foreach (string breadcrumb in breadcrumbs)
{
if (breadcrumb.ToLower().Contains("python"))
{
python_location += breadcrumb + '\\';
break;
}
python_location += breadcrumb + '\\';
}
break;
}
}
Just change the FileName to "python.exe" if you already set python env path
private void runPython(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "python.exe";
start.Arguments = string.Format("{0} {1}", cmd, args);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
On my machine, with Python 3.11 installed, I can query it by defining this property:
public string PythonInstallPath
{
get => (string)Microsoft.Win32.Registry.GetValue(
#"HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\3.11\InstallPath",
"ExecutablePath", null);
}
Pythonw.exe is located in the same path, so you can do:
public string PythonWInstallPath
{
get => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(PythonInstallPath),
"pythonw.exe");
}
There is also a way to look it up in the environment, check this out as an alternative.

C# .NET "Parameter is invalid" when Image in using statement

Windows 8.1 Pro, Visual Studio 2015 Update 3, C#, .NET Framework 4.5. Ghostscript.NET (latest), GhostScript 9.20.
I'm converting a PDF to a PDF. Hah. Well, I'm making an "editable" PDF "hard" PDF that can't be edited and is of lower quality. The process is I take the editable PDF, save it out as x-pages of PNG files, convert those PNG files to a multipage TIFF, and then convert the multipage TIFF to the PDF I need.
This worked just fine with Visual Studio 2012, one version earlier of GhostScript .NET and GS 9.10.
public static Tuple<string, List<string>> CreatePNGFromPDF(string inputFile, string outputfile)
{
Tuple<string, List<string>> t = null;
List<string> fileList = new List<string>();
string message = "Success";
string outputFileName = string.Empty;
int desired_x_dpi = 96;
int desired_y_dpi = 96;
try
{
using (GhostscriptViewer gsViewer = new GhostscriptViewer())
{
gsViewer.Open(inputFile);
using (GhostscriptRasterizer rasterizer = new GhostscriptRasterizer(gsViewer))
{
for (int pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++)
{
using (System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber))
{
outputFileName = outputfile.Replace(".png", string.Empty) + "_page_" + pageNumber.ToString() + ".png";
img.Save(outputFileName, ImageFormat.Png);
if (!fileList.Contains(outputFileName))
{
fileList.Add(outputFileName);
}
}
}
}
}
}
catch (Exception ex)
{
message = ex.Message;
}
t = new Tuple<string, List<string>>(message, fileList);
return t;
}
This now fails on this line:
using (System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber))
when processing the second page. The first page works okay.
I downloaded the source for GhostScript.NET, added it to my solution, debugged, etc., and spent a good long while trying to figure this out.
I then decided to separate out the functionality and make the bare minimum available for me to examine further in a simple Console application:
static void Main(string[] args)
{
int xDpi = 96;
int yDpi = 96;
string pdfFile = #"Inputfilenamehere.pdf";
GhostscriptVersionInfo gsVersionInfo = GhostscriptVersionInfo.GetLastInstalledVersion(GhostscriptLicense.GPL | GhostscriptLicense.AFPL, GhostscriptLicense.GPL);
List<GhostscriptVersionInfo> gsVersionInfoList = GhostscriptVersionInfo.GetInstalledVersions(GhostscriptLicense.GPL | GhostscriptLicense.AFPL);
try
{
using (GhostscriptViewer gsViewer = new GhostscriptViewer())
{
gsViewer.Open(pdfFile);
using (GhostscriptRasterizer gsRasterizer = new GhostscriptRasterizer(gsViewer))
{
int pageCount = gsRasterizer.PageCount;
for (int i = 0; i < pageCount; i++)
{
Image img = gsRasterizer.GetPage(xDpi, yDpi, i + 1);
}
}
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Lo and behold, no problems. The difference is that I'm not putting declaration of my Image in the using statement.
I always try to be a good boy developer and use a using statement whenever the class implements IDisposable.
So, I removed the use of the using and I get the lower-quality PDF's that I've always desired. My life is good now.
using (GhostscriptViewer gsViewer = new GhostscriptViewer())
{
gsViewer.Open(inputFile);
using (GhostscriptRasterizer rasterizer = new GhostscriptRasterizer(gsViewer))
{
for (int pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++)
{
System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber);
outputFileName = outputfile.Replace(".png", string.Empty) + "_page_" + pageNumber.ToString() + ".png";
img.Save(outputFileName, ImageFormat.Png);
if (!fileList.Contains(outputFileName))
{
fileList.Add(outputFileName);
}
}
}
}
Note that if I call img.Dispose() at the end of the for loop, I get the same error again!
My best guess is that my issue is not a GhostScript or GhostScript.NET issue. Am I being a bonehead for insisting on blindly using "using" statements if the class implements IDisposable? I've always understood that it's best practice to wrap anything that implements IDisposable with a using statement to forgo leaks, etc.
Hence, my question: Any ideas why I get the "Parameter is invalid" exception when I initialize the System.Drawing.Image class within the using statement but not when I don't? I'd love to understand this more.
Better yet, if anyone knows how I can get this functionality and also ensure I'm properly disposing my object, that would be the best.
I didn't find much about this particular topic when I searched for information. I did find one other StackOverflow post about someone using a graphic object in a using statement with the same error. I wonder if there is a relationship. I also note that I should be using Dispose(), but that appears to be causing the problem, and I need this to work.
FYI, for anyone interested, the actual error occurs here in GhostscriptInterprester.cs in the GhostScript.NET code:
Method: public void Run(string str)
str is "Page pdfshowpage_init pdfshowpage_finish"
// GSAPI: run the string
int rc_run = _gs.gsapi_run_string(_gs_instance, str, 0, out exit_code);
I found the root cause of my failure at least. My GhostscriptRasterizer object had a value of '0' set for the height points and width points.
var rasterizer = new GhostscriptRasterizer();
rasterizer.CustomSwitches.Add("-dDEVICEWIDTHPOINTS=" + widthPoints);
rasterizer.CustomSwitches.Add("-dDEVICEHEIGHTPOINTS=" + heightPoints);
Once I set both height and width to a valid non-zero value, the issue got fixed.

How to simplify href attribute so I don't get "the given paths format is not supported"?

I get an error
the given paths format is not supported
but when I use driver.Title instead of links[i] it works properly, just there is so many same titles and because of that for me its better to use href, but I guess that you cant use ":" or "/" in a file name, so how to simplify href, so I will not get "not supported path" error?
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
string[] links = new string[linkCount];
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
for (int i = 0; i < linkCount; i++)
{
links[i] = linksToClick[i].GetAttribute("href");
}
for (int i = 0; i < linkCount; i++)
{
driver.Navigate().GoToUrl(links[i]);
ITakesScreenshot screenshotDriver = driver as ITakesScreenshot;
Screenshot screenCapture = screenshotDriver.GetScreenshot();
screenCapture.SaveAsFile(Path.Combine(testPath, links[i] +"_"+ testScreenshotTitle),
System.Drawing.Imaging.ImageFormat.Png);
}
If the goal is to get list of links on the page except for a specific links, maybe this would work better
using System.Link;
var blackList = {"LogOff", ...};
var links = driver
.FindElements(By.CssSelector("a[href]"))
.Select(a => a.GetAttribute("href"))
.Where(u => !blackList.Any(s => s.Contains(u)));
foreach (string link in links)
{
...
}
Update
So to sanitize a file name
foreach (string link in links)
{
var fileName = Path.Combine(testPath, link + "_" + testScreenshotTitle;
foreach (char c in Path.GetInvalidFileNameChars())
{
fileName = fileName.Replace(c, '_');
}
...
}

taglib# cannot save tag

I try to make app which will load and edit id3 tags. I decided to use taglib for that. Everything works fine, but when i try to save edited tag it falls on IOException "The process cannot access the file ...". Heres code:
TagLib.File f = TagLib.File.Create(cesta);
f.Tag.Year = 1999;//uint.Parse(textBox1.Text);
f.Save();
Previously i just have load procedure:
TagLib.File f = TagLib.File.Create(path);
string rok = f.Tag.Year.ToString();
textBox1.Text = rok;
string album = f.Tag.Album;
textBox2.Text = album;
string[] artist = f.Tag.Performers;
string autor = "";
for (int i = 0; i < artist.Length; i++)
{
autor = autor + artist[i];
}
textBox3.Text = autor;
Does anyone know, that I did wrong?
If you're trying to edit an existing file by reopening it, make sure that you have previously closed it. Also, see if you can enclose all of your file access code with using blocks. For example:
using(TagLib.File f = TagLib.File.Create(path))
{
// do work
}

Method Hangs on for loop and won't continue

I'm using html agility pack to parse several text files that I load. I then save the data that I parse out into a string list for further processing. However, when I use this method, it never hits the line:
MessageBox.Show("test");
Additionally, if I include any other code following this method, none of it is triggered.
Does anyone have any suggestions as to my error?
The entire method is included below:
private void ParseOutput()
{
nodeDupList = new List<string>();
StreamWriter OurStream;
OurStream = File.CreateText(dir + #"\CombinedPages.txt");
OurStream.Close();
for (int crawl = 1; crawl <= crawlPages.Length; crawl++)
{
var web = new HtmlWeb();
var doc = web.Load(dir + #"\Pages" + crawl.ToString() + ".txt");
var nodeCount = doc.DocumentNode.SelectNodes(#"/html[1]/body[1]/div[1]/table[3]/tbody[1]/tr[td/#class=""style_23""]");
int nCount = nodeCount.Count;
for (int a = 3; a <= nCount; a++)
{
var specContent = doc.DocumentNode.SelectNodes(#"/html[1]/body[1]/div[1]/table[3]/tbody[1]/tr[" + a + #"]/td[3]/div[contains(#class,'style_24')]");
foreach (HtmlNode node in specContent)
{
nodeDupList.Add(node.InnerText + ".d");
}
}
}
MessageBox.Show("test");
}
I've created a crawler to save multiple html pages to text and parse them separately using this method.
I'm just using MessageBox to show that it won't continue following the "for loop". I've called multiple methods in my solution and it won't iterate through them.
The application is a Win Forms Application targeted at .Net Framework 4.
Edit:
Thanks for the help.
I realized after rerunning it through the debugger that it was crashing at times on the loop
for (int a = 3; a <= nCount; a++)
{
var specContent = doc.DocumentNode.SelectNodes(#"/html[1]/body[1]/div[1]/table[3]/tbody[1]/tr[" + a + #"]/td[3]/div[contains(#class,'style_24')]");
foreach (HtmlNode node in specContent)
{
nodeDupList.Add(node.InnerText + ".d");
}
}
when the var specContent was null.
There was no exception generated; the method just ended.
As the website is dynamic that I was crawling it rarely returned null but on several instances it had and this happened.
The solution, for anyone who might need this is to check if
for (int a = 3; a <= nCount; a++)
{
var specContent = doc.DocumentNode.SelectNodes(#"/html[1]/body[1]/div[1]/table[3]/tbody[1]/tr[" + a + #"]/td[3]/div[contains(#class,'style_24')]");
if(specContent !=null) //added this check for null
{
foreach (HtmlNode node in specContent)
{
nodeDupList.Add(node.InnerText + ".d");
}
}
}
I also could have used a try{} catch{} block to output the error if needed

Categories

Resources