Menu

#498 TOcRegistrar constructor ignores options amNoRegValidate and amQuietReg (regression in 7.0)

7
closed
1
2026-04-03
2021-06-08
No

When registering an OLE server without appropriate (admin) rights, TOcRegistrar throws "TRegKey::TRegKey: Init failed", even if the "-QuietReg" option is supplied in the command line, or the preselectedOptions parameter of the TOcRegistrar::TOcRegistrar() call has the amNoRegValidate flag set.

PS. Also see related issue "TOcRegistrar constructor throws exception when registry access fails (regression in 6.40)" [bugs:#376].

Related

Bugs: #376
Discussion: State of OCFNext — time to discontinue?
Wiki: OWLNext_Stable_Releases

Discussion

1 2 3 > >> (Page 1 of 3)
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-10
    • labels: --> OCF
    • summary: TOcRegistrar constructor ignores options amNoRegValidate and amQuietReg --> TOcRegistrar constructor ignores options amNoRegValidate and amQuietReg (regression in 7.0)
     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-10

    This issue is a regression caused by [r4865], which introduced new exception behaviour for TRegKey constructors, in line with modern C++ coding style. See [feature-requests:#151].

    Proposed resolution: OCF should be updated to handle exceptions in TRegKey.

     

    Related

    Commit: [r4865]
    Feature Requests: #151


    Last edit: Vidar Hasfjord 2021-06-11
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-11

    Hi Erwin,

    I've had a brief look at the OCF code, and it looks to me the problem is in the catch block in TAppDescriptor::RegisterServer:

    catch (TXBase& ) {
      TRegKey::GetClassesRoot().SetValue(_T("OcErr_RegServer"),REG_SZ, (uint8*)(LPCTSTR)AppClassId, 0);
      if (!(Options & amQuietReg))
        throw;
    }
    

    I guess it is the attempt to put that "OcErr_RegServer" value (presumably as an error indicator) into the registry here that causes an exception which escapes. Is this what you see?

    If so, the suppression of this exception may be enough to solve the issue:

    catch (const TXBase& x)
    {
      TRACEX(OcfDll, 0, _T("TAppDescriptor::RegisterServer: Exception: ") << x.why());
      try
      {
        TRegKey::GetClassesRoot().SetValue(_T("OcErr_RegServer"), REG_SZ, (uint8*) (LPCTSTR) AppClassId, 0);
        // Simplification: SetValue(_T("OcErr_RegServer"), tstring{AppClassId});
      }
      catch (...)
      {
        WARNX(OcfDll, true, 0, _T("TAppDescriptor::RegisterServer: Failed to set HKEY_CLASSES_ROOT\\OcErr_RegServer."));
      }
      if (!(Options & amQuietReg))
        throw;
      WARNX(OcfDll, true, 0, _T("TAppDescriptor::RegisterServer: Exception suppressed."));
    }
    

    Please test.

    However, there may be other code in OCF that assumes TRegKey will not throw, so a broader review may be needed.

     
  • Erwin Lotter

    Erwin Lotter - 2021-06-12

    Hi Vidar,

    you have correctly identified the source of the exception.
    The solution should work, but doesn't because the amQuietReg Flag is not set in 'Options' despite "-QuietReg" is given in the command line. It is set, however, if I put it into the 'preselectedOptions' parameter of the TOcRegistrar constructor. Strange.
    In addition: Shouldn't 'amNoRegValidate' also suppress exceptions?

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-12

    Hi Erwin,

    Thanks for the feedback.

    The solution should work, but doesn't because the amQuietReg Flag is not set in 'Options' despite "-QuietReg" is given in the command line. It is set, however, if I put it into the 'preselectedOptions' parameter of the TOcRegistrar constructor. Strange.

    Note that options are processed in sequence in TAppDescriptor::ProcessCmdLine, so "-QuietReg" needs to be passed before "-RegServer".

    Ideally, this should be made more robust, by allowing any order.

    Shouldn't 'amNoRegValidate' also suppress exceptions?

    As fas as I can see amNoRegValidate is only used at the end of TAppDescriptor::ProcessCmdLine, where if this option is set, then "normal registry update" is skipped:

    // Perform normal registry update if no other registry option was specified
    //
    if (!(Options & (amRegServer | amUnregServer | amNoRegValidate)))
      RegisterServer(0);
    

    RegisterServer is only called within ProcessCmdLine — in this line, or as a response to the amRegServer option (processed earlier in the same function) — so the question then becomes whether the option amNoRegValidate should disable exceptions if amRegServer is given (and if so, it would have to be specified on the command line before the latter, to be effective).

    Also note that, unlike RegisterServer, TAppDescriptor::UnregisterServer has no exception handling, so it may now throw TXRegistry, regardless of the given options (amQuietReg and amNoRegValidate).

    The documentation is thin, so I am not sure what the intended behaviour is. Feel free to take control and make sense of it!

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-12
    • labels: OCF --> OCF, Registry
     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-12
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -1 +1,3 @@
     When registering an OLE server without appropriate (admin) rights, TOcRegistrar throws &#34;TRegKey::TRegKey: Init failed&#34;, even if the &#34;-QuietReg&#34; option is supplied in the command line, or the preselectedOptions parameter of the TOcRegistrar::TOcRegistrar() call has the amNoRegValidate flag set.
    +
    +PS. Also see related issue &#34;TOcRegistrar constructor throws exception when registry access fails (regression in 6.40)&#34; [bugs:#376].
    
     

    Related

    Bugs: #376

  • Erwin Lotter

    Erwin Lotter - 2021-06-12

    Note that options are processed in sequence in TAppDescriptor::ProcessCmdLine, so "-QuietReg" needs to be passed before "-RegServer".

    Ok, in reverse order "-QuietReg" is recognized.

    Ideally, this should be made more robust, by allowing any order.

    Absolutely. It's REALLY unusual to be forced to specify a detail-option before the option itself.

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-12

    Absolutely. It's REALLY unusual to be forced to specify a detail-option before the option itself.

    You can allow any order simply by processing the command line in two passes, I would think: The first pass records all the options, while the second acts on them.

    You can then also do some sanity checking. For example, specifying more than one of options "RegServer", "UnregServer" and "NoRegValidate" should be an error, I think. If I understand the code correctly, then "NoRegValidate" is intended to be used only to suppress the default call to RegisterServer (the "normal registry update", as the code comment says) when none of options "RegServer" and "UnregServer" are given.

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-12
    • labels: OCF, Registry --> OCF, Registry, Regression
     
  • Erwin Lotter

    Erwin Lotter - 2021-06-13

    You can allow any order simply by processing the command line in two passes, I would think: The first pass records all the options, while the second acts on them.

    Right. Do you know whether TAppDescriptor::ProcessCmdLine() is called from other places too?

    specifying more than one of options "RegServer", "UnregServer" and "NoRegValidate" should be an error, I think

    In case someone likes to unregister and register again in one run, this possibility might be desired. So I wouldn't like to risk too much if it isn't not really worth it.

     

    Last edit: Erwin Lotter 2021-06-13
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-13

    Hi Erwin,

    Here is a patch that rewrites TAppDescriptor::ProcessCmdLine to allow any order of "QuietReg" and other flag options. It also detects if multiple registry options have been specified and warns (should perhaps generate an error?). And I have added documentation and code comments.

    Beware: Not tested!

    PS. By the way, why is 0 passed for the language ID in the call to RegisterServer at the end of the function? Why isn't AppLang passed, like for the processed options?

    Edit: Patch removed. See later posts for later patch.

     

    Last edit: Vidar Hasfjord 2021-06-15
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-13

    Still more simply, we could resort the option table (if I don't miss something). Putting the items without actions on top should do the job.

    I don't think that will work, since the processing happens in the order specified on the command line (regardless of lookup table order). My patch delays the actions until the whole command line has been parsed. It also delays the modification of the "cmdLine" parameter, in case there are exceptions.

    Do you know whether TAppDescriptor::ProcessCmdLine is called from other places too?

    Using "Find All References" and "Find All" in Visual Studio, I see it is called from TAppDescriptor constructors, as well as from TRegistrar::ProcessCmdLine. The latter is called from DllRegisterCommand, which in turn is called from DllRegisterServer and DllUnregisterServer.

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-13

    Note: My patch has an issue. The AppLang argument passed to the action functions is not updated by the "Language" option, since the action functions and their arguments are bound before the actions are performed.

    Edit: Disregard the note above. The AppLang argument is accessed through the "this" pointer, so it will not be bound until the actual call of the action function, i.e. it will use the value at the time of the call.

    Note that order of the "Language" option still matters: The "Language" option will have to be passed before "RegServer", for the latter to use the correct language ID. Perhaps the actions should be sorted, with the language option first, if present.

    Why is AppLang passed at all to RegisterServer, UnregisterServer and MakeTypeLib? After all, it is a member, and these functions are only called from ProcessCmdLine (nowhere else). Even SetLangId has "prevLang" as a dummy parameter to allow AppLang to be passed for no reason.

     

    Last edit: Vidar Hasfjord 2021-06-13
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-13

    Hi Erwin,

    Here is an alternative patch that addresses the strange API issues mentioned in my last post by removing superfluous parameters. It also allows the "Language" option to be specified in any order on the command line.

    Beware: Not tested!

    Edit: Added further documentation and code comments. Also made use of SetOption and IsOptionSet, rather than direct bit fiddling.

    //
    /// Processes known options on the command line.
    ///
    /// Registry options (one allowed):
    /// 
    /// - RegServer [&lt;regfile&gt;]
    /// - UnregServer/Unregister
    /// - NoRegValidate
    /// 
    /// Auxiliary options:
    /// 
    /// - Automation
    /// - Debug
    /// - Embedding
    /// - Language &lt;langid&gt;
    /// - QuietReg
    /// - TypeLib [&lt;name&gt;]
    /// 
    /// On return, the given \p cmdLine will have the processed options removed.
    /// 
    /// \note If no registry option (\p RegServer, \p UnregServer or \p NoRegValidate) is specified on
    /// the command line, then validation of the registry entries will be performed. If validation
    /// fails, then the registry is updated as if registration was specified. Pass \p NoRegValidate to
    /// suppress this behaviour.
    /// 
    /// \exception TXRegistry may be thrown, if access to the registry is denied, unless the option
    /// \p QuietReg is specified, in which case such exceptions will be suppressed.
    /// 
    /// \exception TXOle may be thrown on failure to create a type library, unless the option
    /// \p QuietReg is specified, in which case such exceptions will be suppressed.
    //
    void TAppDescriptor::ProcessCmdLine(owl::tstring& cmdLine)
    {
      struct
      {
        TOcAppMode Flag;
        LPCTSTR Option;
        bool IsAction;
      }
      const optionTbl[] =
      {
        // Registry options (one allowed):
        //
        {amRegServer, _T("RegServer"), true},
        {amUnregServer, _T("UnregServer"), true},
        {amUnregServer, _T("Unregister"), true},
        {amNoRegValidate, _T("NoRegValidate"), false},
    
        // Auxiliary options:
        //
        {amAutomation, _T("Automation"), false},
        {amDebug, _T("Debug"), false},
        {amEmbedding, _T("Embedding"), false},
        {amLangId, _T("Language"), false},
        {amQuietReg, _T("QuietReg"), false},
        {amTypeLib, _T("TypeLib"), true},
      };
    
      const auto isRegistryOptionSet = [](uint32 flags)
      { 
        return (flags & (amRegServer | amUnregServer | amNoRegValidate)) != 0; 
      };
    
      // Parse the command line.
      //
      using TAction = pair<TOcAppMode, tstring>; // Option flag and argument.
      auto actions = vector<TAction>{};
      auto cl = TCmdLine{cmdLine};
      while (cl.GetTokenKind() != TCmdLine::Done)
        switch (cl.GetTokenKind())
        {
        case TCmdLine::Option:
          {
            const auto option = cl.GetToken();
            const auto i = find_if(begin(optionTbl), end(optionTbl), [&option](const auto& entry)
              { return _tcsicmp(entry.Option, option.c_str()) == 0; });
            if (i == end(optionTbl))
            {
              WARNX(OcfDll, true, 1, _T("TAppDescriptor::ProcessCmdLine: Unknown option: ") << option);
              cl.NextToken(false); // Skip this option, but leave it on the command line.
              break;
            }
    
            // Record this option.
            //
            const auto hasMultipleRegistryOptions = (isRegistryOptionSet(Options) && isRegistryOptionSet(i->Flag));
            WARNX(OcfDll, hasMultipleRegistryOptions, 0, _T("TAppDescriptor::ProcessCmdLine: Multiple registry options are specified."));
            SetOption(i->Flag, true);
            cl.NextToken(true); // Remove the option from the command line.
            const auto arg = (cl.GetTokenKind() == TCmdLine::Value) ? cl.GetToken() : tstring{};
            if (i->IsAction)
              actions.push_back(make_pair(i->Flag, arg));
            else if (i->Flag == amLangId && !arg.empty())
              SetLangId(arg);
            while (cl.GetTokenKind() == TCmdLine::Value)
              cl.NextToken(true); // Remove all arguments following the option.
            break;
          }
        default:
          cl.NextToken(); // Skip.
          break;
        }
    
      // Set single use, if this is an automated exe server.
      //
      if (IsOptionSet(amAutomation | amExeModule))
        SetOption(amSingleUse, true);
    
      // Perform specified actions.
      //
      for (const auto& a : actions)
      {
        const auto arg = a.second.empty() ? nullptr : a.second.c_str();
        switch (a.first)
        {
        case amRegServer: RegisterServer(arg); break;
        case amUnregServer: UnregisterServer(); break;
        case amTypeLib: MakeTypeLib(arg); break;
        default: CHECKX(false, _T("TAppDescriptor::ProcessCmdLine: Unexpected action flag: ") + to_tstring(a.first));
        }
      }
      cmdLine = cl.GetLine(); // Update the command line with processed options removed.
    
      // Perform registry validation, if no registry option was specified.
      //
      // Note: RegisterServer executes in validation mode when amRegServer is not set. If validation
      // fails, the registry will be updated, as if registration was specified. See the implementation
      // for details. The registry option NoRegValidate can be specified on the command line to
      // suppress this validation call.
      //
      if (!isRegistryOptionSet(Options))
        RegisterServer(); // Validate registry entries.
    }
    

    Edit: Patch file removed. See later posts for full patch.

     

    Last edit: Vidar Hasfjord 2021-06-15
  • Erwin Lotter

    Erwin Lotter - 2021-06-13

    Can I apply this patch without having the source as a git repo? (I only have the download from OWLMaker.)

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-13

    Can I apply this patch without having the source as a git repo?

    The patch was created with TortoiseSVN against [r5511], so I recommend creating a Subversion working copy (see our installation guide), and apply it using Subversion tools (TortoiseSVN). But you may be able to apply it using other patch tools, or even manually by hand — it isn't very complex.

     

    Related

    Commit: [r5511]

  • Erwin Lotter

    Erwin Lotter - 2021-06-13

    Ok, I can give -quietreg before and after -regserver to suppress the exception. However, the server does not shut down after registration as it should.

    "-quietreg -unregserver" throws "TRegKey::GetSubKey: RegOpenKeyEx failed"

    "-typelib xyz.tlb" crashes in MakeTypeLib() in line TRegKey::GetClassesRoot().DeleteKey(_T("OcErr_Typelib"));

    Exception thrown at...: Access violation reading location 0x0000001C.

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-13

    Hi Erwin, good work on the testing, and thanks for the feedback!

    It looks like you have identified key spots in the code that previously failed silently, but now will need proper error/exception handling. Hopefully, they should be easy to patch.

    To guard against access denial exceptions escaping, TAppDescriptor::MakeTypeLib and UnregisterServer will need a catch block similar to RegisterServer it seems. Further error handling may be needed where the code previously may have just failed silently and proceeded as if nothing happened.

    Ideally, some of the code that should still work fine, could be improved as well. For example, the code in UnregisterServer will create keys if they do not exist, just to try to delete their subkeys, which makes no sense. For example:

    if (LibGuidOffset)
      TRegKey(TRegKey::GetClassesRoot(), _T("TypeLib"))
        .DeleteKey(AppClassId[LibGuidOffset]);
    

    This code will create "HKEY_CLASSES_ROOT\TypeLib", if it does not exist, after which it then tries to delete a non-existent subkey. This does no harm, but it is doing superfluous work by modifying the registry unnecessarily. And it isn't very clear what the code does in case the "TypeLib" key does not exist. A better coding style may be:

    if (LibGuidOffset)
      if (auto typeLibKey = TRegKey::GetClassesRoot().GetSubkey(_T("TypeLib")))
        typeLibKey->DeleteKey(AppClassId[LibGuidOffset]);
    

    Note that, if the subkey does not exist, DeleteKey returns an error code here, which is ignored. It does not throw exceptions. However, an even clearer coding style, would be to write:

    const auto deleteSubkeyIfExists = [](TRegKey& key, LPCTSTR subkey)
    {
      if (key.HasSubkey(subkey))
        key.DeleteKey(subkey);
    };
    
    if (LibGuidOffset)
      if (auto typeLibKey = TRegKey::GetClassesRoot().GetSubkey(_T("TypeLib")))
        deleteSubkeyIfExists(*typeLibKey, AppClassId[LibGuidOffset]);
    

    This code would work, even if DeleteKey at some point should be modernised to use exceptions for its error reporting.

     

    Last edit: Vidar Hasfjord 2021-06-14
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-14

    Hi Erwin,

    Here is another patch (applicable to trunk [r5511]), that now implements similar exception handling for RegisterServer, UnregisterServer and MakeTypeLib, which are the action functions called by TAppDescriptor::ParseCmdLine. They now all handle TXBase exceptions the same way, suppressing them if the flag amQuietReg is set, as discussed.

    I have also slightly rewritten UnregisterServer, as discussed earlier, so that the registry manipulating code is clear and exception safe for the future, while no longer creating non-existent keys unnecessarily.

    Documentation has also been added for these functions, with a more complete comment on exceptions and the effect of the flag amQuietReg.

    Beware: Not tested!

    //
    /// Removes all application and type library information from the registry.
    /// 
    /// \exception TXRegistry may be thrown, if access to the registry is denied, unless the option
    /// \ref amQuietReg is specified, in which case such exceptions will be suppressed. Note that
    /// \ref amQuietReg will also suppress any other exception derived from \ref TXBase.
    //
    void TAppDescriptor::UnregisterServer()
    {
      try
      {
        _TCHAR guidStr[40];
        if (AppProgId) // Don't attempt unregister app if no progid reg key.
        {          
          TRegItem debugItem = {"debugclsid", {guidStr}};
          TRegItem* debugInfo = nullptr; // Init to no debug registration.
          if (DebugGuidOffset)
          {
            // Setup reg item for debug reg.
            //
            TClassId debugClsid(AppClassId[DebugGuidOffset]);
            _tcscpy(guidStr, debugClsid);
            debugInfo = &debugItem;
          }
    
          const auto deleteSubkeyIfExists = [](TRegKey& key, LPCTSTR subkey)
          {
            if (key.HasSubkey(subkey))
              key.DeleteKey(subkey);
          };
    
          ocf::OcUnregisterClass(RegInfo, debugInfo); // Unregister app.
          if (LibGuidOffset)
            if (auto typeLibKey = TRegKey::GetClassesRoot().GetSubkey(_T("TypeLib")))
              deleteSubkeyIfExists(*typeLibKey, AppClassId[LibGuidOffset]);
          if (ClassCount > 0)
            if (auto interfaceKey = TRegKey::GetClassesRoot().GetSubkey(_T("Interface")))
              for (auto i = 0; i != ClassCount; ++i)
                deleteSubkeyIfExists(*interfaceKey, AppClassId[ClassList[i].GuidOffset]);
        }
    
        // Remove templates from registration file as needed.
        //
        int linkGuidIndex = 0;
        for (const TRegLink* link = LinkHead; link; link = link->GetNext())
        {
          TRegList& regList = link->GetRegList();
          if (regList["progid"])
          {
            if (!regList["clsid"]) // Check if GUID needs to be computed.
            {
              TClassId linkClsid(GetLinkGuid(linkGuidIndex++));
              _tcscpy(guidStr, linkClsid);
              TRegItem clsItem = {"clsid", {guidStr}};
              ocf::OcUnregisterClass(regList, &clsItem);
            }
            else
              ocf::OcUnregisterClass(regList);
          }
        }
      }
      catch ([[maybe_unused]] const TXBase& x)
      {
        TRACEX(OcfDll, 0, _T("TAppDescriptor::UnregisterServer: Exception: ") << x.why());
        if (!IsOptionSet(amQuietReg))
          throw;
        WARNX(OcfDll, true, 0, _T("TAppDescriptor::UnregisterServer: Exception suppressed."));
      }
    }
    
    //
    /// Writes a type library description for this application to the given file.
    /// 
    /// \exception TXRegistry may be thrown, if access to the registry is denied, unless the option
    /// \ref amQuietReg is specified, in which case such exceptions will be suppressed. Note that
    /// \ref amQuietReg will also suppress any other exception derived from \ref TXBase.
    //
    void TAppDescriptor::MakeTypeLib(LPCTSTR typeLibName)
    {
      if (ClassCount <= 0) return;
      try
      {
        TCHAR fullPath[_MAX_PATH];
        TCHAR exeDrive[_MAX_DRIVE];
        TCHAR exeDir[_MAX_DIR];
        TCHAR exeFName[_MAX_FNAME];
        TCHAR exeExt[_MAX_EXT];
        TCHAR libDrive[_MAX_DRIVE];
        TCHAR libDir[_MAX_DIR];
        TCHAR libFName[_MAX_FNAME];
        TCHAR libExt[_MAX_EXT];
        ::GetModuleFileName(*Module, fullPath, COUNTOF(fullPath));
        _tsplitpath(typeLibName ? typeLibName : _T(""), libDrive, libDir, libFName, libExt);
        _tsplitpath(fullPath, exeDrive, exeDir, exeFName, exeExt);
        _tmakepath(fullPath,
          libDrive[0] ? libDrive : exeDrive,
          libDir[0] ? libDir : exeDir,
          libFName[0] ? libFName : exeFName,
          libExt[0] ? libExt : _T("OLB"));
        WriteTypeLibrary(AppLang, fullPath); // Note: Implemented in "typelib.cpp".
        TRegKey::GetClassesRoot().DeleteKey(_T("OcErr_Typelib"));
      }
      catch ([[maybe_unused]] const TXBase& x)
      {
        TRACEX(OcfDll, 0, _T("TAppDescriptor::MakeTypeLib: Exception: ") << x.why());
        try
        {
          TRegKey::GetClassesRoot().SetValue(_T("OcErr_Typelib"), tstring{AppClassId});
        }
        catch (...)
        {
          WARNX(OcfDll, true, 0, _T("TAppDescriptor::MakeTypeLib: Failed to set HKEY_CLASSES_ROOT\\OcErr_Typelib."));
        }
        if (!IsOptionSet(amQuietReg))
          throw;
        WARNX(OcfDll, true, 0, _T("TAppDescriptor::MakeTypeLib: Exception suppressed."));
      }
    }
    

    Note: The attached patch contains complete changes since [r5511]. The above code just shows UnregisterServer and MakeTypeLib.

     

    Related

    Commit: [r5511]


    Last edit: Vidar Hasfjord 2021-06-14
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-14

    Off-topic challenge: Using std::filesystem::path, rewrite the following nonsense in MakeTypeLib, and document what it is supposed to do:

    TCHAR fullPath[_MAX_PATH];
    TCHAR exeDrive[_MAX_DRIVE];
    TCHAR exeDir[_MAX_DIR];
    TCHAR exeFName[_MAX_FNAME];
    TCHAR exeExt[_MAX_EXT];
    TCHAR libDrive[_MAX_DRIVE];
    TCHAR libDir[_MAX_DIR];
    TCHAR libFName[_MAX_FNAME];
    TCHAR libExt[_MAX_EXT];
    ::GetModuleFileName(*Module, fullPath, COUNTOF(fullPath));
    _tsplitpath(typeLibName ? typeLibName : _T(""), libDrive, libDir, libFName, libExt);
    _tsplitpath(fullPath, exeDrive, exeDir, exeFName, exeExt);
    _tmakepath(fullPath,
      libDrive[0] ? libDrive : exeDrive,
      libDir[0] ? libDir : exeDir,
      libFName[0] ? libFName : exeFName,
      libExt[0] ? libExt : _T("OLB"));
    
     
  • Erwin Lotter

    Erwin Lotter - 2021-06-14

    With the new patch "-unregserver" behaves like "-regserver": "-quietreg" is accepted but the server does not shut down after reg update.
    The crash with "-typelib" does not occur if the call of WriteTypeLibrary() - i.e. the line before the exception line - is commented out. Looks like stack/memory corruption in WriteTypeLibrary().

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-14

    Hi Erwin,

    Sounds like you have identified other issues deeper within OCF. To pinpoint the code that throws exceptions, be sure to turn on break on exceptions in the debugger. In Visual Studio, go to Debug | Windows | Exception Settings, then select C++ Exceptions | "All C++ Exceptions not in this list".

    The code probably needs to be fixed (made more robust) so that it makes sense in the presence of exceptions, i.e. leave the program in a valid state.

    Does your test code work fine without QuietReg when you run with administrator rights?

    PS. On the topic of code simplification, I just played around with rewriting UnregisterServer to eliminate all this hard-to-read, explicit and brittle buffer handling:

    _TCHAR guidStr[40];
    TRegItem debugItem = {"debugclsid", {guidStr}};
    TRegItem* debugInfo = 0;              // init to no debug registration
    //...
    if (DebugGuidOffset) {                // setup reg item for debug reg
      TClassId debugClsid(AppClassId[DebugGuidOffset]);
      _tcscpy(guidStr, debugClsid);
      debugInfo = &debugItem;
    }
    ocf::OcUnregisterClass(RegInfo, debugInfo);   // unregister app
    //...
    if (!regList["clsid"]) {      // check if GUID needs to be computed
      TClassId linkClsid(GetLinkGuid(linkGuidIndex++));
      _tcscpy(guidStr, linkClsid);
      TRegItem clsItem = {"clsid", {guidStr}};
      ocf::OcUnregisterClass(regList, &clsItem);
    }
    else
      ocf::OcUnregisterClass(regList);
    

    By defining a simple overload of OcUnregisterClass, all that buffering can be removed:

    OcUnregisterClass(RegInfo, DebugGuidOffset ? TRegItem{"debugclsid", AppClassId[DebugGuidOffset]} : TRegItem{});
    //...
    OcUnregisterClass(regList, !regList["clsid"] ? TRegItem{"clsid", GetLinkGuid(linkGuidIndex++)} : TRegItem{});
    

    The OcUnregisterClass overload is simply defined like this:

    inline auto OcUnregisterClass(owl::TRegList& regInfo, const owl::TRegItem& overrides) -> int
    {
      return OcUnregisterClass(regInfo, overrides.Key ? &overrides : nullptr);
    }
    

    It simply takes the TRegItem parameter by const reference. This allows us to pass a temporary TRegItem in the call, thereby eliminating the need for any buffering (the temporaries used to initialise TRegItem stay alive to the end of the statement). The parameter is treated as optional, with the TRegItem default value being the empty state, thereby allowing us to optionally pass a proper value or an empty value in a single statement.

    Note that the TRegItem arguments cannot be refactored into named variables without reintroducing buffering, since TRegItem is itself unbuffered (it has just two pointers as members). So the proper use of TRegItem is quite brittle.

     

    Last edit: Vidar Hasfjord 2021-06-15
  • Erwin Lotter

    Erwin Lotter - 2021-06-14

    The exception settings didn't help on the -typelib exception, which might be related to the fact that my OLE server doesn't expose an automation interface..

     
  • Vidar Hasfjord

    Vidar Hasfjord - 2021-06-14

    Did you pinpoint the problem in WriteTypeLibrary?

     
1 2 3 > >> (Page 1 of 3)

Log in to post a comment.

MongoDB Logo MongoDB