Dot-Net
註冊沒有管理員權限的 COM
我想將 .net 程序集註冊為 COM。
事實上,據我所知,這意味著,而不是
HKEY_CLASSES_ROOT我希望將條目寫入HKEY_CURRENT_USER/Software/Classes,這樣就不需要 UAC/Admin 權限。找到了該問題的兩個解決方案,我都在苦苦掙扎:
1)以程式方式,使用以下程式碼:
IntPtr key; var openKeyresult = RegOpenKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\Classes", 0, (int)RegistrySecurity.KEY_WOW64_64KEY, out key); var overrideKeyResult = RegOverridePredefKey(HKEY_CLASSES_ROOT, key); var registerResult = Registrar.RegisterAssembly(GetAssembly(), AssemblyRegistrationFlags.SetCodeBase);在這種方法中,
overrideKeyResult是 6,對應於 ERROR_INVALID_HANDLE,因此,RegisterAssembly拋出“拒絕訪問”異常,因為它試圖寫入HKEY_CLASSES_ROOT.附帶說明:每次我執行的
RegOpenKeyEx鍵值都不同,可以嗎?2)再充氣
通過使用
regasm.exe標誌/regfile,然後在生成的.reg文件中替換所有HKEY_CLASSES_ROOT出現HKEY_CURRENT_USER/Software/Classes我認為這應該可以,但是當我不想解除安裝我的 Outlook 外掛時如何取消註冊這樣的程序集?
正如我所見,我無法以與註冊相同的方式進行註冊,因為:
var openKeyresult = RegOpenKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\Classes", 0, (int)RegistrySecurity.KEY_WOW64_64KEY, out key);
值得一提的是,我編寫了一組 C# 實用程序,可以在使用者系統資料庫中註冊/取消註冊 .NET 類型(當然應該標記為 ComVisible),而不需要 regasm,也不需要 UAC 提示,您可以像這樣使用它:
// register into current user registry, needs no specific rights ComUtilities.RegisterComObject(ComUtilities.Target.User, typeof(MyClass)); // unregister from user registry, needs no specific rights ComUtilities.UnregisterComObject(ComUtilities.Target.User, typeof(MyClass)); // register into machine registry (needs admin, UAC, etc.) ComUtilities.RegisterComObject(ComUtilities.Target.Machine, typeof(MyClass)); // unregister from machine registry (needs admin, UAC, etc.) ComUtilities.UnregisterComObject(ComUtilities.Target.Machine, typeof(MyClass)); public static class ComUtilities { private const string ClsidRegistryKey = @"Software\Classes\CLSID"; public enum Target { Machine, // registers or unregisters a .NET COM object in HKEY_LOCAL_MACHINE, for all users, needs proper rights User // registers or unregisters a .NET COM object in HKEY_CURRENT_USER to avoid UAC prompts } public static void RegisterComObject(Target target, Type type) { RegisterComObject(target, type, null); } public static void RegisterComObject(Target target, Type type, string assemblyPath) { RegisterComObject(target, type, assemblyPath, null); } public static void RegisterComObject(Target target, Type type, string assemblyPath, string runtimeVersion) { if (type == null) throw new ArgumentNullException(nameof(type)); if (type.Assembly == null) throw new ArgumentException(null, nameof(type)); // note we don't check if the type is marked as ComVisible, maybe we should if (assemblyPath == null) { assemblyPath = new Uri(type.Assembly.Location).LocalPath; } if (runtimeVersion == null) { runtimeVersion = GetRuntimeVersion(type.Assembly); } var root = target == Target.User ? Registry.CurrentUser : Registry.LocalMachine; using (RegistryKey key = EnsureSubKey(root, Path.Combine(ClsidRegistryKey, type.GUID.ToString("B"), "InprocServer32"))) { key.SetValue(null, "mscoree.dll"); key.SetValue("Assembly", type.Assembly.FullName); key.SetValue("Class", type.FullName); key.SetValue("ThreadingModel", "Both"); if (assemblyPath != null) { key.SetValue("CodeBase", assemblyPath); } key.SetValue("RuntimeVersion", runtimeVersion); } using (RegistryKey key = EnsureSubKey(root, Path.Combine(ClsidRegistryKey, type.GUID.ToString("B")))) { // cf http://stackoverflow.com/questions/2070999/is-the-implemented-categories-key-needed-when-registering-a-managed-com-compon using (RegistryKey cats = EnsureSubKey(key, @"Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}")) { // do nothing special } var att = type.GetCustomAttribute<ProgIdAttribute>(); if (att != null && !string.IsNullOrEmpty(att.Value)) { using (RegistryKey progid = EnsureSubKey(key, "ProgId")) { progid.SetValue(null, att.Value); } } } } public static void UnregisterComObject(Target target, Type type) { if (type == null) throw new ArgumentNullException(nameof(type)); var root = target == Target.User ? Registry.CurrentUser : Registry.LocalMachine; using (RegistryKey key = root.OpenSubKey(ClsidRegistryKey, true)) { if (key == null) return; key.DeleteSubKeyTree(type.GUID.ToString("B"), false); } } // kind of hack to determine clr version of an assembly private static string GetRuntimeVersion(Assembly asm) { string def = "v4.0.30319"; // use CLR4 as the default try { var mscorlib = asm.GetReferencedAssemblies().FirstOrDefault(a => a.Name == "mscorlib"); if (mscorlib != null && mscorlib.Version.Major < 4) return "v2.0.50727"; // use CLR2 } catch { // too bad, assume CLR4 } return def; } private static RegistryKey EnsureSubKey(RegistryKey root, string name) { RegistryKey key = root.OpenSubKey(name, true); if (key != null) return key; string parentName = Path.GetDirectoryName(name); if (string.IsNullOrEmpty(parentName)) return root.CreateSubKey(name); using (RegistryKey parentKey = EnsureSubKey(root, parentName)) { return parentKey.CreateSubKey(Path.GetFileName(name)); } } }