Ticket #14474: getmodulehandle_msw_vista_7_fix.patch

File getmodulehandle_msw_vista_7_fix.patch, 9.8 KB (added by jgmdev, 6 years ago)
  • src/msw/dlmsw.cpp

     
    2929#include "wx/msw/debughlp.h"
    3030#include "wx/filename.h"
    3131
     32#include "psapi.h"
     33
    3234const wxString wxDynamicLibrary::ms_dllext(wxT(".dll"));
    3335
    3436// ----------------------------------------------------------------------------
     37// private functions
     38// ----------------------------------------------------------------------------
     39 
     40// Forward declarations.
     41static bool moduleFileNameEquals(HMODULE, wxString const &);
     42
     43
     44////////////////////////////////////////////////////////////////////////////////
     45//
     46// manualGetModuleHandle()
     47// -----------------------
     48//
     49//   Manually traverses the list of processes currently loaded modules and
     50// compares their base names to the given one. Returns a handle of the located
     51// module or 0 in case no module matching the given name could be found.
     52//
     53//   Expects the module to be specified without any path information.
     54//
     55//   In order to be compatible with the documented ::GetModuleHandle() Windows
     56// API behaviour we append the '.dll' extension for module names specified
     57// without an extension. We are 'duplicating' the default Windows DLL extension
     58// here and not reusing the one defined by the wxDynamicLibrary class since we
     59// are directly implenting the ::GetModuleHandle() specification which
     60// explicitly defines the extension used.
     61//
     62//   Returned module handle is compatible with ones returned by the
     63// ::GetModuleHandle() Windows API, including the fact that it should not be
     64// closed by the caller. See the ::GetModuleHandle() and ::EnumProcessModules()
     65// Windows API
     66//
     67//   If we return 0, we either failed to enumerate all the modules for the
     68// current process or did not find the module we were looking for.
     69//
     70////////////////////////////////////////////////////////////////////////////////
     71
     72static
     73wxDllType manualGetModuleHandle(wxString const & moduleNameParam)
     74{
     75    static int const maxSupportedLoadedModules(4096);
     76    static wxChar const * const defaultModuleExtensionForGetModuleHandle(_T(".dll"));
     77
     78    wxString moduleName;
     79    moduleName.reserve( moduleNameParam.length() + sizeof(defaultModuleExtensionForGetModuleHandle) / sizeof(wxChar) );
     80    moduleName = moduleNameParam;
     81
     82    if (moduleName.find_first_of(_T('.')) == wxString::npos)
     83        moduleName.append(defaultModuleExtensionForGetModuleHandle);
     84
     85    // We lowercase the passed moduleName in order to make it compatible with
     86    // our internal moduleFileNameEquals() function requirements. Otherwise we
     87    // would have to do this for every separate moduleFileNameEquals() call.
     88    moduleName.MakeLower();
     89
     90    HMODULE allModuleHandles[maxSupportedLoadedModules];
     91    DWORD allModuleHandlesByteCount(0);
     92    if (::EnumProcessModules(::GetCurrentProcess(), allModuleHandles, sizeof(allModuleHandles), &allModuleHandlesByteCount))
     93    {
     94        DWORD const allModuleHandlesCount(allModuleHandlesByteCount / sizeof(HMODULE));
     95        for (unsigned int loadedModuleIndex(0); loadedModuleIndex < allModuleHandlesCount; ++loadedModuleIndex)
     96            if (moduleFileNameEquals(allModuleHandles[loadedModuleIndex], moduleName))
     97                return wxDllType(allModuleHandles[loadedModuleIndex]);
     98    }
     99    return wxDllType(0);
     100}
     101
     102
     103////////////////////////////////////////////////////////////////////////////////
     104//
     105// moduleFileNameEquals()
     106// ----------------------
     107//
     108// Internal function returning whether a module's file name (path information
     109// not included) matches the given one. Expects to be passed the candidate file
     110// name using all lowercase letters and including its file extension.
     111//
     112////////////////////////////////////////////////////////////////////////////////
     113
     114static bool moduleFileNameEquals(HMODULE const moduleHandle, wxString const & moduleName)
     115{
     116    wxChar currentModuleFullPath[MAX_PATH * 10] = {0};
     117    std::size_t const currentModuleFullPathSize(sizeof(currentModuleFullPath) / sizeof(wxChar));
     118
     119    // Find out the module's full path.
     120    DWORD const apiResult(::GetModuleFileName(moduleHandle, currentModuleFullPath, currentModuleFullPathSize));
     121    if (apiResult == 0)
     122    {
     123        // (todo)
     124        //   Better Windows API failure handling.
     125        //                                    (todo) (26.04.2009.) (Jurko)
     126        return false;
     127    }
     128    if (apiResult == currentModuleFullPathSize)
     129    {
     130        // (todo)
     131        //   Add more robust support for handling paths longer than MAX_PATH
     132        // characters. Current implementation is efficient but only works if
     133        // path names are shorter than some predefined size. Note though that
     134        // due to the large size chosen it is highly unlikely such paths will
     135        // ever appear in practice.
     136        //                                    (todo) (26.04.2009.) (Jurko)
     137        return false;
     138    }
     139
     140    //   Module's base name starts immediately after the final backslash
     141    // character. If there is no trailing backslash that this is actually a
     142    // Windows API failure as all paths returned by ::GetModuleFileName() should
     143    // be absolute paths.
     144    wxChar * const pFinalBackslash(wxStrrchr(currentModuleFullPath, _T('\\')));
     145    if (!pFinalBackslash) return false;
     146    wxChar * const currentModuleBaseName(pFinalBackslash + 1);
     147
     148    // Convert the 'currentModuleBaseName' to lowercase.
     149    for (wxChar * pCurrentModuleBaseNameChar(currentModuleBaseName); *pCurrentModuleBaseNameChar; ++pCurrentModuleBaseNameChar)
     150        *pCurrentModuleBaseNameChar = wxTolower(*pCurrentModuleBaseNameChar);
     151
     152    //   Find whether moduleName (expected to be passed to us already
     153    // lowercased) matches the current module's lowercased base file name.
     154    return !moduleName.compare(currentModuleBaseName);
     155}
     156
     157
     158////////////////////////////////////////////////////////////////////////////////
     159//
     160// wxGetModuleHandle()
     161// -------------------
     162//
     163//   Wrapper function for the ::GetModuleHandle() Windows API providing a
     164// workaround for a bug in the original API causing it to sometimes not locate
     165// modules specified using just their base name and not their full path. This
     166// has been detected on some Windows Vista installations when attempting to
     167// locate the comctl32.dll Window Common Controls DLL module.
     168//
     169//   Note that on systems demonstrating this bug, related :LoadLibrary() Windows
     170// API may also behave unexpectedly and not load libraries or load them more
     171// than once when requested without specifying their full path.
     172//
     173////////////////////////////////////////////////////////////////////////////////
     174
     175static wxDllType wxGetModuleHandle(wxString const & moduleName)
     176{
     177    wxDllType handle(::GetModuleHandle(moduleName));
     178
     179    //   If ::GetModuleHandle() Windows API fails and caller specified just the
     180    // module file name without its path then there is a chance this might be a
     181    // false report so try to look up the module manually. ::GetModuleHandle()
     182    // explicitly requires its caller to use backslashes as path separators so
     183    // there is no need for any fancy path separator detection here.
     184    if (!handle && (moduleName.find_first_of(_T('\\')) == wxString::npos))
     185        handle = manualGetModuleHandle(moduleName);
     186
     187    return handle;
     188}
     189
     190
     191////////////////////////////////////////////////////////////////////////////////
     192//
     193// wxGetModuleHandle()
     194// -------------------
     195//
     196//   Returns a module handle for a module identified by an address belonging to
     197// that module, if possible, or by its name if not.
     198//
     199//   We want to use ::GetModuleHandleEx() instead of the usual
     200// ::GetModuleHandle() Windows API because the former works correctly for
     201// comctl32.dll while the latter returns NULL when comctl32.dll version 6 is
     202// used under XP. Note that GetModuleHandleEx() is only available under XP and
     203// later, coincidence?
     204//
     205////////////////////////////////////////////////////////////////////////////////
     206
     207static
     208HMODULE wxGetModuleHandle(const char *name, void *addr)
     209{
     210    // Check whether we can use GetModuleHandleEx().
     211    typedef BOOL (WINAPI *GetModuleHandleEx_t)(DWORD, LPCSTR, HMODULE *);
     212 
     213    static const GetModuleHandleEx_t INVALID_FUNC_PTR = (GetModuleHandleEx_t)-1;
     214 
     215    static GetModuleHandleEx_t s_pfnGetModuleHandleEx = INVALID_FUNC_PTR;
     216    if ( s_pfnGetModuleHandleEx == INVALID_FUNC_PTR )
     217    {
     218        wxDynamicLibrary dll(_T("kernel32.dll"), wxDL_VERBATIM);
     219        s_pfnGetModuleHandleEx =
     220            (GetModuleHandleEx_t)dll.RawGetSymbol(_T("GetModuleHandleExA"));
     221 
     222        // DLL object can be destroyed, kernel32.dll won't be unloaded anyhow.
     223    }
     224 
     225    // Get module handle from its address.
     226    if ( s_pfnGetModuleHandleEx )
     227    {
     228        // flags are GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT |
     229        //           GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
     230        HMODULE hmod;
     231        if ( s_pfnGetModuleHandleEx(6, (char *)addr, &hmod) && hmod )
     232            return hmod;
     233    }
     234 
     235    //   Windows CE only has Unicode API, so even if we have an ANSI string
     236    // here, we still need to use GetModuleHandleW() there.
     237#ifdef __WXWINCE__
     238    return ::GetModuleHandleW(wxConvLibc.cMB2WC(name).data());
     239#else
     240    return ::GetModuleHandleA((char *)name);
     241#endif
     242}
     243
     244// ----------------------------------------------------------------------------
    35245// private classes
    36246// ----------------------------------------------------------------------------
    37247
     
    234444wxDynamicLibrary::RawLoad(const wxString& libname, int flags)
    235445{
    236446    if (flags & wxDL_GET_LOADED)
    237         return ::GetModuleHandle(libname.t_str());
     447        return ::wxGetModuleHandle(libname.t_str());
    238448
    239449    // Explicitly look in the same path as where the main wx HINSTANCE module
    240450    // is located (usually the executable or the DLL that uses wx).  Normally