-1

Have some code originally designed for Windows 2000 and the WMI SDK. I am trying to resolve the linker error. After trying the options referneced in What is an undefined reference/unresolved external symbol error and how do I fix it? was unable to find a solution.

The unresolved symbols error:

1>WmiProvider.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall Provider::Provider(wchar_t const *,wchar_t const *)" (__imp_??0Provider@@QAE@PB_W0@Z)
1>WmiProvider.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: bool __thiscall CInstance::SetCHString(wchar_t const *,char const *)" (__imp_?SetCHString@CInstance@@QAE_NPB_WPBD@Z)
1>dllmain.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: static int __stdcall CWbemProviderGlue::FrameworkLogoffDLL(wchar_t const *)" (__imp_?FrameworkLogoffDLL@CWbemProviderGlue@@SGHPB_W@Z)
1>dllmain.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: static int __stdcall CWbemProviderGlue::FrameworkLoginDLL(wchar_t const *)" (__imp_?FrameworkLoginDLL@CWbemProviderGlue@@SGHPB_W@Z)

I've tried adding code:

#pragma comment(lib,"Framedyn.lib")

and tried adding to lib to framedyn.lib linker options but still get same error.

The project has the following defined to allow running on Windows XP:

#define _WIN32_WINNT 0x0501

Project settings include:

  • Target Platform Version 7.0
  • Platform Toolset Visual Studio 2017 - Windows XP (v141_xp)
  • C++ Language Standard Default (ISO C++14 Standard7)
  • Language - Conformance Mode - No
  • Target X86

Example usage:

case DLL_PROCESS_ATTACH:
    ghModule = hInstDLL;
    bRet = CWbemProviderGlue::FrameworkLoginDLL(L"WmiProvider");
    break;

CL.exe and MSBUILD.exe load WbemGlue.h from:

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\WbemGlue.h

link.exe loads FrameDyn.lib from:

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\FrameDyn.Lib

dumpbin /exports "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\FrameDyn.Lib" shows entry for FrameworkLoginDLL as:

?FrameworkLoginDLL@CWbemProviderGlue@@SGHPBG@Z (public: static int __stdcall CWbemProviderGlue::FrameworkLoginDLL(unsigned short const *))
?FrameworkLoginDLL@CWbemProviderGlue@@SGHPBGPAJ@Z (public: static int __stdcall CWbemProviderGlue::FrameworkLoginDLL(unsigned short const *,long *))

Looking at the symbols difference is when I compile visual studio is looking for FrameworkLoginDLL@CWbemProviderGlue@@SGHPB_W@Z whereas the lib file has a G at the end and no W i.e. **FrameworkLoginDLL@CWbemProviderGlue@@SGHPBG@Z**

The old MSDN documentation (from August 2001) specifies this use:

The FrameworkLoginDLL method is called when the DLL_PROCESS_ATTACH value is sent to DllMain to determine whether the provider server can be loaded.

static BOOL WINAPI CWbemProviderGlue::FrameworkLoginDLL(
  LPCWSTR name
);

Requires:

  • Windows NT/2000/XP: Included in Windows NT 4.0 SP4 and later.
  • Windows 95/98/Me: Included in Windows 95 and later.
  • Header: Declared in WbemGlue.h; include FwCommon.h.
  • Library: Use Framedyn.lib.

The current documentation here specifies use:

BOOL FrameworkLoginDLL(
  LPCWSTR name
);

With following requirements:

  • Minimum supported client Windows Vista
  • Minimum supported server Windows Server 2008
  • Target Platform Windows Header wbemglue.h (include FwCommon.h)
  • Library FrameDyn.lib
  • DLL FrameDynOS.dll; FrameDyn.dll

Creating a new Visual Studio 2019 Project with Desktop C++ Wizard, selecting DLL, and all other defaults reproduces same issue with this code:

#include "framework.h"
#include <FwCommon.h>
#pragma comment(lib,"FrameDyn.lib")
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    BOOL bRet = TRUE;

    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        bRet = CWbemProviderGlue::FrameworkLoginDLL(L"WmiProvider");

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return bRet;
}

Demangling the symbol linker is trying to use get the following:

__imp_public: static int __stdcall CWbemProviderGlue::FrameworkLoginDLL(wchar_t const *)

Demangling the symbol in actual .lib file:

public: static int __stdcall CWbemProviderGlue::FrameworkLoginDLL(unsigned short const *)

Definition in WbemGlue.h

