C # 코드를 사용하여 시스템에 애플리케이션을 설치하는 방법은 무엇입니까?
답변:
레지스트리 키 "SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall"을 반복하면 설치된 응용 프로그램의 전체 목록이 제공되는 것 같습니다.
아래 예제를 제외하고 여기에서 수행 한 것과 유사한 버전을 찾을 수 있습니다 .
이것은 대략적인 예입니다. 제공된 두 번째 링크에서와 같이 빈 행을 제거하기 위해 무언가를하고 싶을 것입니다.
string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using(Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
{
foreach(string subkey_name in key.GetSubKeyNames())
{
using(RegistryKey subkey = key.OpenSubKey(subkey_name))
{
Console.WriteLine(subkey.GetValue("DisplayName"));
}
}
}
또는 앞서 언급 한대로 WMI를 사용할 수 있습니다.
ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach(ManagementObject mo in mos.Get())
{
Console.WriteLine(mo["Name"]);
}
그러나 이것은 실행 속도가 다소 느리고 "ALLUSERS"아래에 설치된 프로그램 만 나열 될 수 있다고 들었습니다. 비록 정확하지 않을 수 있습니다. 또한 편리 할 수있는 Windows 구성 요소 및 업데이트를 무시합니다.
이 기사를 볼 수 있습니다 . 설치된 응용 프로그램 목록을 읽기 위해 레지스트리를 사용합니다.
public void GetInstalledApps()
{
string uninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(uninstallKey))
{
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
lstInstalled.Items.Add(sk.GetValue("DisplayName"));
}
catch (Exception ex)
{ }
}
}
}
}
레지스트리 키를 통해 열거하는 것이 가장 좋은 방법이라는 데 동의합니다.
참고 하지만, 키가 주어진 것을 @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
, 윈도우 64 비트 설치의 모든 32 비트 Windows 설치에서 응용 프로그램 및 64 비트 응용 프로그램을 나열합니다.
Windows 64 비트 설치에 설치된 32 비트 애플리케이션도 보려면 키를 열거해야합니다 @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
.
regedit
그렇게 보인다. 32 비트 프로그램 (64 비트 윈도우에서) 그러나 두리스트는 동일 WOW6432Node
하나 regedit
.
시작 메뉴에 나타나는대로 앱 목록을 추출 할 수 있기를 원했습니다. 레지스트리를 사용하여 시작 메뉴에 표시되지 않는 항목을 가져 왔습니다.
또한 exe 경로를 찾고 아이콘을 추출하여 멋진 런처를 만들고 싶었습니다. 불행히도 레지스트리 방법을 사용하면이 정보를 안정적으로 사용할 수 없다는 것이 관찰 되었기 때문에 이것은 일종의 히트 앤 미스입니다.
내 대안은 shell : AppsFolder를 기반으로하며 실행 explorer.exe shell:appsFolder
하여 액세스 할 수 있으며 현재 설치되어 시작 메뉴를 통해 사용 가능한 스토어 앱을 포함한 모든 앱을 나열합니다. 문제는이 폴더가 .NET으로 액세스 할 수없는 가상 폴더라는 것입니다 System.IO.Directory
. 대신 기본 shell32 명령을 사용해야합니다. 다행히 Microsoft 는 앞서 언급 한 명령의 래퍼 인 Nuget에 Microsoft.WindowsAPICodePack-Shell 을 게시했습니다 . 충분히 말하면 다음은 코드입니다.
// GUID taken from https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid
var FOLDERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FOLDERID_AppsFolder);
foreach (var app in (IKnownFolder)appsFolder)
{
// The friendly app name
string name = app.Name;
// The ParsingName property is the AppUserModelID
string appUserModelID = app.ParsingName; // or app.Properties.System.AppUserModel.ID
// You can even get the Jumbo icon in one shot
ImageSource icon = app.Thumbnail.ExtraLargeBitmapSource;
}
그리고 그게 전부입니다. 다음을 사용하여 앱을 시작할 수도 있습니다.
System.Diagnostics.Process.Start("explorer.exe", @" shell:appsFolder\" + appModelUserID);
이는 일반 Win32 앱 및 UWP 스토어 앱에서 작동합니다. 사과는 어때요?
설치된 모든 앱을 나열하는 데 관심이 있으므로 다음을 사용하여 수행 할 수있는 새 앱 또는 제거 된 앱도 모니터링 할 수 있습니다 ShellObjectWatcher
.
ShellObjectWatcher sow = new ShellObjectWatcher(appsFolder, false);
sow.AllEvents += (s, e) => DoWhatever();
sow.Start();
편집 : 위에 언급 된 AppUserMoedlID가 Windows가 작업 표시 줄에서 창을 그룹화하는 데 사용 하는 고유 ID 라는 사실을 알고 싶을 수도 있습니다 .
AllEvents
등 ItemCreated
이나 ItemRenamed
나는 그들이 설치 또는 제거로 응용 프로그램을 추적하기 위해 사용하는 시도한다. 이 이벤트의 이벤트 인수에는 Path
속성이 포함되어 있지만이 속성은 적어도 내 테스트에서 항상 null입니다. 항상 null이기 때문에 구문 분석 이름을 얻는 방법을 알아 내려고 노력하지 않았습니다. 대신 폴더의 앱을 반복하여 항목이 발생할 때마다 동기화하는 앱 목록을 유지합니다. 이상적이지는 않지만 작업을 완료합니다.
Win32_Product WMI 클래스는 Windows Installer에 의해 설치되는 제품을 나타냅니다 . 모든 응용 프로그램이 Windows 설치 프로그램을 사용하는 것은 아닙니다.
그러나 "SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall"은 32 비트 용 응용 프로그램을 나타냅니다. 64 비트의 경우 "HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall"을 통과해야합니다. 모든 소프트웨어에 64 비트 버전이있는 것은 아니기 때문에 설치된 전체 응용 프로그램은 "UninstallString"이있는 두 위치에서 키의 합집합입니다. 그들과 함께 가치.
그러나 최상의 옵션은 동일한 .traverse 레지스트리 키로 유지되는 것이 좋습니다. 모든 응용 프로그램이 레지스트리에 항목 (Windows Installer의 항목 포함)이 있기 때문에 더 나은 방법입니다. 그러나 레지스트리 방법은 마치 누군가가 해당 키를 제거하는 것처럼 안전하지 않습니다. 반대로 HKEY_Classes_ROOT \ Installers를 변경하는 것은 Microsoft Office 또는 기타 제품과 같은 라이선스 문제와 관련되어 있기 때문에 더 까다 롭습니다. 보다 강력한 솔루션을 위해 언제든지 레지스트리 대안을 WMI와 결합 할 수 있습니다.
허용되는 솔루션이 작동하지만 완전하지는 않습니다. 지금까지.
모든 키를 얻으려면 다음 두 가지를 더 고려해야합니다.
x86 및 x64 응용 프로그램은 동일한 레지스트리에 액세스 할 수 없습니다. 기본적으로 x86은 일반적으로 x64 레지스트리에 액세스 할 수 없습니다. 일부 응용 프로그램은 x64 레지스트리에만 등록됩니다.
과
일부 응용 프로그램은 실제로 LocalMachine 대신 CurrentUser 레지스트리에 설치됩니다.
염두에두고, 나는, ALL 다음 코드를 사용하여 응용 프로그램을 설치 얻을 수 있었다 없이 WMI를 사용하여
다음은 코드입니다.
List<string> installs = new List<string>();
List<string> keys = new List<string>() {
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
};
// The RegistryView.Registry64 forces the application to open the registry as x64 even if the application is compiled as x86
FindInstalls(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64), keys, installs);
FindInstalls(RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64), keys, installs);
installs = installs.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList();
installs.Sort(); // The list of ALL installed applications
private void FindInstalls(RegistryKey regKey, List<string> keys, List<string> installed)
{
foreach (string key in keys)
{
using (RegistryKey rk = regKey.OpenSubKey(key))
{
if (rk == null)
{
continue;
}
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
installed.Add(Convert.ToString(sk.GetValue("DisplayName")));
}
catch (Exception ex)
{ }
}
}
}
}
}
"HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall"키를 반복하고 "DisplayName"값을 확인합니다.
Windows Installer API를 사용하십시오!
모든 프로그램을 안정적으로 열거 할 수 있습니다. 레지스트리는 신뢰할 수 없지만 WMI는 무겁습니다.
목록의 개체 :
public class InstalledProgram
{
public string DisplayName { get; set; }
public string Version { get; set; }
public string InstalledDate { get; set; }
public string Publisher { get; set; }
public string UnninstallCommand { get; set; }
public string ModifyPath { get; set; }
}
목록 생성 요청 :
List<InstalledProgram> installedprograms = new List<InstalledProgram>();
string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
{
foreach (string subkey_name in key.GetSubKeyNames())
{
using (RegistryKey subkey = key.OpenSubKey(subkey_name))
{
if (subkey.GetValue("DisplayName") != null)
{
installedprograms.Add(new InstalledProgram
{
DisplayName = (string)subkey.GetValue("DisplayName"),
Version = (string)subkey.GetValue("DisplayVersion"),
InstalledDate = (string)subkey.GetValue("InstallDate"),
Publisher = (string)subkey.GetValue("Publisher"),
UnninstallCommand = (string)subkey.GetValue("UninstallString"),
ModifyPath = (string)subkey.GetValue("ModifyPath")
});
}
}
}
}
다른 사람들이 지적했듯이 수락 된 대답은 x86 및 x64 설치를 모두 반환하지 않습니다. 아래는 이에 대한 나의 해결책입니다. 를 만들고 StringBuilder
레지스트리 값을 서식과 함께 추가하고 출력을 텍스트 파일에 씁니다.
const string FORMAT = "{0,-100} {1,-20} {2,-30} {3,-8}\n";
private void LogInstalledSoftware()
{
var line = string.Format(FORMAT, "DisplayName", "Version", "Publisher", "InstallDate");
line += string.Format(FORMAT, "-----------", "-------", "---------", "-----------");
var sb = new StringBuilder(line, 100000);
ReadRegistryUninstall(ref sb, RegistryView.Registry32);
sb.Append($"\n[64 bit section]\n\n{line}");
ReadRegistryUninstall(ref sb, RegistryView.Registry64);
File.WriteAllText(@"c:\temp\log.txt", sb.ToString());
}
private static void ReadRegistryUninstall(ref StringBuilder sb, RegistryView view)
{
const string REGISTRY_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view);
using var subKey = baseKey.OpenSubKey(REGISTRY_KEY);
foreach (string subkey_name in subKey.GetSubKeyNames())
{
using RegistryKey key = subKey.OpenSubKey(subkey_name);
if (!string.IsNullOrEmpty(key.GetValue("DisplayName") as string))
{
var line = string.Format(FORMAT,
key.GetValue("DisplayName"),
key.GetValue("DisplayVersion"),
key.GetValue("Publisher"),
key.GetValue("InstallDate"));
sb.Append(line);
}
key.Close();
}
subKey.Close();
baseKey.Close();
}
가장 좋은 방법은 WMI 를 사용하는 것 입니다. 특히 Win32_Product 클래스입니다.
WMI ( Windows Management Instrumentation )를 살펴 보시기 바랍니다 . System.Management 참조를 C # 프로젝트에 추가하면`ManagementObjectSearcher '클래스에 액세스 할 수 있으며 유용 할 것입니다.
설치된 응용 프로그램에 대한 다양한 WMI 클래스가 있지만 Windows Installer와 함께 설치된 경우 Win32_Product 클래스가 가장 적합 할 것입니다.
ManagementObjectSearcher s = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
Nicks 접근 방식을 사용했습니다. Visual Studio 용 원격 도구가 설치되어 있는지 여부를 확인해야했습니다. 약간 느려 보이지만 별도의 스레드에서는 괜찮습니다. -여기 내 확장 코드 :
private bool isRdInstalled() {
ManagementObjectSearcher p = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach (ManagementObject program in p.Get()) {
if (program != null && program.GetPropertyValue("Name") != null && program.GetPropertyValue("Name").ToString().Contains("Microsoft Visual Studio 2012 Remote Debugger")) {
return true;
}
if (program != null && program.GetPropertyValue("Name") != null) {
Trace.WriteLine(program.GetPropertyValue("Name"));
}
}
return false;
}
내 요구 사항은 내 시스템에 특정 소프트웨어가 설치되어 있는지 확인하는 것입니다. 이 솔루션은 예상대로 작동합니다. 도움이 될 수 있습니다. Visual Studio 2015에서 C #의 Windows 응용 프로그램을 사용했습니다.
private void Form1_Load(object sender, EventArgs e)
{
object line;
string softwareinstallpath = string.Empty;
string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (var baseKey = Microsoft.Win32.RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
{
using (var key = baseKey.OpenSubKey(registry_key))
{
foreach (string subkey_name in key.GetSubKeyNames())
{
using (var subKey = key.OpenSubKey(subkey_name))
{
line = subKey.GetValue("DisplayName");
if (line != null && (line.ToString().ToUpper().Contains("SPARK")))
{
softwareinstallpath = subKey.GetValue("InstallLocation").ToString();
listBox1.Items.Add(subKey.GetValue("InstallLocation"));
break;
}
}
}
}
}
if(softwareinstallpath.Equals(string.Empty))
{
MessageBox.Show("The Mirth connect software not installed in this system.")
}
string targetPath = softwareinstallpath + @"\custom-lib\";
string[] files = System.IO.Directory.GetFiles(@"D:\BaseFiles");
// Copy the files and overwrite destination files if they already exist.
foreach (var item in files)
{
string srcfilepath = item;
string fileName = System.IO.Path.GetFileName(item);
System.IO.File.Copy(srcfilepath, targetPath + fileName, true);
}
return;
}