找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1121|回复: 1

[每日一码] .net创建按需加载的代码

[复制链接]

已领礼包: 6个

财富等级: 恭喜发财

发表于 2017-1-17 11:30:59 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
[size=1.2em]
Here’s a question I received recently by email:
How do you set up a .NET plugin for AutoCAD to install & demand load in the same way as ObjectARX plugins? The documentation is not very clear at making the distinctions visible.
In ARX terms, we currently write a set of registry entries as part of our installer, along with refreshing these via an AcadAppInfo registration during ARX load. The ARX itself can be located anywhere as long as the registry entries point to it. I’m not sure of the correct procedure for .NET plugins to duplicate this.
Augusto Gonçalves, from our DevTech Americas team, provided a solution for this which showed how to create demand-loading Registry keys programmatically based on the current assembly’s name and location. It occurred to me that extending the code to make further use of reflection to query the commands defined by an assembly would make this really interesting, and could essentially create a very flexible approach for creation of a demand-loading entries as an application initializes or during execution of a custom command.
Here’s the C# code:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Resources;
using Microsoft.Win32;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
 
namespace DemandLoading
{
  public class RegistryUpdate
  {
    public static void RegisterForDemandLoading()
    {
      // Get the assembly, its name and location
 
      Assembly assem = Assembly.GetExecutingAssembly();
      string name = assem.GetName().Name;
      string path = assem.Location;
 
      // We'll collect information on the commands
      // (we could have used a map or a more complex
      // container for the global and localized names
      // - the assumption is we will have an equal
      // number of each with possibly fewer groups)
 
      List<string> globCmds = new List<string>();
      List<string> locCmds = new List<string>();
      List<string> groups = new List<string>();
 
      // Iterate through the modules in the assembly
 
      Module[] mods = assem.GetModules(true);
      foreach (Module mod in mods)
      {
        // Within each module, iterate through the types
 
        Type[] types = mod.GetTypes();
        foreach (Type type in types)
        {
          // We may need to get a type's resources
 
          ResourceManager rm =
            new ResourceManager(type.FullName, assem);
          rm.IgnoreCase = true;
 
          // Get each method on a type
 
          MethodInfo[] meths = type.GetMethods();
          foreach (MethodInfo meth in meths)
          {
            // Get the methods custom command attribute(s)
 
            object[] attbs =
              meth.GetCustomAttributes(
                typeof(CommandMethodAttribute),
                true
              );
            foreach (object attb in attbs)
            {
              CommandMethodAttribute cma =
                attb as CommandMethodAttribute;
              if (cma != null)
              {
                // And we can finally harvest the information
                // about each command
 
                string globName = cma.GlobalName;
                string locName = globName;
                string lid = cma.LocalizedNameId;
 
                // If we have a localized command ID,
                // let's look it up in our resources
 
                if (lid != null)
                {
                  // Let's put a try-catch block around this
                  // Failure just means we use the global
                  // name twice (the default)
 
                  try
                  {
                    locName = rm.GetString(lid);
                  }
                  catch { }
                }
 
                // Add the information to our data structures
 
                globCmds.Add(globName);
                locCmds.Add(locName);
 
                if (cma.GroupName != null &&
                    !groups.Contains(cma.GroupName))
                  groups.Add(cma.GroupName);
              }
            }
          }
        }
      }
 
      // Let's register the application to load on demand (12)
      // if it contains commands, otherwise we will have it
      // load on AutoCAD startup (2)
 
      int flags = (globCmds.Count > 0 ? 12 : 2);
 
      // By default let's create the commands in HKCU
      // (pass false if we want to create in HKLM)
 
      CreateDemandLoadingEntries(
        name, path, globCmds, locCmds, groups, flags, true
      );
    }
 
    public static void UnregisterForDemandLoading()
    {
      RemoveDemandLoadingEntries(true);
    }
 
    // Helper functions
 
