基於QT的webkit與ExtJs開發CB/S結構的企業應用管理系統

 
一:源起
 
     1.何爲CB/S的應用程序
 
    C/S結構的應用程序,是客戶端/服務端形式的應用程序,這種應用程序要在客戶電腦上安裝一個程序,客戶使用這個程序與服務端通訊,完成必定的操做。
    B/S結構的應用程序,是瀏覽器/服務端形式的應用程序,這種應用程序不用在客戶端部署任何東西,客戶只須要經過瀏覽器與服務端通訊,來完成必定的操做。
    兩種類型的程序優缺點對比:
對比內容
C/S結構的應用程序
B/S結構的應用程序
部署
較困難
方便
升級
較困難
方便
對客戶端的控制權限
數據實時性
較高
通訊效率
較高
跨平臺性
    由上可知,兩種形式的應用程序各有利弊。架構師在作技術選型的時候,每每會根據項目須要,對比這兩種技術形式的優缺點,作出正確的選擇。
    然而,國內大多數企業應用程序,須要頻繁、及時的更新升級、須要更高的客戶端控制權限、須要更高的數據實時性和更高的通訊效率,但卻不在乎部署上的問題。
    這時,架構師就考慮把C/S結構的應用程序和B/S結構的應用程序結合起來,讓客戶端嵌套一個瀏覽器以與服務器通訊,完成必定的操做。這樣的程序就是CB/S結構的應用程序。
    這樣作的好處是通常的業務邏輯只要在服務端更新升級,便可體如今客戶端。對於客戶端系統權限、基於Socket的通訊等瀏覽器核心沒法完成的操做,能夠由客戶端來完成。客戶端能夠直接與服務端通訊,也能夠經過瀏覽器核心與服務端通訊。
    下圖爲CB/S結構應用程序的基本示意圖:
 
目前還有一種介於C/S和B/S結構的應用程序之間的應用程序:RIA富互聯網應用程序,這種結構的應用程序通常都是基於瀏覽器插件來運行的,它有較高的客戶端控制權限(比B/S程序高,但比C/S程序低),通訊方式也有較多的選擇(不僅是基於HTTP協議),目前較常見的RIA技術有:Adobe的flex技術、微軟的Silverlight技術、Oracle的WebStart技術。架構師在作技術選型的時候,也能夠綜合權衡採用這些技術。
    
    2.爲什麼選擇QT的WebKit與Extjs開發企業應用
 
    ExtJs是一個用於建立Web用戶界面的JS框架,提供了豐富的界面部件及佈局方式,對於web開發者來講,實現企業應用所需的各類畫面只要掌握JS語言便可。沒必要再引入flash或silverlight技術,並且能很容易的建立風格統一的企業應用程序。
    雖然ExtJs支持各類流行的瀏覽器,甚至包括IE6,可是它在IE系瀏覽器下運行、渲染的效率不高。在谷歌瀏覽器下表現最好,FireFox瀏覽器次之(這得益於谷歌瀏覽器的JS腳本引擎)。
    然而谷歌瀏覽器 和FireFox瀏覽器的核心都是WebKit(蘋果公司開源的瀏覽器核心,負責解析HTML文本,並呈現到界面上),因此,要想讓咱們的CB/S+ExtJs結構的應用程序能有更好的表現,咱們必須採用WebKit核心的瀏覽器。
    雖然咱們能很方便的得到WebKit的源碼,然而編譯它卻十分耗時費力,不但要選對編譯工具,還要安裝一系列的SDK,編譯時間更是長的驚人(這幾乎是大型C++項目的通病)。編譯出來的DLL使用起來也不是很方便(要翻閱大量的WebKit的API)。
    幸運的是QT界面庫爲咱們作了這些工做,QT庫中包含webkit的瀏覽器控件,而且這個C++庫是跨平臺的,也就是說基於這幾項技術開發的CB/S企業應用能夠部署在Linux系統內。
    除了使用QT界面庫,還能夠選擇gtk+和wxWidgets兩個界面庫,並且這兩個界面庫都對WebKit作過包裝,可是從開發方式,生產效率,運行速度等多方面考慮,仍是QT最爲合適。
    QT界面庫也分爲兩個版本,一個是收費的digia提供的QT,另外一個是免費的qt-project提供的QT(GPL V3 LGPL V2),這裏咱們選擇免費版的QT,本文第三節會介紹如何搭建開發環境。
 
