Author Topic: Changing the order of Values for Registry Keys  (Read 9758 times)

0 Members and 1 Guest are viewing this topic.

Keith Brown

  • Swamp Rat
  • Posts: 601
Changing the order of Values for Registry Keys
« on: October 18, 2013, 07:56:01 AM »
Does anyone know how to change the order of the values of registry keys?
 
I have a registry key with 5 items in it.
 
Item1
Item2
Item3
Item4
Item5
 
I would like to make the new order
 
Item5
Item1
Item2
Item3
Item4
 
The AutoCAD MEP API has some catalog functions but a lot of them only work with the first catalog in the list of catalogs.  I could of course manually change the order of the catalogs in the options editor but I would like to be able to do it with code.  I searched MSDN but didn't find any relevant information.  albeit I don't know much about the registry.
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Changing the order of Values for Registry Keys
« Reply #1 on: October 18, 2013, 09:45:00 AM »
The registry stores data sequentially, based on when it is put into the registry, however, the registry editor, when opening a particular hive, loads the current set of keys in alphabetical order. As you might know, any changes in the registry data in the editor causes the subsequent hive to be updated.

I think this might be one of those cases where simply viewing the data causes it to change in the hive.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Changing the order of Values for Registry Keys
« Reply #2 on: October 18, 2013, 09:51:59 AM »
Maybe I have my terminology mixed up.  Once I delve down into the last node of the tree I find the key that I want.  In this case the key name would be:
 
HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\R19.1\ACAD-D006:409\Profiles\AutoCAD MEP (US Imperial)\Preferences\AecbCatalog70\MvPartContent
 
Inside of that key I see 4 pieces of data each with a name, type and data.  The names are in alphabetical order but the data is not.  So I guess what I should do is read the names, get the data values, sort the values and then overwrite the existing data values?
 
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Changing the order of Values for Registry Keys
« Reply #3 on: October 18, 2013, 10:24:26 AM »
I'm still not sure what you want ... the keys and values are sorted alphabetically when viewed by the registry editor, thus if you look at the keys and values in the registry editor (to verify their order) they will never be in the order you are expecting, unless you expect them to be in alphabetical order.

Accessing them programmatically, you have the possibility that they could be in the order you are expecting, but I don't think you can guarantee that.

I believe you will have to manually select the key and value you want and not rely upon it to be the first in the list.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Changing the order of Values for Registry Keys
« Reply #4 on: October 18, 2013, 11:48:25 AM »
Ok,  This is what is in the registry for the key I posted above.
 
Name         Type        Data
(Default)    REG_SZ     (value not set)
Path0        REG_SZ     C:\Users\Keith\Equipment Files\MvParts\Catalog4.apc
Path1        REG_SZ     C:\Users\Keith\Equipment Files\MvParts\Catalog3.apc
Path2        REG_SZ     C:\Users\Keith\Equipment Files\MvParts\Catalog1.apc
Path3        REG_SZ     C:\Users\Keith\Equipment Files\MvParts\Catalog2.apc
 
 
I need to change it to
 
Name         Type        Data
(Default)    REG_SZ     (value not set)
Path0        REG_SZ     C:\Users\Keith\Equipment Files\MvParts\Catalog3.apc
Path1        REG_SZ     C:\Users\Keith\Equipment Files\MvParts\Catalog4.apc
Path2        REG_SZ     C:\Users\Keith\Equipment Files\MvParts\Catalog1.apc
Path3        REG_SZ     C:\Users\Keith\Equipment Files\MvParts\Catalog2.apc
 
 
The reason why I need to do this is AutoCAD MEP's API for catalogs has some methods that only operate on the first catalog on the list or ALL catalogs.  If I want to do an operation on the third catalog on the list then I need to move it to the top of the list.  So I believe my only recourse is to read all four paths and get their values.  I would then assign the value (catalog name) to Path0 and then slide the rest on down.  So I just want to swap the data around but leave the Names the same.  Does this make sense?
 
AutoCAD MEP has a screen in the Options dialog to add/remove catalogs and then to change the order up down.  Changing the order will change the values in Data.  I just need to do the same thing with code.
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

BlackBox

  • King Gator
  • Posts: 3770
Re: Changing the order of Values for Registry Keys
« Reply #5 on: October 18, 2013, 12:58:36 PM »
So essentially, you need to identify which task-specific catalog you're targetting, find it in the list of stored data, and re-sort so that your target is popped to the top of the stack so-to-speak... And perhaps when done (modifying the target, etc), restore the original sort-order, no?

