0

I have a dialog window with a Tree-View control where the user can drag the items to rearrange the tree.

It looks and behaves differently when it is used in an executable and in an MS Excel add-in XLL.

It looks like this during a drag operation when used in an executable(this is the desired look):

enter image description here

But when I use the same dialog in an MS Excel add-in XLL(where it is displayed when user selects a command) it looks like this(notice the missing tooltip, and icons for expanded items):

enter image description here

What could be causing this? Is there a way I can make the dialog look like the way it does when used in an executable?


I suspect it has to do with the ComCtl32.dll version, as the following returns version 6.16 when called from the executable, but 5.82 when called from XLL:

HINSTANCE hinstDll = LoadLibrary(lpszDllName);
DLLGETVERSIONPROC pDllGetVersion =
    (DLLGETVERSIONPROC)GetProcAddress(hinstDll, "DllGetVersion");

I have the following manifest in the .cpp file.

#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")

// Enable Visual Style
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
Kemal
  • 849
  • 5
  • 21
  • have correct manifest in dll not enough. need also activate it before create control via `ActivateActCtx` – RbMm Sep 12 '17 at 12:37
  • @RbMm: I just looked that function up in MSDN. It is under "Isolated Applications and Side-by-side Assemblies", and I browsed through it for a while but it turns out to be a way too advanced topic for me at the moment. I don't understand most of it. Is there a way you can give me an example on how to use that function related to the problem I described? – Kemal Sep 12 '17 at 14:07

1 Answers1

1

if we in DLL have resource of type RT_MANIFEST with name ISOLATIONAWARE_MANIFEST_RESOURCE_ID (note that for EXE need use CREATEPROCESS_MANIFEST_RESOURCE_ID) the loader create activation context for our DLL (and save it in LDR_DATA_TABLE_ENTRY.EntryPointActivationContext) but it activate it only before call our DLL entry point, and just deactivate it after it return. really - in process we have many DLLs - from which DLL need activate context ? the system activate activation context from EXE only. and from DLL - temporary, during call it Entry point. however we need use this activation context (from our DLL) during create a window etc.

so strategy is next:

get and save our activation context on DLL_PROCESS_ATTACH:

// global variables
HANDLE g_hActCtx;
BOOLEAN g_bActCtxValid;

case DLL_PROCESS_ATTACH:
    //...
    g_bActCtxValid = GetCurrentActCtx(&g_hActCtx) != FALSE;
    //...

when we need create window, dialog, etc - we need activate saved context:

ULONG_PTR Cookie;
if (ActivateActCtx(g_hActCtx, &Cookie))
{
    CreateWindowExW(..);
    DeactivateActCtx(0, Cookie);
}

and finally, on DLL unload - release context:

case DLL_PROCESS_DETACH:
    if (g_bActCtxValid)ReleaseActCtx(g_hActCtx);

note that this already not once answered here - for example. but with one difference - in all other solutions used CreateActCtx api (with dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID, lpResourceName = ISOLATIONAWARE_MANIFEST_RESOURCE_ID, hModule = &__ImageBase) - this call create activation context again for our DLL. but i instead this prefer use already created by system activation context for our DLL (this is current context inside DllMain). so not duplicate, but reference already created. GetCurrentActCtx instead CreateActCtx

