Dot-Net

註冊沒有管理員權限的 COM

  • December 10, 2019

我想將 .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));
       }
   }
}

引用自:https://stackoverflow.com/questions/35782404