6

I think I have researched this pretty thoroughly and I have not found an acceptable answer. First the broad strokes: Windows 8.1, Visual Studio 2013. Although, I don't think these are important.

Problem as follows. The application I am writing makes use of A.dll. We use a third-party vendor product (a backup program, but again this is not important) that has installed a Context Menu Handler control under HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers. Let's say the path to this is c:\Program Files\Vendor\control.dll.

Now, the issue is that when my program opens a file chooser dialog (it's a Qt program that uses QFileDialog which then uses the standard Windows one), this control is loaded to provide context-sensitive right-click functionality. This control depends on a different version of "A.dll" and when control.dll is loaded, my program promptly crashes.

I don't need this extra functionality. What I would love to do is to prevent this specific dll (control.dll) from loading in my process. In an object-oriented world I would simply overload LoadLibrary(), check for this specific DLL, and then call the standard one otherwise. However this doesn't seem feasible.

Is there an easy way to do this?

Thanks! Dan

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
DGehlhaar
  • 111
  • 2
  • 8
  • "Easy" way to do this? I'm certain there is none :( Since you have little (or none, actually) control over the 3-rd party library you cannot tell it to "not load" a DLL. Maybe you can fake it? Create your own stub DLL and have the registry key point to it? – YePhIcK Sep 24 '15 at 21:02
  • 1
    You could try using one of the hooking libraries to hook `LoadLibrary()` in your process and make it fail when the troublesome DLL is loaded. Another option would be to move the file chooser code to a separate process. Neither solution is ideal unfortunately. – Jonathan Potter Sep 24 '15 at 21:03
  • 1
    So, to summarize, your program uses A and B, and B uses a different version of A. Solution: Don't try any crappy workaround, but go to the core of the problem and make that there are not 2 different versions of A [in the same process]. Anything else will cause more headache than necessary in the long run. – deviantfan Sep 24 '15 at 21:04
  • @deviantfan: the problem is that (if I've understood the OP correctly) B isn't something that the OPs software uses, but a third-party product (backup software) that just happens to be installed on the same computer. Even if B were removed from the OPs computer, any end-users with B installed would still have the same problem. And even if the OP upgrades/downgrades A to match the version that B uses, it probably still won't work properly for someone using a different version of B than the OP. – Harry Johnston Sep 24 '15 at 22:18
  • To add to what @JonathanPotter said, hooking `LoadLibrary()` is one approach, but you should hook the lower-level function in the Native API (aka ntdll.dll) for added certainty. – MrEricSir Sep 24 '15 at 22:45
  • Do you have the possibility to statically link your own A.dll(A.lib)? – Sebastian Lange Sep 25 '15 at 07:29
  • A few things. The vendor has developed a solution but my company won't deploy it for a while, so I'd like a workaround. @deviantfan: Agreed, we should use the same versions of the offending library. It is not totally clear that this will fix the problem and in this case will be a big deal. Looking for an easier fix. The ideal is to just prevent the control.dll from loading in my process. I will investigate hooking LoadLibrary. – DGehlhaar Sep 25 '15 at 21:49
  • Wish there was a way to just replace LoadLibrary with my own that says "if asked to load this DLL then fail, otherwise call the standard LoadLibrary". I suppose i can do this with the Hooking libraries... – DGehlhaar Sep 25 '15 at 21:58
  • @YePhIck: I can't reach into the registry and stub out the offending Context Menu Handler -- would require more access than I have... – DGehlhaar Sep 25 '15 at 21:59
  • 1
    In that case... I'm not sure if that has changed in the modern versions of Windows but "way back when..." there was an interesting problem with DLLs. If there was already a DLL **with that name** loaded, Windows would reuse it instead of trying to load the "correct" one. You might want to look into that option. – YePhIcK Sep 26 '15 at 04:49
  • I *think* that if you created a corresponding key in `HKCU\Software\Classes` it would take precedence over the key in `HKLM`. Still not a good solution, because it affects the entire user account, not just your program. There's also RegOverridePredefKey, which only affects your process, but that would be bound to break all sorts of stuff. But YePhicK's suggestion of having a dummy `control.dll` loaded into your process seems promising. – Harry Johnston Sep 27 '15 at 20:33

2 Answers2

4

To prevent the vendor.dll from loading you can use a hook on the following Win32API function LoadLibrary and LoadLibraryEx which are responsible for dynamically loading DLLs and which are also used to load shell extensions. The hook is really assembler code at the code site of the LoadLibrary function, which redirects (jumps) to a function defined by yourself. In this function you can then intercept any call to vendor.dll being loaded and just return 0, which indicates that the library could not be loaded.

Some example code how to go about it using MinHook library:

HMODULE WINAPI LoadLibraryA_check(_In_ LPCTSTR lpFileName)
{
  if (isInWhiteList(lpFileName))
    return loadLibraryA_Original(lpFileName);
  else 
  {
    // Pretend that the module was not found by returning
    // 126 (0x7E): "The specified module could not be found."
    SetLastError(ERROR_MOD_NOT_FOUND);         
    return NULL;
  }
}

bool installWhitelistFilter()
{
  // Initialize MinHook.
  if (MH_Initialize() != MH_OK)
    return false;

  if (MH_CreateHook(&LoadLibraryA, &LoadLibraryA_check, 
      reinterpret_cast<LPVOID*>(&loadLibraryA_Original)) != MH_OK)
    return false;

  if (MH_EnableHook(&LoadLibraryA) != MH_OK)
    return false;

  // same for LoadLibraryW, LoadLibraryExW, LoadLibraryExA

  return true;
}
Christopher Oezbek
  • 23,994
  • 6
  • 61
  • 85
3

In this situation, you are launching a standard Windows Open File dialog that internally hosts Windows Explorer as part of its UI, and Explorer loads Shell Extension DLLs. To prevent that, you have to either:

  1. call the GetOpenFileName() API directly, opting to use the old-style dialog instead of the newer explorer-style dialog by omitting the OFN_EXPLORER flag (the newer IFileOpenDialog API does not support this option). The older-style dialog does not support Shell Extensions (but it also has an outdated look-and-feel).

  2. don't use the standard Open File dialog at all. Make you own instead.

Another option is to let Explorer do its job normally, but create and register your own shim DLL as a replacement for the offending Shell Extension. Have the shim check if the calling process is explorer.exe. If so, load and delegate all actions to the original Extension, otherwise do nothing. See How To Disable Shell Extension In FileOpen Dialog.

Another option might be to create a side-by-side assembly manifest for the offending Shell Extension, and have that manifest explicitly state which DLL to load, so the Extension loads its own version of the DLL and not the version your app is using.

Community
  • 1
  • 1
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770