RbMm
  • 31,280
  • 3
  • 35
  • 56
  • Your solution worked, thank you. But I have a couple of questions, if you don't mind. – Kemal Sep 12 '17 at 19:51
  • In `DllMain()` we do not create an activation context, but simply make a call to `GetCurrentActCtx()`. Which makes me think, there **already is** a "current active content" when `DllMain()` is called. That much, I understand. But if I don't initialize `g_hActCtx` from `DllMain()`, and instead, I call `GetCurrentActCtx()` from another function in the dll, then activate it, it doesn't work. Why is this? Isn't the "current activation context" the same in another function as it is in `DllMain()`? – Kemal Sep 12 '17 at 19:55
  • You might have already addressed this question at the beginning of your answer, but I want to make sure that I understand correctly. From your answer, I think the way it works is like this: Dll loader creates and activates the "active context" before calling `DllMain()`. This is the one specified as a compiler directive. Then, after the `DllMain()` ends, loader deactivates(and maybe releases?) it. At that point, the "active context" comes from the module that loaded the dll. Is that correct? – Kemal Sep 12 '17 at 20:04
  • @Kemal - `Isn't the "current activation context" the same in another function as it is in DllMain()` of course no. before call your dll entry point loader call `ActivateActCtx` which he create personally for your dll (based on resource). and call `DeactivateActCtx` just after `DllMain` return. so in `DllMain` and **only** in `DllMain` the active context is from your *DLL* resource. in another DLL functions it unknown. really - system don know about your exported function called and not special activate your native context – RbMm Sep 12 '17 at 20:04
  • @Kemal call `GetCurrentActCtx()` and **just** `ActivateActCtx` in place - no sense. you got the current active context and say make it again current active. exist sense call this functions from different places - first from entry point and save. then in another function - you run in arbitrary activation context. and call `ActivateActCtx` change this arbitrary to correct saved – RbMm Sep 12 '17 at 20:07
  • @Kemal - `Dll loader creates and activates the "active context" before calling DllMain(). This is the one specified as a compiler directive. Then, after the DllMain() ends, loader deactivatesit. At that point, the "active context" comes from the module that loaded the dll. Is that correct?` - yes. you correct understand. the loader create context when it load your dll. and save in `LDR_DATA_TABLE_ENTRY.EntryPointActivationContext`. and release it when your dll is unloaded – RbMm Sep 12 '17 at 20:10
  • Got it. One more question: Assume that I don't have a .manifest file and instead, I have the compiler directive, which is exactly my case. If I `#define ISOLATION_AWARE_ENABLED 1`, my dialog resources display correctly(say, comctl32.dll version 6.0.0.0 style) without me having to use activate/deactivate any "activation context"s. Why is that? – Kemal Sep 12 '17 at 20:13
  • In that scenario, even though dialog resources are displayed in v6.0.0.0 style without the need for context activation/deactivation, `MessageBox()` displays a dialog in v5.82 style **unless** I explicitly activate "activation context". Is that normal? – Kemal Sep 12 '17 at 20:18
  • @Kemal - this manifest from exe or dll. loader activate and use activation context from exe resource permanent. it can not use it from some dll - because many dlls - from which need use ? so activate it only temporary. if exe have needed to you manifest - you can not have and activate context from dlls. and instead linker directives, you can yoursel set manifest in resource: `ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST ".manifest"` – RbMm Sep 12 '17 at 20:18
  • @Kemal - `In that scenario,..` i not complete understand concrete scenario - which manifests in exe and dll. how and when activated context from dll and how dialog/messagebox(modal dialog) created. – RbMm Sep 12 '17 at 20:21
  • The scenario is this: I do not use a manifest file for the dll, only the compiler directive. I have `ISOLATION_AWARE_ENABLED 1` defined. I call `DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DIALOG1), g_hWndMain, TreeviewDlgProc);` from a function `f()` in the dll, **without activating** any "activation context". IDD_DIALOG1 is displayed in v6-style. After that, in `f()`, I make a call to `MessageBox()`. Again, no activated context. Message box is displayed in v5-style. I haven't changed anything, so why is one dialog displayed in one style, and the other in another? – Kemal Sep 12 '17 at 20:27
  • 1
    @Kemal - read [Specifying a Default Activation Context](https://msdn.microsoft.com/en-us/library/windows/desktop/aa376607(v=vs.85).aspx) - if you ask how it work - search `ISOLATION_AWARE_ENABLED` in header files. – RbMm Sep 12 '17 at 20:33
  • 1
    say `#if defined(ISOLATION_AWARE_ENABLED) && (ISOLATION_AWARE_ENABLED != 0) #include "winuser.inl" #endif /* ISOLATION_AWARE_ENABLED */` and look "winuser.inl" – RbMm Sep 12 '17 at 20:34
  • 1
    @Kemal - so instead `DialogBoxParamW` called `IsolationAwareDialogBoxParamW` (by macro redirection) and look for `IsolationAwareDialogBoxParam` implementation - this is inline – RbMm Sep 12 '17 at 20:37
  • 1
    @Kemal - and look for `winbase.inl ` - when you define `ISOLATION_AWARE_ENABLED` - all this code included (as inline) to your code and increase size of module. really this is what i paste here `CreateActCtx` + `ActivateActCtx` and function wrap – RbMm Sep 12 '17 at 20:41
  • Proceeding as you suggested, I just found out why `DialogBox()` is isolation aware, whereas `MessageBox()` is not. It turns out, unlike `DialogBoxParamW()`, macro indirection is skipped for `MessageBoxW()` with the comment `MessageBoxW skipped, as it is a popular C++ member function name.` – Kemal Sep 12 '17 at 21:17
  • @Kemal - yes, because exist macro for `#define DialogBoxParamW IsolationAwareDialogBoxParamW` (the `DialogBox` is also macro expanded to `DialogBoxParam`) but no macro for `MessageBox` – RbMm Sep 12 '17 at 21:24