1. 程式人生 > >外部程式呼叫Qt5帶介面的dll

外部程式呼叫Qt5帶介面的dll

一、            主要參考

二、            背景及環境

目標:在MFC、VC++控制檯程式裡能呼叫Qt5編寫的帶介面的DLL。

Qt5直接編譯的DLL在MFC、VC++控制檯程式裡是不能呼叫的,為了實現上述目標,開始在度娘上查詢資料。首先發現瞭解決方法是qtwinmigrate,但正如參考1中所寫,想找一個現成可用的資料真是不容易。後來把能查出的基本都瀏覽了一遍,經過一些曲折的學習測試,終於基本實現。所以還是梳理下,便於之後檢視,也給有需要的小夥伴們提供一個參考。

本人環境:

軟體

版本

Qt

qt-opensource-windows-x86-mingw492-5.6.0.exe

Visual Studio

VS2010

QtWinmigrate

https://github.com/qtproject/qt-solutions

       重點說下,qtwinmigrate我是在GitHub(https://github.com/qtproject/qt-solutions)上down下來的,發現是可用的。GitHub嘛,相信大家都懂,最好自己down了。若確實沒用過GitHub又懶得去弄,也可在這裡(http://download.csdn.net/detail/shuishanga/9601396)下載。

三、            具體使用    

3.1         編譯qtwinmigrate

將下載好的qtwinmigrate解壓,用Qt Creator開啟…\qtwinmigrate\examples\qtdll\qtdll.pro,然後在Release模式下構建。


圖1 qtdll及構建

       構建完成後,會在…\qtwinmigrate\examples\build-qtdll-Desktop_Qt_5_6_0_MinGW_32bit-Release\release下生成qtdialog.dll 。

3.2         找到DLL的依賴項

上步中生成了DLL,但是還不能直接拿去使用,此時LoadLibrary是載入不上的,因為還需要其依賴的Qt庫。網上有些使用Dependency Walker

等軟體來找DLL依賴項,當然可以,但是過於麻煩。這裡採用Qt官方開發環境裡自帶的工具:windeployqt.exe。如我安裝的5.6版本可以在Qt安裝目錄…\5.6\mingw49_32\bin下找到。

具體使用方法:

先將上步生成的qtdialog.dll 複製到一個新建資料夾,如D:\MyQtDLL。在開始選單中開啟Qt命令列,在命令列中定位到DLL相應目錄,執行“windeployqt qtdialog.dll”。此時qtdialog.dll的依賴項便自動copy到其目錄下了。

這裡有些依賴項可能用不到,比如translations資料夾,刪除就行。

   

圖2 開啟Qt命令列


圖3 使用Qt命令列

 

圖4 qtdialog.dll及其依賴項

3.3         呼叫Qt5編譯的DLL

MFC或者VC++控制檯程式在呼叫Qt5編譯的DLL時,需要將上步中qtdialog.dll所在資料夾內所有內容複製到工程編譯的exe資料夾內。

1.        MFC呼叫

為測試,用VS2010新建一個MFC對話方塊程式,簡單地在介面上放一個按鈕,並在按鈕點選事件中載入DLL,彈出Qt5的DLL的介面。

void XXX::OnBnClickedButton1()
{
	const char* dllName = "qtdialog.dll";

	HMODULE hDLL = LoadLibrary(dllName);

	if (hDLL != NULL)
	{
		typedef bool(*pShow)(HWND parent);

		pShow fp1 = pShow(GetProcAddress(hDLL, "showDialog"));
		if (fp1 != NULL)
		{
			fp1(theApp.m_pMainWnd->m_hWnd);
		}

		FreeLibrary(hDLL);
	}
	else
	{
		CString strInfo;
		strInfo.Format("Cannot Find %s", dllName);
		MessageBox(strInfo);
	}
}

記住執行前將qtdialog.dll所在資料夾內的全部內容複製到工程編譯的exe資料夾內。編譯成功後,執行如下如

圖5  MFC呼叫qtdialog.dll

2.        控制檯程式呼叫

#include <iostream>
#include <Windows.h>
using std::cout;
using std::endl;

int main()
{
	const char* dllName = "qtdialog.dll";

	HMODULE hDLL = LoadLibrary(dllName);

	if (hDLL != NULL)
	{
		typedef bool(*pShow)(HWND parent);

		pShow fp1 = pShow(GetProcAddress(hDLL, "showDialog"));
		if (fp1 != NULL)
		{
			//ShowWindow(GetConsoleWindow(), SW_HIDE);
			fp1(GetConsoleWindow());
		}
		
		FreeLibrary(hDLL);
	}
	else
	{
		cout << "Cannot Find " << dllName << endl;
	}

	return 0;
}



圖6  控制檯呼叫qtdialog.dll

3.4         修改qtwinmigrate,載入自寫介面

上面例子中直接使用了qtwinmigrate中自帶的QMessageBox,這裡將其修改下變為一個自己寫的介面。

隨便用Qt Creator新建一個Dialog,如這裡TestDlg:


圖7  新建TestDlg

將TestDlg的標頭檔案和原始檔複製到qtdll目錄下,並在Qt Creator中qtdll專案上右鍵新增進來。


圖8 將TestDlg檔案複製到qtdll下並在工程中新增

此時修改介面函式為:

extern "C" __declspec(dllexport) bool showDialog( HWND parent )
{
    QWinWidget win( parent );
    win.showCentered();

    CMyDialog mydlg(&win);
    mydlg.exec();

    return TRUE;
}

然後重新構建,同上步驟。此時MFC、VC++控制檯的呼叫結果:

圖9 MFC呼叫qtdll自寫介面


圖10 控制檯呼叫qtdll自寫介面

四、            其他問題

4.1         介面函式問題 int main(int argc, char *argv[]) ?

在參考1和4中指出用int main(int argc, char *argv[]) 做介面函式,並在其中建立QApplication物件。

介面函式:

extern "C"__declspec(dllexport) int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   CTestDialog w;
   w.show();
   return a.exec();
}

測試:

HMODULE hDLL = LoadLibrary(dllName);
 
if (hDLL != NULL)
{
         typedef bool(*pShow)(HWND parent);
 
         typedef int(*pMain)(int, char *[]);
         pMain fMain =pMain(GetProcAddress(hDLL, "main"));
         if (fMain != NULL)
         {
                   fMain(0, 0);
         }
 
         FreeLibrary(hDLL);
}
結合原始碼和參考3可知其實QApplication物件在QmfcApp::pluginInstance已經建立。並且QMfcApp::pluginInstance(hInstance)提供了Qt和MFC訊息迴圈共同工作的機制。因此個人覺得采用showDialog中QWinWidget方式更好些。

對於3.4的小例子,測試發現使用發現兩種方法都可以。但是測試自寫的一個更復雜的介面時發現,多次點選按鈕載入DLL時,採用main介面函式會出現異常崩潰現象,如下圖:


圖11 main介面發生異常情況