Menu

#207 Add RegDeleteTree function to TRegKey

8
pending
1
2022-12-29
2022-04-16
No

TRegKey encapsulates a lot of the functions for manipulation the Windows registry, but some are missing, for example RegDeleteTree. Note that there is the function NukeKey, which does the same job, but manually traverses the subkey tree. That function most likely was created before RegDeleteTree was available in the Windows API.

Other functions that are missing are RegCopyTree and RegRenameKey.

Related

Commit: [r5968]
Wiki: OWLNext_Roadmap_and_Prereleases

Discussion

  • Ognyan Chernokozhev

    New methods added in [r5958].

     
    👍
    1

    Related

    Commit: [r5958]

  • Ognyan Chernokozhev

    • status: open --> pending
     
  • Vidar Hasfjord

    Vidar Hasfjord - 2022-04-19

    Hi Jogy, I've had a brief look at [r5958]:

    • I would prefer exceptions (TXRegistry) for error handling, in line with my recent overhaul of TRegKey in version 7. See [feature-requests:#151].
    • Doxygen documentation should consistently link to the Windows API documentation (see documentation of other function that does this).
    • I propose NukeKey is deprecated and moved to OWL5_COMPAT mode. It no longer makes sense, now that we have DeleteTree.

    PS. Regarding your use of DeleteTree in FolderSize [r5959], where you open the parent "shell" key, you will get an exception (since 7.0) if it is not present, since you pass TRegKey::NoCreate to the constructor, so there is no point in testing for key validity in the if-statement. If you don't want the exception, you can preferably use the new GetSubKey function (returns std::optional). Note that I here assume exceptions are used for error handling in DeleteTree.

    //
    // Removes any FolderSize key under the given base.
    // Returns true if a key was present, false if not.
    // Throws TXRegistry on errors.
    //
    auto UnregisterAssociation(const tstring& base) -> bool
    {
      if (auto shell = TRegKey::GetClassesRoot()->GetSubKey(base + _T("\\shell")))
      {
        const auto subkey = _T("OWLNextFolderSize");
        if (shell->HasSubKey(subkey)
        {
          shell->DeleteTree(subkey);
          return true;
        }
      }
      return false;
    }
    

    Alternatively, if you want to treat a missing key as an error, you can simply do:

    //
    // Removes the FolderSize key under the given base.
    // Throws TXRegistry if the key is missing or any other error occurs.
    //
    void UnregisterAssociation(const tstring& base)
    {
      TRegKey{TRegKey::GetClassesRoot(), base + _T("\\shell"), KEY_ALL_ACCESS, TRegKey::NoCreate}
        .DeleteTree(_T("OWLNextFolderSize"));
    }
    

    PS. There is a spelling error ("Regsitry") in the error message in OwlMain, which by the way should be sent to "cerr" not "cout".

     

    Related

    Commit: [r5958]
    Commit: [r5959]
    Feature Requests: #151


    Last edit: Vidar Hasfjord 2022-04-19
    • Ognyan Chernokozhev

      Great suggestions. Please review latest commits for the implementation changes.

      PS. There is a spelling error ("Regsitry") in the error message in OwlMain, which by the way should be sent to "cerr" not "cout".

      I discovered that when invoked from the command line, neither cerr or cout display anything. Most likely because it is built as a GUI application.

      So I changed it to display all the errors in message boxes.

       
  • Vidar Hasfjord

    Vidar Hasfjord - 2022-04-20
    • labels: API --> API, TRegKey
     
  • Vidar Hasfjord

    Vidar Hasfjord - 2022-04-23

    @jogybl wrote:

    Please review latest [TRegKey] commits [r5961]-[r5963].

    I've had a brief look, and it looks good! Some comments:

    • Mark NukeKey as [[deprecated]] (C++14 attribute).
    • Local variable key, used for diagnostics in lambda tryNukeKey in "richeditor/App.cpp", should be marked [[maybe_unused]] to avoid a warning in release build mode.
    • In "dockingex.cpp", "harbourex.cpp", you do not have to call "c_str" on the argument to DeleteTree, since the latter has an overload that has tstring as parameter.

    So [in "foldersize/main.cpp"] I changed it to display all the errors in message boxes.

    • There are bugs in your string additions; you add pointers rather than concatenate strings. Tip: Use to_tstring to convert path to tstring, and simplify your code by using a lambda to display the message box, to which you can simply pass tstring.
    • Do not use NULL; use nullptr instead.
     

    Related

    Commit: [r5961]
    Commit: [r5963]

    • Ognyan Chernokozhev

      There are bugs in your string additions; you add pointers rather than concatenate strings.

      Which one? In all the cases when displaying a folder name, one of the operands is a tstring, and adding a pointer to it should work.

      But the code looks very clunky and unreadable this way, so I may go to using a stringstream and displaying it.

       
  • Vidar Hasfjord

    Vidar Hasfjord - 2022-04-24

    @jogybl wrote:

    [string addition errors] Which one?

    Microsoft Visual Studio Community 2022 (version 17.1.5) gives me the following compilation errors when building the Debug | x64 configuration:

    1>Build FAILED.
    1>
    1>C:\OWLNext\trunk\examples\foldersize\foldersize\main.cpp(443,38): error C2679: binary '+': no operator found which takes a right-hand operand of type 'const std::filesystem::path::value_type *' (or there is no acceptable conversion)
    1>C:\OWLNext\trunk\examples\foldersize\foldersize\main.cpp(443,38): error C2660: 'MessageBox': function does not take 3 arguments
    1>C:\OWLNext\trunk\examples\foldersize\foldersize\main.cpp(449,79): error C2678: binary '+': no operator found which takes a left-hand operand of type 'const std::filesystem::path::value_type *' (or there is no acceptable conversion)
    1>C:\OWLNext\trunk\examples\foldersize\foldersize\main.cpp(449,128): error C2660: 'MessageBox': function does not take 3 arguments
    1>C:\OWLNext\trunk\examples\foldersize\foldersize\main.cpp(472,38): error C2679: binary '+': no operator found which takes a right-hand operand of type 'const std::filesystem::path::value_type *' (or there is no acceptable conversion)
    1>C:\OWLNext\trunk\examples\foldersize\foldersize\main.cpp(472,38): error C2660: 'MessageBox': function does not take 3 arguments
    1>C:\OWLNext\trunk\examples\foldersize\foldersize\main.cpp(478,79): error C2678: binary '+': no operator found which takes a left-hand operand of type 'const std::filesystem::path::value_type *' (or there is no acceptable conversion)
    1>C:\OWLNext\trunk\examples\foldersize\foldersize\main.cpp(478,128): error C2660: 'MessageBox': function does not take 3 arguments
    1>    0 Warning(s)
    1>    8 Error(s)
    

    In all the cases when displaying a folder name, one of the operands is a tstring, and adding a pointer to it should work.

    I think the problem is that you call path::c_str, which return type is const path::value_type*, where value_type is wchar_t, regardless of character set build mode. Replace the path::c_str calls by to_tstring, and things should be fine. You can then even remove the tstring constructor calls around the string literals. Then replace the MessageBox calls by a local lambda, so that you do not have to call c_str. It also lets you avoid repeating the other arguments.

    const auto showMessage = [](const tstring& m, bool isError = false)
    {
      MessageBox(nullptr, m.c_str(), _T("FolderSize"),
        MB_OK | (isError ? MB_ICONERROR : MB_ICONINFORMATION)
    };
    
     
    • Ognyan Chernokozhev

      Ah yes, I was building only in Unicode, as there are some file and folder names with non-english letters in their names on my machine that cannot be processed by std::filesystem if built for ANSI.

       
  • Vidar Hasfjord

    Vidar Hasfjord - 2022-04-25

    @jogybl wrote:

    Ah yes, I was building only in Unicode [...bugs fixed in] [r5967].

    Compiles fine now!

    Here are some coding tips:

    Don't repeat yourself (DRY principle). Often you use if-statements to branch the thread of execution, when both branches are just setting up the same call, with only slightly different arguments. In particular for this code; branched calls to "showMessage", when the code logic only computes the message to display.

    Current code:

    if (x.GetErrorCode() == ERROR_ACCESS_DENIED)
      showMessage(_T("Registry access denied"), true);
    else
      showMessage(x.why(), true);
    

    Refactored to one call, only computing the message argument:

    const auto e = (x.GetErrorCode() == ERROR_ACCESS_DENIED) ?
      _T("Registry access denied") : x.why();
    showMessage(e, true);
    

    As another example, the code sections for options "-export-csv" and "-export-tab-delimited" are almost identical (copy-and-paste probably), except for the delimiter ("\t" or ","). The code can be abstracted into a local lambda taking the delimiter as a parameter.

    Regarding formatting, ideally do not use space within braces of uniform initialisation and initializer lists.

    E.g. folder = path{params[2]}, and not folder = path{ params[2] }.

    The Visual Studio editor can now (finally!) tell the difference between initialisers and function bodies, so you can configure the automatic formatting accordingly.

    Also, configure your editor to not indent the contents of namespaces (generally unhelpful indentation).

    Tip: In Visual Studio, select editor settings can be saved and easily recalled for working on projects with different formatting conventions (Tools | Import and Export Settings).

     

    Related

    Commit: [r5967]


    Last edit: Vidar Hasfjord 2022-04-25

Anonymous
Anonymous

Add attachments
Cancel





MongoDB Logo MongoDB