"How we think determines what we do, and what we do determines what we get."

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Changing the order of Values for Registry Keys
« Reply #6 on: October 18, 2013, 01:31:53 PM »
So essentially, you need to identify which task-specific catalog you're targetting, find it in the list of stored data, and re-sort so that your target is popped to the top of the stack so-to-speak... And perhaps when done (modifying the target, etc), restore the original sort-order, no?



Exactly.
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

BlackBox

  • King Gator
  • Posts: 3770
Re: Changing the order of Values for Registry Keys
« Reply #7 on: October 18, 2013, 03:53:41 PM »
So essentially, you need to identify which task-specific catalog you're targetting, find it in the list of stored data, and re-sort so that your target is popped to the top of the stack so-to-speak... And perhaps when done (modifying the target, etc), restore the original sort-order, no?



Exactly.

So let's break it out into logical steps....

Define a Method with a single parameter (the key you're wanting to search/modify) that returns bool, and instantiates two Types which will store the original values (one for each registry key & value, and one for the list of keys) in order to 'deserialize' so-to-speak... Then (if == true), query that list to identify your target's index, and evaluate the list into a new Type (also a list of keys), but set the placement you need... When that's done, either modify each actual key to be in the index you need, or simply delete them and 'serialize' your new list of keys to registry so-to-speak.

Just be sure to include the appropriate try / catch / finally to restore the original keys (that you stored from the outset) in the event an exception is raised.
"How we think determines what we do, and what we do determines what we get."

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Changing the order of Values for Registry Keys
« Reply #8 on: October 20, 2013, 09:34:53 AM »
So this is the code that I came up with.  I decided that I really didn't need to sort the entire list but just find the requested catalog and if it wasn't already first in the list then swap it with the first catalog in the list.  There are some refactorings that need to be done.  Right now it is hard coded to only set catalogs for MEP 2014.  I need to programmatically change the release number or at a minimum set it equal to a constant.  Additionally it is hard coded to MvPartContent but that could be modified to work with pipe, duct, conduit, and cabletray by adding a secondary input specifying the domain.
Code - C#: [Select]
  1.         /// <summary>
  2.         /// Places the requested catalog to first in the list of MvParts.
  3.         /// </summary>
  4.         /// <param name="catalogName">The catalog to move to the first position</param>
  5.         /// <returns>ErrorStatus.Ok if the operation succeeded</returns>
  6.         /// <returns>ErrorStatus.KeyNotFound if the requested catalog was not found</returns>
  7.         /// <returns>ErrorStatus.RegistryAccessError if there was a problem accessing the catalogs</returns>
  8.         public static ErrorStatus SetFirstCatalog(string catalogName)
  9.         {
  10.             /// TODO: Automate the release year of the registry location.  Currently this code only works
  11.             /// with Autocad MEP 2014.    Use the system variable ACADVER
  12.             const string subkey1 = "Software\\Autodesk\\AutoCAD\\R19.1\\ACAD-D006:409\\Profiles\\";
  13.             const string subkey2 = "\\Preferences\\AecbCatalog70\\MvPartContent";
  14.             string profile = Application.GetSystemVariable("CPROFILE").ToString();
  15.             string subkey = subkey1 + profile + subkey2;
  16.             string catalogPath = "";
  17.             string pathZero = "";
  18.             bool found = false;
  19.             int foundLocation = 0;
  20.             try
  21.             {
  22.                 // Since the number of catalogs is unknown then we must start at the beginning and work our way through each
  23.                 // catalog.  If the requested catalog is in the first position then we can just break from the search and
  24.                 // return ok.  Otherwise save the first catalog path and once the requested catalog is found the first postion
  25.                 // and the requested catalog position can be switch.  The only position that really matters is the position of
  26.                 // the first catalog.  Other catalog positions are irrelevant.
  27.                 RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree);
  28.                 foreach (string valueName in registryKey.GetValueNames())
  29.                 {
  30.                     if (foundLocation == 0)
  31.                     {
  32.                         if (catalogName.ToUpper() == Path.GetFileNameWithoutExtension(registryKey.GetValue(valueName).ToString().ToUpper()))
  33.                         {
  34.                             return ErrorStatus.OK;
  35.                         }
  36.                         pathZero = registryKey.GetValue(valueName).ToString();
  37.                     }
  38.                     if (catalogName.ToUpper() == Path.GetFileNameWithoutExtension(registryKey.GetValue(valueName).ToString().ToUpper()))
  39.                     {
  40.                         catalogPath = registryKey.GetValue(valueName).ToString();
  41.                         found = true;
  42.                         break;
  43.                     }
  44.                     foundLocation++;
  45.                 }
  46.                 // If we are at this point then we have either searched the entire list of catalogs
  47.                 // and did not find the requested catalog or we did find the requested catalog.
  48.                 if (found && catalogPath != "" && pathZero != "")
  49.                 {
  50.                     registryKey.SetValue("PATH" + foundLocation.ToString(), pathZero);
  51.                     registryKey.SetValue("PATH0", catalogPath);
  52.                     return ErrorStatus.OK;
  53.                 }
  54.                 return ErrorStatus.KeyNotFound;
  55.             }
  56.             // Any exceptions that we catch will mean that there was a Registry Access error
  57.             // We can just return the correct ErrorStatus and have the calling method handle it.
  58.             // If there is a exception then the catalog order will just have to be set manually.
  59.             catch (System.Exception exception)
  60.             {
  61.                 return ErrorStatus.RegistryAccessError;
  62.             }
  63.         }  