架構師除了選擇QT的WebKit作瀏覽器核心以外,還能夠選擇CEF(Chromium Embedded Framework,項目地址: https://code.google.com/p/chromiumembedded/)這個項目是對谷歌瀏覽器的從新編譯、封裝,分爲兩個版本線,CEF1和CEF3,我曾對此項目作過一些研究,研究的相關資料參見: http://www.cnblogs.com/liulun/archive/2013/03/18/2874276.html;另外,還有一個node webkit的項目(地址: https://github.com/rogerwang/node-webkit)也是對谷歌瀏覽器的從新編譯和封裝,但它引入了NodeJs,使用簡單的HTML JS CSS就能夠編寫出絢麗的客戶端界面。node webkit目前處於V0.7.X版本。 
 
二:思路
 
     1.目標
  •     搭建一個CB/S結構的企業應用程序
  •     儘可能保證系統的執行效率
  •     儘可能保證系統升級更新的便利性
  •     儘可能保證系統的可擴展性
     2.方案
 
    ExtJs框架是一個比較龐大的框架,通常B/S結構的程序使用ExtJS框架,都是把ExtJs的框架放在服務端,這樣用戶每次請求頁面的時候,都會去訪問ExtJS框架的JS文件,從而產生大量的磁盤IO和網絡消耗,這也是ExtJS框架看起來渲染很慢的一個因素。B/S結構的應用程序沒法解決這個問題,主要是由於沒法控制客戶端的瀏覽器,CB/S結構的程序就能輕鬆解決這個問題。能夠把ExtJs框架打包進客戶端程序中,隨客戶端程序分發給使用者,使用者請求頁面時,使用的是本地的ExtJS框架的JS文件,業務邏輯程序則仍舊使用服務端的。這樣作減小了磁盤IO和網絡消耗,保證了系統的執行效率;服務端對業務邏輯程序依舊保持着很好的控制權,保證了系統升級更新的便利性
    關於系統的可擴展性,ExtJs就能很好的處理,在下一節中會有詳細描述。
 
    3.難點
 
    CB/S結構的應用程序其實就是一個高度定製的瀏覽器。爲了讓這個瀏覽器完成指定的功能(好比:包含ExtJs框架的js文件,作成cookie,發起請求等)不免會有不少客戶端和瀏覽器核心的交互。這些交互涉及到C++,Js,HTML,CSS等的互操做,是系統在技術上的難點。
 
 
三:客戶端瀏覽器實現
 
    1.搭建開發環境
 
    咱們下載基於MinGW 4.8, OpenGL建立的QT 5.1,地址爲: http://qt-project.org/downloads。不選擇基於VS編譯器的QT是由於用VS編譯器編譯出的DLL依賴VS運行時,分發程序時較困難。下載並安裝後,你會看到這並非一個簡單的界面庫,它還包含了一個IDE,Qt Creator。
    安裝完成後,就可使用Qt Creator來建立你本身的基於Qt的桌面程序,你能夠在Qt Creator的歡迎界面看到入門程序、示例程序和幫助文檔。Qt的開發方式並非本文所講述的重點,建議讀者到官網學習。
    雖然咱們能夠成功在Qt Creator內編譯併成功執行程序,但到windows目錄下經過雙擊執行編譯出的exe程序,就不能正常運行,這是由於可執行程序所需的動態連接庫並無與可執行程序在同一個目錄內,至於可執行程序依賴哪些動態連接庫,咱們將在本文第四節詳細描述。
 
    2.邊框和標題欄
 
    目前大部分windows桌面程序都使用自定義的邊框和標題欄,好比QQ,360安全衛士等,使用MFC或Windows API自定義窗口的標題欄和邊框並非一件容易的事情,使用Qt來開發Windows桌面程序也有同樣的困難。
    因爲咱們開發的是企業應用系統,這類系統通常狀況下都出於最大化狀態,因此咱們在考慮自定義標題欄和邊框的時候就能夠不用考慮還原按鈕、拖拽改變窗口大小和位置的功能。可是,咱們須要爲標題欄增長一個下拉菜單按鈕,以使用戶完成系統設置、打開調試器等相關功能。
    另外,爲了使標題欄和業務界面中ExtJs的風格一致,咱們索性去掉了主窗口的標題欄和邊框,直接使用ExtJs來生成。
    在Qt中去掉標題欄和邊框是很容易的事,建立窗口的時候設置一個WindowFlags便可,見以下代碼:    
w.setWindowFlags(Qt::FramelessWindowHint);
    但設置此WindowFlags以後隨之帶來的問題是,窗口將撐滿整個屏幕,把系統的任務欄也遮住了,這顯然不是咱們想要的,解決此問題須要重寫Qt窗口類的changeEvent槽,見以下代碼:
if(event->WindowStateChange)
{
   switch(this->windowState())
   {
   case Qt::WindowMinimized:
this->hide();
event->ignore();
   break;
   case Qt::WindowMaximized:
   QDesktopWidget* desktopWidget =QApplication::desktop();
   QRect deskRect =desktopWidget->availableGeometry();
   this->resize(deskRect.width(), deskRect.height());
   break;
   }
}
    這樣建立的Qt窗口將不具備標題欄和邊框,至於如何用ExtJs來渲染標題欄,以及如何實現標題欄的最小化及關閉等功能,將在後續小節講述。
 
  3.打開新窗口
 
    使用Qt的WebKit很是簡單,直接把QWebView控件拖放到界面中去便可,可是默認的QWebView在實現上有些缺憾,好比沒法打開新窗口,沒法下載文件,沒法打印等。然而這些功能是一個瀏覽器所必備的功能,咱們的CB/S企業應用系統也須要這些功能。要想讓瀏覽器支撐這些功能,只能經過重寫QWebView來完成。
    要想讓自制的瀏覽器打開新窗口,須要重寫QWebView的createWindow方法,見以下代碼:(UtmpWebView即爲QWebView的子類)
    UtmpWebView* webView = new UtmpWebView;
    QWebPage* newWeb = new QWebPage;
    if(type == QWebPage::WebModalDialog)
    {
        webView->setWindowModality(Qt::ApplicationModal);
    }
    webView->setAttribute(Qt::WA_DeleteOnClose,true);
    webView->setPage(newWeb);
    webView->show();
    return webView;
    然而,這隻能應對a標籤的target屬性爲_blank的新窗口連接,沒法應對使用javascript經過window.open的方式打開新窗口的場景。要想知足這一點,必須在QWebView的構造函數裏,更改一下瀏覽器的配置參數,代碼以下:
QWebSettings* default_settings = QWebSettings::globalSettings();
default_settings->setAttribute(QWebSettings::JavascriptEnabled,true);
default_settings->setAttribute(QWebSettings::JavascriptCanOpenWindows,true);
 
    4.打印
 
    咱們常常在網頁中經過javascript使用window.print的方式來調用打印機打印HTML頁面,常見的瀏覽器都會支持這個功能,然而QWebView默認並不支持此功能,要想讓咱們定製的瀏覽器支持此功能必須爲其作一個事件連接,代碼以下:
connect(this->page(), SIGNAL(printRequested(QWebFrame*)),this,SLOT(customPrintRequested(QWebFrame*)));
    this->page()->setForwardUnsupportedContent(true);
customPrintRequested槽的實現以下:
    QPrinter* p = new QPrinter(QPrinter::HighResolution);
    QPrintDialog printDialog(p, this);
    printDialog.setWindowTitle("UTMP打印");
    if(printDialog.exec() != QDialog::Accepted)
    {
        return;
    }
    frame->print(p);
 
    5.下載
 
    一樣QWebView默認也不支持下載文件。全部的瀏覽器把請求的響應分爲兩類,一類是瀏覽器能夠解析的(Html文本),另外一類是瀏覽器沒法解析的(文件),常見的瀏覽器遇到沒法解析的文件,每每會下載到本地給用戶使用,要想讓QWebView支持下載,就必須截獲瀏覽器的unsupportedContent信號,該信號所對應的槽的代碼實現以下
ShellExecuteA(NULL, "open", reply->url().toString().toStdString().c_str(), "", "", SW_SHOW);
    注意,要想讓上面的代碼正確執行,必須在頭文件中引入windows.h(這也體現出QT框架與NativeAPI能沒有任何限制的輕鬆交互)。上面的代碼是調用了系統默認的瀏覽器來完成下載。固然讀者也能夠考慮本身實現下載線程並提示下載進度、保存地址等。
 
    6.與頁面腳本交互
 
    咱們既然選擇本身開發瀏覽器,那麼瀏覽器必定能自如的讓頁面執行一些特殊腳本,頁面也能夠經過腳本讓瀏覽器完成一些腳本沒法完成的操做。此功能通常的瀏覽器都沒法支撐,只有咱們自定義的QWebView能夠輕鬆實現。
    咱們知道javascript在頁面中執行都會用到window對象,好比,咱們調用alert()方法時,實際上是調用window.alert()方法,使用document對象時,實際上是使用window.document對象,要想讓瀏覽器能與頁面腳本交互,咱們必須讓瀏覽器給頁面的window對象註冊一個子對象(window對象的屬性)。
    遇到的第一個問題並非如何註冊此對象,而是在什麼時候註冊。因爲在頁面加載之初,window對象就已經初始化完成了,此時爲其註冊子對象已爲時已晚,必須在其初始化以前爲其註冊,爲此QWebView專門提供了javaScriptWindowObjectCleared信號,在刷新網頁、打開新網頁和加載嵌套的iframe頁面時(window對象初始化時),此信號都會被觸發。與此信號關聯的槽,代碼以下:
this->page()->mainFrame()->addToJavaScriptWindowObject("QtWinFrame", this);
如你所見,咱們爲window對象註冊了一個名爲QtWinFrame的對象。這就像瀏覽器爲window對象註冊document子對象同樣,要想讓頁面腳本能調用瀏覽器核心的方法,必須爲讓瀏覽器核心提供相應的方法才行,因爲咱們在第二小節已經把窗口默認的標題欄和邊框去掉了,因此必須經過頁面javascript來關閉瀏覽器和最小化瀏覽器,假設咱們在瀏覽器核心中實現的方法代碼以下:
void UtmpWebView::SetFrameWindow(int flag)
{
    switch(flag)
    {
        case 0:
            this->close();
            break;
        case 1:
            this->showMinimized();
            break;
}
}
在瀏覽器頁面內,只要經過以下javascript代碼,便可讓瀏覽器核心執行相應的操做:
QtWinFrame.SetFrameWindow(1); QtWinFrame.SetFrameWindow(0);
相對於「腳本讓瀏覽器執行工做」來講,「瀏覽器讓腳本執行工做」就簡單不少,只須要在瀏覽器中調用evaluateJavaScript方法便可,見以下代碼:
this->page()->mainFrame()->evaluateJavaScript("testFun();");
注意:這有些相似於javascirpt中的eval()方法,若是前端框架中引入了ExtJs,最好不要直接使用此方法來調用ExtJs提供的函數,執行效率很是慢。能夠先在頁面上用普通的js函數包裝一下ExtJs提供的函數,再來調用。
 
    7.打開腳本調試器
 
    調試javascript代碼一直以來都是開發人員面臨的老大難的問題,自從有了FireBug和谷歌瀏覽器自帶的javascript調試器以後,這個問題獲得了很大程度的解決,因此有個好的javascript調試器十分關鍵。QWebView也提供了相應的調試工具(我認爲就是谷歌瀏覽器的javascript調試器,但未經驗證。)。使瀏覽器核心打開調試器的代碼以下:
QDialog* d = new QDialog(this,(Qt::WindowMinimizeButtonHint|Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint));
d->setAttribute(Qt::WA_DeleteOnClose, true);
QWebInspector* wi = new QWebInspector(d);
wi->setPage(this->page());
d->setLayout(new QVBoxLayout());
d->layout()->setMargin(0);
d->layout()->addWidget(wi);
d->show();
d->resize(600,350);
    因爲咱們在系統啓動的時候,使用Qt::FramelessWindowHint屬性禁用掉了窗口的標題欄和邊框,因此在打開調試器子窗口的時候,要恢復該子窗口的標題欄和邊框,爲此咱們多作了一些工做,讀者也能夠本身實現QDialog類型的父類,以應對更多子窗口業務。
 
 
 8.截獲瀏覽器請求
 
    既然咱們對瀏覽器有最大的控制權,那麼咱們就但願當瀏覽器完成指定工做時通知咱們,好讓咱們作一些前期或後期的處理。最多見的工做莫過於瀏覽器發起請求了。咱們知道瀏覽器解析一個網頁的過程當中,可能會發起屢次請求,好比圖片標籤的src路徑,iframe標籤的src路徑,js/css資源的路徑等等。要想知道這些請求什麼時候發起,什麼時候終結須要重寫QNetworkAccessManager,而後經過以下方式,讓瀏覽器加載自定義的QNetworkAccessManager
QNetworkAccessManager *oldManager = webview->page->networkAccessManager();
MyNetworkAccessManager *newManager = new MyNetworkAccessManager(oldManager, this);
webview->page->setNetworkAccessManager(newManager);
而後,咱們能夠在自定義的MyNetworkAccessManager類中重寫createRequest(QNetworkAccessManager::Operation operation,const QNetworkRequest &request, QIODevice *device)方法,其中request參數,包含了原始請求的URL信息,此方法須要返回一個QNetworkReply對象,假設咱們想改變原始請求的路徑,能夠按以下操做方式來完成
return QNetworkAccessManager::createRequest(operation, myrequest, device);
如你所見,咱們用QNetworkAccessManager新建了一個請求(createRequest的返回值爲QNetworkReply類型),該請求中myrequest實參的類型爲QNetworkRequest,其餘兩個實參從原始方法中得到。
 
    9.本地化ExtJs庫
 
    通常咱們使用ExtJs(官方地址: http://www.sencha.com/products/extjs/),都是把它部署在服務端,瀏覽器請求頁面時,也會相應的加載ExtJs的資源以渲染界面,但因爲ExtJs包含衆多js文件和其餘資源,經過網絡來加載的話,一方面增長了服務器IO消耗,另外一方面增長了網絡延時,不少用戶反應基於ExtJs的網絡應用呈現速度慢,都是這兩個緣由致使的。
    如今咱們開發本身的瀏覽器,就能夠把Extjs庫(不包含業務JS代碼,由於業務JS代碼易於變化,不適合看成資源放在客戶端)看成資源放在客戶端,對於一個客戶端來講,體積越小越好,然而以ext4.2.1 gpl版爲例,官方提供的壓縮包裏,有不少內容不適合打包到客戶端中。好比:教程、文檔、源碼、示例等,讀者能夠自行將這些內容刪掉,而後把精簡後的ExtJs類庫放到瀏覽器應用程序編譯文件夾內([appDirectory]\build-UTMP-Desktop_Qt_5_1_1_MinGW_32bit-Debug\debug),這樣Extjs類庫就與咱們的瀏覽器可執行程序在同一個目錄下了,若是讓瀏覽器使用Extjs類庫的資源,還應該在此目錄下建立一個靜態文件,以引入同目錄下的靜態資源,代碼以下:
    <link href="ext-4.2.1.883/resources/Css/ext-all.css" rel="stylesheet" type="text/css" />
    <script src="ext-4.2.1.883/ext-all-debug.js"></script>
    固然,單單引入資源,還沒法呈現ExtJs的絢麗界面,此時還須要引入一個服務器端的JS文件,此文件經過Extjs的類庫加載機制,加載更多的業務JS,以達到實現特定業務邏輯的目的。咱們在下一節中會詳細介紹這些內容。
    <script src="http://localhost:8080/UTMP/app.js"></script>
    在QT中只須要經過本地路徑加載這個靜態頁面便可,代碼以下:
UtmpWebView w;    
QDir dir(QDir::currentPath());
QUrl url = url.fromLocalFile(dir.path()+"/debug/index.html");
w.load(url);
    因而可知,保存在客戶端的資源基本都是業務無關的、比較穩定的、不易變動的資源。保存在服務端的內容,都是與業務有關的,比較容易變動的內容,這種機制主要意圖是保證了業務的可升級性。    
 
 
四:服務端業務腳本
 
 1. OPOA模式
 
    使用Extjs的企業應用系統大多都是OPOA模式(One Page One Application),OPOA模式的WEB系統只有一個頁面,在這個頁面中會引入extjs的資源並經過js來渲染一個框架頁面,而後根據用戶的操做載入更多的js代碼,來完成不一樣的業務。對於咱們的系統來講這個頁面就是放在客戶端本地debug目錄下的靜態頁面。這個頁面引入了一個服務器端的js文件(http://localhost:8080/UTMP/app.js),經過此文件以及由此文件加載的其餘js文件,咱們渲染出了一個框架頁面,見以下代碼:
Ext.application({
    name:'UTMP',
    appFolder:'http://10.0.7.109:8080/UTMP/app',
    controllers:["sys.index"],
    views:["sys.menuTree","sys.titleBar","sys.contentTabPanel"],
    launch:function(){    
        Ext.create('Ext.Viewport',{
           layout:'border',
           items:[
               {xtype: 'menuTree'},
               {xtype: 'titleBar'},
               {xtype: 'contentTabPanel'}
           ]
        });
    }
});
如你所見,這是一個Extjs系統的開始(Ext.application),並且咱們使用了Extjs的MVC模式(關於ExtJs的MVC模式的相關資料請參閱: http://docs.sencha.com/extjs/4.2.1/#!/guide/application_architecture),系統界面中包含三個視圖:menuTree、titleBar和contentTabPanel。因爲咱們設計的瀏覽器沒有標題欄,因此視圖titleBar就是系統的標題欄,它包含了關閉、最小化按鈕。
 
 2.定製模塊加載基址
 
    Extjs有一套獨特的模塊加載機制,它能夠經過js類的名稱空間來加載相應的js代碼文件,好比視圖文件的名稱空間是UTMP.sys.menuTree,ExtJs框架會從appFolder指定的路徑下找sys目錄下的menuTree.js文件。在普通的ExtJs項目中,appFolder屬性並不用設定爲絕對路徑,只須要使用相對路徑便可,但因爲咱們的項目的主頁(靜態頁面)是放在客戶端本地的,若是使用相對路徑的話,ExtJs框架就會在客戶端本地尋找相應的資源,然而咱們的業務JS文件都是放在服務端的,因此勢必會沒法加載這些資源。
 
 3. 定製AJAX請求基址
 
    模塊加載機制能夠經過設置appFolder基路徑來解決,可是對於業務JS代碼隨處可見的AJAX請求該如何處理呢?確實,AJAX請求也會面臨這種問題,並且更爲突出。由於在ExtJs中對AJAX請求作了不少封裝:proxy、store、request、load等,隨處可見ajax的身影。幸而ExtJs是一個對象化程度較高的js類庫,使得這個問題能很容易的解決。
    在ExtJs中全部Ajax請求都離不開Ext.data.Connection類的支撐,咱們可使用ExtJs提供的觀察者模式來註冊Ext.data.Connection類的beforerequest事件(這是一種侵入性較強的作法),這樣全部的AJAX請求,無論是proxy發起的,仍是request發起的,都逃不出咱們的手心,具體實現代碼以下:
Ext.util.Observable.observe(Ext.data.Connection,{
    beforerequest: function(conn, options, eOpts){
        options.url = "http://10.0.7.109:8080/UTMP/"+options.url;
    }
});
 
五:分發
 
 1.依賴的動態連接庫
 
    在使用QTCreator開發基於QT的應用程序時,無論是debug編譯仍是release編譯,都沒法到編譯目錄下,經過雙擊exe程序來執行應用(會提示「沒法啓動此程序,由於計算機中丟失xxxx.dll....」的錯誤信息),之因此在IDE內能順利執行,是由於IDE已經爲程序執行建立好了環境,但假若不解決此問題,就沒法把應用程序分發給直接用戶。
     要解決此問題只要把Qt類庫提供的dll文件放在可執行程序的目錄下或其所在目錄的子目錄下便可,在C:\Qt\Qt5.1.1\5.1.1\mingw48_32\bin目錄下有Qt類庫提供的大多數dll,這些dll名稱以字母d結尾的是debug編譯的應用程序所依賴的類庫,不以字母d結尾的則是release編譯的應用程序所須要的類庫,除了此目錄內的dll外,在C:\Qt\Qt5.1.1\5.1.1\mingw48_32\plugins目錄下還有一些應用程序須要的dll類庫。
    如此數量衆多的dll,都須要打包到咱們最終的安裝程序中去嗎?固然不用這麼作。經過IDE執行咱們的應用程序時,咱們只須要經過processExplorer工具來查看應用程序進程所依賴的dll,便可斷定哪些dll是須要打包到安裝包中去的(大多數狀況下能夠這麼作,若是是開發人員經過代碼動態加載的類庫,那麼我相信你也會自行甄別的)。
 
 2.打包
    
    可能有的讀者會問:「我可不能夠把類庫靜態編譯到exe中去呢?」固然能夠,可是很是麻煩,你須要本身靜態編譯整個QT工程,還須要對IDE作出相應的調整(要編譯QT的Webkit還須要作更多的工做),這是一項耗時、耗力還不必定能成功的工做,我不建議這麼作。
    當咱們找到應用程序依賴的全部dll後,咱們就可使用打包工具來製做應用程序的安裝包,固然也能夠本身開發安裝包工具(能夠參見個人博客: http://www.cnblogs.com/liulun/archive/2011/12/12/2284360.html)。
    國內外有不少不錯的打包工具,我推薦使用inno setup( http://www.jrsoftware.org/),它支持編寫腳原本控制安裝過程,使用LZMA壓縮算法來打包程序(壓縮效率很是高,是7-zip使用的壓縮算法),但它並不支持中文安裝界面,目前社區有開發者提供了針對inno setup的中文語言包,使用起來也很是方便。
 
 
 
 
請不要讓我調試代碼,老夫很忙!懶的管!
相關文章
相關標籤/搜索