    private static void CreateDemandLoadingEntries(
      string name,
      string path,
      List<string> globCmds,
      List<string> locCmds,
      List<string> groups,
      int flags,
      bool currentUser
    )
    {
      // Choose a Registry hive based on the function input
 
      RegistryKey hive =
        (currentUser ? Registry.CurrentUser : Registry.LocalMachine);
 
      // Open the main AutoCAD (or vertical) and "Applications" keys
 
      RegistryKey ack =
        hive.OpenSubKey(
          HostApplicationServices.Current.RegistryProductRootKey,
          true
        );
      using (ack)
      {
        RegistryKey appk = ack.CreateSubKey("Applications");
        using (appk)
        {
          // Already registered? Just return
 
          string[] subKeys = appk.GetSubKeyNames();
          foreach (string subKey in subKeys)
          {
            if (subKey.Equals(name))
            {
              return;
            }
          }
 
          // Create the our application's root key and its values
 
          RegistryKey rk = appk.CreateSubKey(name);
          using (rk)
          {
            rk.SetValue(
              "DESCRIPTION", name, RegistryValueKind.String
            );
            rk.SetValue("LOADCTRLS", flags, RegistryValueKind.DWord);
            rk.SetValue("LOADER", path, RegistryValueKind.String);
            rk.SetValue("MANAGED", 1, RegistryValueKind.DWord);
 
            // Create a subkey if there are any commands...
 
            if ((globCmds.Count == locCmds.Count) &&
                globCmds.Count > 0)
            {
              RegistryKey ck = rk.CreateSubKey("Commands");
              using (ck)
              {
                for (int i = 0; i < globCmds.Count; i++)
                  ck.SetValue(
                    globCmds[i],
                    locCmds[i],
                    RegistryValueKind.String
                  );
              }
            }
 
            // And the command groups, if there are any
 
            if (groups.Count > 0)
            {
              RegistryKey gk = rk.CreateSubKey("Groups");
              using (gk)
              {
                foreach (string grpName in groups)
                  gk.SetValue(
                    grpName, grpName, RegistryValueKind.String
                  );
              }
            }
          }
        }
      }
    }
 
    private static void RemoveDemandLoadingEntries(bool currentUser)
    {
      try
      {
        // Choose a Registry hive based on the function input
 
        RegistryKey hive =
          (currentUser ?
            Registry.CurrentUser :
            Registry.LocalMachine);
 
        // Open the main AutoCAD (vertical) and "Applications" keys
 
        RegistryKey ack =
          hive.OpenSubKey(
            HostApplicationServices.Current.RegistryProductRootKey
          );
        using (ack)
        {
          RegistryKey appk = ack.OpenSubKey("Applications", true);
          using (appk)
          {
            // Delete the key with the same name as this assembly
 
            appk.DeleteSubKeyTree(
              Assembly.GetExecutingAssembly().GetName().Name
            );
          }
        }
      }
      catch { }
    }
  }
}

If you drop this code into an existing project, you should be able simply to add a call to DemandLoading.RegistryUpdate.RegisterForDemandLoading() during your IExtensionApplication’s Initialize() method or during a custom command.
Here’s an example of the Registry keys created by this code (exported from Regedit) when called from the Initialize() method of the application in this previous post:
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\R18.0\ACAD-8001:409\Applications\OffsetInXref]
"DESCRIPTION"="OffsetInXref"
"LOADCTRLS"=dword:0000000c
"LOADER"="C:\\Program Files\\Autodesk\\AutoCAD 2010\\OffsetInXref.dll"
"MANAGED"=dword:00000001

[HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\R18.0\ACAD-8001:409\Applications\OffsetInXref\Commands]
"XOFFSETLAYER"="XOFFSETLAYER"
"XOFFSETCPLAYS"="XOFFSETCPLAYS"


You can see that our code found the commands defined by the application and created Registry entries which tell AutoCAD to load the module when one of them is chosen by the user. You’ll notice the LOADCTRLS value is c (hexadecimal for 12), which means the application will be loaded “on demand” as a specified command is invoked, but we could also adjust the code to force this to 2, which would mean the module would be loaded on AutoCAD startup (the default when no commands are found). This would actually be a good idea, in this case, as the command hooks into the OFFSET command, and we can’t demand-load a module on invocation of a built-in command.
You’ll also notice that the keys were created under R18.0\ACAD-8001:409 (the English version of AutoCAD 2010), but if the module was loaded in a different language version of an AutoCAD-based vertical product (French AutoCAD Architecture 2009, for instance) then the root key would be the one for that product. All you have to do is load the module once in the AutoCAD-based product of your choice, and it will be registered for automatic loading from then onwards.
This is a useful technique for people who want to deploy .NET modules without installers, or for people who wish applications to re-create their demand-loading keys on load (something this code currently does not do, by the way: if the application’s key is found we do not recreate the contents for the sake of efficiency… you may want to change the code to force creation of the keys should you be adding new commands regularly to your application, for instance).
Update:
I've gone ahead and updated the above code as per the information in this more recent post.



论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

已领礼包: 42个

财富等级: 招财进宝

发表于 2017-8-16 10:22:22 | 显示全部楼层
按需加载,学习了!
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2024-9-20 12:39 , Processed in 0.176263 second(s), 33 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表