static BOOL WINAPI FrameworkLoginDLL(LPCWSTR name);
Malcolm McCaffery
  • 2,468
  • 1
  • 22
  • 43
  • Does this answer your question? [What is an undefined reference/unresolved external symbol error and how do I fix it?](https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) – Ken White Jan 21 '21 at 04:51
  • I've read this article but don't yet see one that applies to my case as far as I know, not to say one doesn't, but i'm still trying to fully understand all the options for troubleshooting this. – Malcolm McCaffery Jan 21 '21 at 04:53
  • 1
    Just a guess but this could be due to the combination of `wchar_t` and `unsigned short`. In C++ these are different types but IIRC this was not always the case for the MS compiler. There's an option 'treat wchar_t as a build in type' maybe you should try setting that to no. – john Jan 21 '21 at 06:16
  • 1
    Yes. This is it, interesting not mentioned in the MS doco. IT only compiles with Treat WChar_t as built in type set to No (/Zc:wchar_t-) – Malcolm McCaffery Jan 21 '21 at 06:21
  • 1
    More details, https://learn.microsoft.com/en-us/cpp/build/reference/zc-wchar-t-wchar-t-is-native-type?view=msvc-160 I was thinking that it would be possible to turn that off only for the header file but that seems not to be possible, maybe you could just edit the header file directly. – john Jan 21 '21 at 06:26
  • @MalcolmMcCaffery "*compiles with ... /Zc:wchar_t-*" You should post that as a (self) answer. – dxiv Jan 21 '21 at 06:42

1 Answers1

1

This issue occurred because MSVC was looking for symbol FrameworkLoginDLL@CWbemProviderGlue@@SGHPB_W@Z but the lib file had definition FrameworkLoginDLL@CWbemProviderGlue@@SGHPBG@Z.

Demangling the names using https://demangler.com/ showed the symbol used by MSVC was:

__imp_public: static int __stdcall CWbemProviderGlue::FrameworkLoginDLL(wchar_t const *)

Where as the lib file was:

public: static int __stdcall CWbemProviderGlue::FrameworkLoginDLL(unsigned short const *)

This issue was resolved by setting Project Properties under C/C++ - Language - Treat WChart As Built in Type to No (/Zc:wchar_t-)

enter image description here

Microsoft recommends the following regarding this setting here:

If /Zc:wchar_t is on, wchar_t is a keyword for a built-in integral type in code compiled as C++. If /Zc:wchar_t- (with a minus sign) is specified, or in code compiled as C, wchar_t is not a built-in type. Instead, wchar_t is defined as a typedef for unsigned short in the canonical header stddef.h. (The Microsoft implementation defines it in another header that is included by stddef.h and other standard headers.)

We do not recommend /Zc:wchar_t- because the C++ standard requires that wchar_t be a built-in type. Using the typedef version can cause portability problems. If you upgrade from earlier versions of Visual Studio and encounter compiler error C2664 because the code is trying to implicitly convert a wchar_t to unsigned short, we recommend that you change the code to fix the error, instead of setting /Zc:wchar_t-.

The /Zc:wchar_t option is on by default in C++ compilations, and is ignored in C compilations. The /permissive- option does not affect /Zc:wchar_t.

Microsoft implements wchar_t as a two-byte unsigned value. It maps to the Microsoft-specific native type __wchar_t. For more information about wchar_t, see Data Type Ranges and Fundamental Types.

If you write new code that has to interoperate with older code that still uses the typedef version of wchar_t, you can provide overloads for both the unsigned short and __wchar_t variations of wchar_t, so that your code can be linked with code compiled with /Zc:wchar_t or code compiled without it. Otherwise, you would have to provide two different builds of the library, one with and one without /Zc:wchar_t enabled. Even in this case, we recommend that you build the older code by using the same compiler that you use to compile the new code. Never mix binaries compiled with different compilers.

When /Zc:wchar_t is specified, _WCHAR_T_DEFINED and _NATIVE_WCHAR_T_DEFINED symbols are defined. For more information, see Predefined Macros.

If your code uses the compiler COM global functions, because /Zc:wchar_t is now on by default, we recommend that you change explicit references to comsupp.lib (either from the comment pragma or on the command line) to either comsuppw.lib or comsuppwd.lib. (If you must compile with /Zc:wchar_t-, use comsupp.lib.) If you include the comdef.h header file, the correct library is specified for you. For information about compiler COM support, see Compiler COM Support.

The wchar_t built-in type is not supported when you compile C code. For more information about conformance issues with Visual C++, see Nonstandard Behavior.

Malcolm McCaffery
  • 2,468
  • 1
  • 22
  • 43