Qt QLibrary load 로드 실패 문제 심층 분석

Qt QLibrary load 실패 문제 해결

  • 앞말
  • 일반 해결 방법
  • QLibrary load DLL 방식 자세히 살펴보기
  • 문제 해결
  • 요약
  • 전언


    최근에는 프로젝트에 qt를 사용하여 개발해야 하기 때문에 프로젝트 프로젝트가 비교적 크고 여러 사람이 동시에 개발하는 것과 관련되기 때문에 제품의 서로 다른 모듈 간의 독립성을 높이기 위해 불가피한 필요보다 DLL을 호출해야 한다.다행히도 강력한 qt가 QLibrary 클래스를 제공하여 dll 동적 불러오기를 편리하게 했습니다.모든 것이 그렇게 순조롭게 진행되었을 때, 테스트 동료는 윈도우즈 플랫폼의 개별 컴퓨터에 dll를 불러오는 데 실패할 것이라는 것을 발견하였다.

    일반적인 해결 방법


    1. dll 의존 관계 보기: Depends 도구를 통해 dll 의존 파일을 볼 수 있습니다. 의존 DLL 파일을 주 DLL 파일 경로 아래에 놓거나exe 프로그램 작업 경로 아래에 놓아야 합니다. 물론 시스템 디렉터리에 넣을 수도 있지만 가능한 한 이렇게 하지 마십시오.2. dll 경로가 정확한지 확인: dll 동적 불러오는 경로가 정확한지 확인한다. 특히 윈도우즈 플랫폼에서 중국어 경로의 난자 문제와 관련될 수 있으므로 먼저 경로 인코딩 문제를 해결하고load 경로가 정확함을 확보해야 한다.물론 이 문제를 피하기 위해 검색할 때 먼저 영문 경로 테스트를 사용할 수 있다.그러나 불행하게도 위의 조사를 통해 제가 겪은 문제를 해결할 수 없습니다. 저희 응용 방식과 관련이 있을 수 있습니다. 왜냐하면 저희 메인 프로그램 App1이 설치한 디렉터리와 메인 모듈 Dll1은 같은 등급 디렉터리에 있지 않고 메인 모듈 Dll1은 Dll2 모듈에 의존하기 때문입니다.

    QLibrary load DLL 방식에 대한 자세한 내용


    우리는 qt가 윈도우즈 API에 대한 봉인된 것을 알고 있다. 이 문제를 철저히 해결하기 위해서는 QLibrary가 DLL을 불러오는 것이 윈도우즈 API를 호출하는 어느 인터페이스인지 볼 수 밖에 없다.드디어, qlibrarywin 파일의 QLibrary Private:::loadsys () 방법에서 발견되었습니다.
    	 Q_FOREACH (const QString &attempt, attempts) {
            pHnd = LoadLibrary((wchar_t*)QDir::toNativeSeparators(attempt).utf16());
    
            // If we have a handle or the last error is something other than "unable
            // to find the module", then bail out
            if (pHnd || ::GetLastError() != ERROR_MOD_NOT_FOUND)
                break;
        }
    

    즉 QLibrary는 Windows API LoadLibrary 방법을 통해 dll을 로드합니다.Loadlibrary에서 dll을 불러오는 데 실패했습니다. DLL 검색 경로의 문제로 인한 경우가 많습니다. LoadLibrary에서 DLL 검색 경로를 불러오는 것은 MSDN의 글:Dynamic-Link Library Search Order 참조.기본값은 다음과 같습니다.
    If SafeDllSearchMode is enabled, the search order is as follows: 1、The directory from which the application loaded. 2、The system directory. Use the GetSystemDirectory function to get the path of this directory. 3、The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. 4、The Windows directory. Use the GetWindowsDirectory function to get the path of this directory. 5、The current directory. 6、The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path. If SafeDllSearchMode is disabled, the search order is as follows: 1、The directory from which the application loaded. 2、The current directory. 3、The system directory. Use the GetSystemDirectory function to get the path of this directory. 4、The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. 5、The Windows directory. Use the GetWindowsDirectory function to get the path of this directory. 6、The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.

    머리가 어지럽고 알아볼 수 없으니 구글에서 번역해 보세요.하하.괜찮습니다. 계속 읽으면 다음과 같은 내용을 발견할 수 있습니다.
    The standard search order used by the system can be changed by calling the LoadLibraryEx function with LOAD_WITH_ALTERED_SEARCH_PATH. The standard search order can also be changed by calling the SetDllDirectory function.

    LOADWITH_ALTERED_SEARCH_PATH 매개변수 태그는 시스템이 사용하는 표준 검색 순서를 변경할 수 있는 LoadLibraryEx 함수를 호출합니다.SetDllDirectory 함수를 호출하여 표준 검색 순서를 변경할 수도 있습니다.

    문제 해결


    여기서 우리는 첫 번째 방법을 사용해서 해결한다. 즉, LoadLibrary Ex 방법을 호출하는 것이다. 이런 방식은 기존의 LoadLibrary 불러오는 방식과 일치한다. 우리는 QLibrary 클래스에서 우리 자신의 QLibrary 클래스를 파생시킬 수 있다. 구체적으로는 다음과 같다.
    qmylibrary.h 헤더 파일 #ifndef QMYLIBRARY_H #define QMYLIBRARY_H #include #ifdef Q_OS_WIN #include #endif class QMyLibrary : public QLibrary { public: QMyLibrary (QString strFileName); QMyLibrary (QObject* parent); ~QMyLibrary (); public: void setFileName(const QString &fileName); QString fileName() const; void *resolve(const char *symbol); bool load(); bool unload(); bool isLoaded() const; QString errorString() const; #ifdef Q_OS_WIN private: HMODULE m_hModalHandle; #endif }; #endif // QMYLIBRARY_H

    CPP 파일은 다음과 같습니다.
    #include “qmylibrary.h” #include QMyLibrary::QMyLibrary(QString strFileName) : QLibrary(strFileName) { #ifdef Q_OS_WIN m_hModalHandle = NULL; #endif } QMyLibrary::QMyLibrary(QObject* parent) : QLibrary(parent) { } QMyLibrary::~QMyLibrary() { unload(); } void QMyLibrary::setFileName(const QString &fileName) { QLibrary::setFileName(fileName); } QString QMyLibrary::fileName() const { return QLibrary::fileName(); } void *QMyLibrary::resolve(const char *symbol) { #ifdef Q_OS_WIN if(m_hModalHandle) { return GetProcAddress(m_hModalHandle,symbol); } #endif return QLibrary::resolve(symbol); } bool QMyLibrary::load() { #ifdef Q_OS_WIN QString strFilePath = fileName(); strFilePath.replace("/","\"); m_hModalHandle = LoadLibraryEx(strFilePath.toStdWString().c_str(),NULL,LOAD_WITH_ALTERED_SEARCH_PATH); if(m_hModalHandle == NULL) { strFilePath.append(".dll"); m_hModalHandle = LoadLibraryEx(strFilePath.toStdWString().c_str(),NULL,LOAD_WITH_ALTERED_SEARCH_PATH); } if(m_hModalHandle) return true; #endif bool blret = QLibrary::load(); return blret; } bool QMyLibrary::unload() { #ifdef Q_OS_WIN if(m_hModalHandle) { FreeLibrary(m_hModalHandle); m_hModalHandle = NULL; } #endif return QLibrary::unload(); } bool QMyLibrary::isLoaded() const { #ifdef Q_OS_WIN if(m_hModalHandle) { return true; } #endif return QLibrary::isLoaded(); } QString QMyLibrary::errorString() const { return QLibrary::errorString(); }

    테스트를 통해 메인 프로그램 App은 메인 모듈 Dll1을 성공적으로 불러올 수 있다. 약간 흥분되지 않았는가!

    총결산


    Qt는 강력하지만 구덩이도 적지 않다.DLL을 로드하려면 LoadLibraryEx 메서드를 사용하는 것이 좋습니다. 코드 변동량이 적어 제어에 영향을 미칩니다.QT 개발에서 윈도우즈 플랫폼 dll 불러오는 클래스를 QLibrary에서 파생할 수 있습니다.

    좋은 웹페이지 즐겨찾기