The only thing I am really worried about is the code crashing after the first SetValue and before/during the second SetValue.  This situation shouldn't really occur but you never know.  At that point in the code both values exist and if for some reason the code was unable to set a value it would happen on the first attempt.  Should I wrap each SetValue call up in an individual try/catch/finally and in the second call reset the first value?  I am inclined to believe that it is not necessary.  What happens if it fails when trying to reset the first value?  My choice was to just return the RegistryAccessError and let the calling function handle what happens.

Thanks again for all of the help.
« Last Edit: October 20, 2013, 09:54:17 AM by Keith Brown »
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Changing the order of Values for Registry Keys
« Reply #9 on: October 20, 2013, 06:11:38 PM »
I would recommend the use of the
Code - C#: [Select]
  1. HostApplicationServices.Current.UserRegistryProductRootKey
property to insure you don't need to change code in the future.

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Changing the order of Values for Registry Keys
« Reply #10 on: October 20, 2013, 10:54:23 PM »
Nice find Will!  I had it documented in the previous code to find a way to automatically do this but the method you just showed makes it easy.  I added it to my code and wrapped the RegistryKey statements inside a using block and below is the result.  The only refactoring left to do is add a section to make it work for all catalog types and not just MvParts. 
 
Code - C#: [Select]
  1.         public static ErrorStatus SetFirstCatalog(string catalogName)
  2.         {
  3.             const string subkey1 = "\\Preferences\\AecbCatalog70\\MvPartContent";
  4.             string profile = Application.GetSystemVariable("CPROFILE").ToString();
  5.             string subkey = String.Format("Profiles\\{0}{1}", profile, subkey1);
  6.             string catalogPath = "";
  7.             string pathZero = "";
  8.             bool found = false;
  9.             int foundLocation = 0;
  10.  
  11.             try
  12.             {
  13.                 RegistryKey registryKey1 = Registry.CurrentUser.OpenSubKey(HostApplicationServices.Current.UserRegistryProductRootKey, RegistryKeyPermissionCheck.ReadWriteSubTree);
  14.                 using (registryKey1)
  15.                 {
  16.                     RegistryKey registryKey2 = registryKey1.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree);
  17.                     using (registryKey2)
  18.                     {
  19.                         foreach (string valueName in registryKey2.GetValueNames())
  20.                         {
  21.                             if (foundLocation == 0)
  22.                             {
  23.                                 if (catalogName.ToUpper() == Path.GetFileNameWithoutExtension(registryKey2.GetValue(valueName).ToString().ToUpper()))
  24.                                 {
  25.                                     return ErrorStatus.OK;
  26.                                 }
  27.                                 pathZero = registryKey2.GetValue(valueName).ToString();
  28.                             }
  29.                             if (catalogName.ToUpper() == Path.GetFileNameWithoutExtension(registryKey2.GetValue(valueName).ToString().ToUpper()))
  30.                             {
  31.                                 catalogPath = registryKey2.GetValue(valueName).ToString();
  32.                                 found = true;
  33.                                 break;
  34.                             }
  35.                             foundLocation++;
  36.                         }
  37.                         if (found && catalogPath != "" && pathZero != "")
  38.                         {
  39.                             registryKey2.SetValue("PATH" + foundLocation.ToString(), pathZero);
  40.                             registryKey2.SetValue("PATH0", catalogPath);
  41.                             return ErrorStatus.OK;
  42.                         }
  43.                         return ErrorStatus.KeyNotFound;
  44.                     }
  45.                 }
  46.             }
