Como encontrar o diretório de instalação de um aplicativo de terceiros, como o Google Earth, utilizando C #?
-
08-07-2019 - |
Pergunta
Eu tenho o seguinte fragmento de código que inicia o processo de um Google Earth usando um caminho codificado:
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();
Eu quero buscar programaticamente o local de instalação do googleearth.exe de algum lugar (provavelmente o registro).
Solução
Obviamente, se você está abrindo um arquivo específico associado com o programa, em seguida, iniciá-lo através do arquivo é preferível (por exemplo, o usuário pode ter um programa associado ao tipo de arquivo que eles preferem usar).
Aqui está um método que eu usei no passado para lançar uma aplicação associada a um determinado tipo de arquivo, mas sem realmente abrir um arquivo. Pode haver uma maneira melhor de fazê-lo.
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;
}
Às vezes, porém há casos em que você deseja lançar um programa específico sem abrir um arquivo. Normalmente (espero) o programa tem uma entrada de registro com o local de instalação. Aqui está um exemplo de como iniciar o Google Earth de tal maneira a.
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;
}
Outras dicas
A partir do exemplo dado você pode avaliar que estou realmente tentando passar um arquivo KML para o Google Earth. Devido a isso, a maneira mais simples de resolver este problema está contando com a associação de arquivo de KML com o Google Earth e usando o seguinte como um substituto para o exemplo inteiro:
Process.Start(kmlPath);
Esta foi encontrado pela análise das respostas a este questão.
Isso também funciona: (código C #)
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);
}
}
Aqui está uma versão C ++ eu só tinha de escrever. Levado diretamente a partir da versão do ICR C #.
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();
}
}