Does windows preload ComCtrl32.dll in an app?
Asked Answered
B

4

5

I've come across a situation where I know a code

I've created an MFC app in Visual Studio 2008 that generates a Tray Icon and some notifications. I've read I can use a different NOTIFYICONDATA structure for Windows Vista than for Windows XP by setting the cbSize property of the structure to initialize it. I've also read that I can use LoadIconMetric in Windows Vista to load my icon for the notification, whereas in Windows XP, that function is not available to me and I must use LoadIcon.

In my application, I've set the following:

#ifndef WINVER
#define WINVER 0x0600 // Vista
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600 // Vista
#end#if

#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS 0x0600 // Vista
#endif

#ifndef _WIN32_IE
#define _WIN32_IE 0x0700
#endif

I'm compiling and linking against the Windows 7 SDK on a Windows 7 x64 machine in Visual Studio 2008. My test for WindowsVista or greater looks like this (straight out of MSDN):

static BOOL IsWinVistaOrLater()
{
    // Initialize the OSVERSIONINFOEX structure.
    OSVERSIONINFOEX osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osvi.dwMajorVersion = 6;
    osvi.dwMinorVersion = 1;

    // Initialize the condition mask.
    DWORDLONG dwlConditionMask = 0;
    VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
    VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);

    // Perform the test.
    return VerifyVersionInfo(&osvi, 
            VER_MAJORVERSION | VER_MINORVERSION,
            dwlConditionMask);
}

Now here's the interesting part. I use IsWinVistaOrLater to determine if I should use LoadIconMetric or LoadIcon:

if (IsWinVistaOrLater())
{
    tnd_Happy.dwInfoFlags = NIIF_LARGE_ICON | tnd_Happy.dwInfoFlags;
    LoadIconMetric(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON_HAPPY), LIM_SMALL, &(tnd_Happy.hIcon));
} else {
    tnd_Happy.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE (IDI_ICON_HAPPY)); // ICON RESOURCE ID
}

On XP this crashes with "Ordinal 380 not found in ComCtrl32.dll". If I comment out the call to LoadIconMetric, things are happy (even with all those target vers set to Vista). Does Windows try to import all the function calls regardless of runtime code path?

Blackfellow answered 4/11, 2010 at 15:10 Comment(0)
V
3

Instead of LoadLibrary and GetProcAddress suggested by Chris, you can write a Vista-only delay-load-linked DLL which contains all Vista-only code while your main project still targets XP. Then you don't need to load Vista-only code unless the execution pass the Vista version check and call functions exported from the Vista code DLL.

Vanderhoek answered 5/11, 2010 at 0:53 Comment(1)
That's exactly what I did. WOrked out well.Blackfellow
A
12

Just to help others who may run into very similar problem. I could not get LoadIconMetric working even under Windows 7 with Visual Studio C++ 2010 Express WinApi template application. After adding Comctl32.lib to dependecies and including CommCtrl.h it keep saying "Ordinal 380 not found in ComCtrl32.dll". It was not very clear to me what I missed because I was completely unfamiliar with switching versions of such components. After quite some time I figure that out and here are valuable sources for such situation:

Common Control Versions

I solved the problem by adding this line before inclusion of CommCtrl.h (Note that my application only targeting Windows Vista and above):

// Need CommCtrl v6 for LoadIconMetric()
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

It's originated from Microsoft sample project:

Notification Icon Sample - NotificationIcon.zip

Ade answered 26/3, 2013 at 10:34 Comment(1)
You saved me some time ;)Hypophosphate
F
7

If you set up the header files for Vista, then it will let you build applications that will not load on earlier versions of Windows. That is why setting that file correctly is so important.

To work around your issue:

  • It should be possible to use the /DELAYLOAD switch on comctl32.dll. Without /DELAYLOAD Windows will try to import all functions up front (regardless of code path)

  • Reset the target version header file to 0x0501 (XP). Windows will now automatically warn you if you try to use any advanced features. Access the new functionality using LoadLibrary("comctl32.dll") && GetProcAddress("LoadIconMetric"). The disadvantage of this method is you can make the new calls via GetProcAddress, but the structures (if they've changed) will be the old structures.

Lastly, a lot of comctl32.dll features are a trick: Unless you have specifically Enabled Visual Styles then your comctl32.dll version should always be the version 5 version. (If you have a MFC project created in VS2005 or VS2008 this should have been done automatically). This means its possible to set WINVER to Vista, but then ComCtl32 calls start failing because the version 5 ComCtl32 doesn't understand the new larger structures.

Fabiola answered 4/11, 2010 at 15:28 Comment(0)
V
3

Instead of LoadLibrary and GetProcAddress suggested by Chris, you can write a Vista-only delay-load-linked DLL which contains all Vista-only code while your main project still targets XP. Then you don't need to load Vista-only code unless the execution pass the Vista version check and call functions exported from the Vista code DLL.

Vanderhoek answered 5/11, 2010 at 0:53 Comment(1)
That's exactly what I did. WOrked out well.Blackfellow
T
1

Similar to Siamoen's answer, I had to manifest my dependency on ComCtrl32.dll. But I did it in my app.manifest, rather than in code.

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <!-- ... -->
    <dependency>
        <!-- ... other dependencies ... -->
        <dependentAssembly>
            <assemblyIdentity
                type="win32"
                name="Microsoft.Windows.Common-Controls"
                version="6.0.0.0"
                processorArchitecture="*"
                publicKeyToken="6595b64144ccf1df"
                language="*" />
        </dependentAssembly>
    </dependency>
</assembly>

See a similar change to EarTrumpet, a C# app. I also happen to be using C#, but this solution applies to C++ code, too.

Triparted answered 25/5, 2023 at 20:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.