« Last Edit: October 20, 2013, 10:57:56 PM by Keith Brown »
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Changing the order of Values for Registry Keys
« Reply #11 on: October 21, 2013, 08:02:36 AM »
May I ask what methods your using that only perform on the first Catalog in the list?  Are you using Catalogs with duplicate parts?
Revit 2019, AMEP 2019 64bit Win 10

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Changing the order of Values for Registry Keys
« Reply #12 on: October 21, 2013, 08:42:59 AM »
The first catalog in the list is the dominant catalog so to speak.  If I was to use a line of code like this:
 
Code - C#: [Select]
  1. CatalogManager manager = new CatalogManager();
  2. DataQuery queryForPartsList = new DataQuery
  3. {
  4.      Domain = Domain.MultiViewPartComponent,
  5.      CatalogId = manager.CatalogID(Domain.MultiViewPartComponent)
  6. };

It would restrict the query to the first catalog in the list without knowing the CatalogID.  If i was to remove the CatalogID from the search query then it would search all catalogs.  CatalogID is a readonly property and the only way that I can see to get the CatalogID is to get the name of the catalog, then the path of the catalog and then parse the catalog.apc xml file for the ID.
 
I am working on an app that is an equipment manager of sorts.  It is tool palette based and allows you to insert equipment based on the type, manufacturer, model, and size.  I have that part working pretty good but only as long as the search for parts is restricted to my catalog.  I have created a strongly enforced directory and naming structure for the parts that allow me to get to the part I need pretty quick based on the type, manufacturer, and model number.  By using the folders and the path of the part I do not need to use the Expanded Tables  for the parts but can find what I need using the PartLookUp which just searches the catalog.apc file.  Once I have found the individual part based on Manufacturer and Model number I then use the Expanded Tables to get the part size.  It is a much smaller list to search from.
 
Once you have found the part in the catalog, the app then prompts for you to place the part (with a jig) and then after placing will add the appropriate property sets, automatically prompt for you to place a tag (with a jig) and then create a 3D access zone around the part.  If available the zone will be created based on dimensions that live on the part and will only need minimal interaction from the user.  (the ceiling height).  I have successfully created the zone as a 3D block but I am working on creating the zone as a mvblock and anchoring it to the mvpart so that it will move and adjust when the mvpart does.
 
Being able to manipulate the registry of catalogs also allows me to use the autoloader to distribute the catalog(s).  It is just a small jump from sorting a catalog to the top of the list to creating a new catalog and placing it at the top of the list.
 
I am contemplating about using a duplicate GUID of the OOTB catalog for my catalog so it will restrict the masses from using "non-certified" parts.  This will effectively remove the OOTB catalog from the program while still keeping it in the list causing a lot of head scratches by the lesser informed.  We create fabrication drawings from MEP so we need to only use "certified" parts in our drawings and this is one method to help achieve that.
 
Not sure if I answered your question or not but in the long run being able to set the first catalog in the list will just make things a lot easier.
 
« Last Edit: October 21, 2013, 08:53:17 AM by Keith Brown »
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Changing the order of Values for Registry Keys
« Reply #13 on: October 21, 2013, 09:24:12 AM »
That does answer my question but I must ask another. Can you not search the catalog using one of your custom fields that you're using to create the box?

We do something similar here but I use PartType, PartSubType, Material, and Custom fields to query the catalog and come back with specific parts for use in my program.  Seems to me, that is a lot easier than moving catalogs around every time you need to query some parts. 
Revit 2019, AMEP 2019 64bit Win 10

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Changing the order of Values for Registry Keys
« Reply #14 on: October 21, 2013, 09:40:00 AM »
In order to search those custom fields you will need to do an expanded table search.  I am searching the catalog based on a datapartlookup.  The difference being is that a datapartlookup will only search the catalog.apc file.  The expanded table search will need to delve into the individual part xml files.
 
So in answer to your question, yes I can search that way and I actually do once I set the part type, manufacturer, and model.  I then search the model for a particular part size which is not available in the catalog.apc file but only in the expanded table.  In order to speed things up, I cache the type, manufacturer, model combo on load of the program.  If their is any change (through the use of the app) then it will recache the equipment.  I also have a manual "regenerate" button that will recache the equipment.  My thoughts on doing it this way is to make the selection of the part as fast and as easy as possible.  The part add dialogs can be cumbersome in trying to find the part you are looking for sometimes.  This is my attempt to make it easier for everyone.  When choosing a part the manufacturer, model and size is almost always known.
 
Additionally, by doing it in code, it will allow me to perform operations on the part after insertion without using cumbersome event handlers.
 
Placing the catalog at the top of the list makes all of this a lot easier.  I can quickly filter out unwanted parts and remove duplicate catalogs/parts quickly and easily with one API call.  Now I do not have to worry about equipment that does not follow the strict guidelines for creation.
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013