1. 程式人生 > >通過nsis和duilib實現自定義安裝介面

通過nsis和duilib實現自定義安裝介面

 nsis用於實現安裝邏輯,duilib用於實現UI介面

通過nsis呼叫duilib生成的dll庫來實現。現擷取部分duilib程式碼用於展示如何使用duilib編寫程式碼。
cpp檔案如下:

#include <windows.h>
#include <commctrl.h>
#include <Shlobj.h>
#include "plugin-common.h"
#include "DlgMain.h"

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

HINSTANCE g_hInstance;
HWND g_hwndParent;
extra_parameters *g_pluginParms = NULL;
CDlgMain *g_pMainDlg = NULL;
std
::map<HWND, WNDPROC> g_windowInfoMap; CDuiString g_progressCtrlName = _T(""); CDuiString g_tabLayoutCtrlName = _T(""); bool g_bMSGLoopFlag = true; #define NSMETHOD_INIT(parent) {\ g_pluginParms = extra; \ g_hwndParent = parent; \ EXDLL_INIT(); } BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { g_hInstance = (HINSTANCE)hInst; if
(ul_reason_for_call == DLL_PROCESS_ATTACH) { //do what you want at init time. } if (ul_reason_for_call == DLL_THREAD_DETACH || ul_reason_for_call == DLL_PROCESS_DETACH) { //clean up code. } return TRUE; } // NSIS外掛匯出函式,NSIS規定函式宣告格式如下: extern "C" __declspec(dllexport) void
__cdecl /* * 在nsis指令碼中呼叫方法myPlugin::myFunction /NOUNLOAD $2 * /NOUNLOAD 表示呼叫完此方法後不解除安裝這個dll,用於儲存dll的資料 * hwndParent:安裝視窗的控制代碼 * stacktop:nsis傳入的引數堆疊, 通過popint/popstring 可以取出來 * extra:外掛裡面呼叫script的函式就需要用到這個 */ add(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { // == 新增自己程式碼 int i = popint(); int j = popint(); int k = i + j; pushint(k); // == } } //視窗大小 extern "C" __declspec(dllexport) void __cdecl GetDialogSize(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { HWND hwnd = (HWND)popint(); RECT rect; ::GetWindowRect(hwnd, &rect); pushint(rect.bottom - rect.top); pushint(rect.right - rect.left); } } //視窗風格 extern "C" __declspec(dllexport) void __cdecl GetDialogStyle(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { HWND hwnd = (HWND)popint(); int style = (int)::GetWindowLongA(hwnd, GWL_STYLE); pushint(style); } } extern "C" __declspec(dllexport) void __cdecl GetSetupPath(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { char buf[512] = { 0 }; ::GetModuleFileName(NULL, buf, 512); pushstring(buf); } } extern "C" __declspec(dllexport) void __cdecl Trace(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { char buf[1024] = { 0 }; popstring(buf); DUI__Trace(_T("NSISHelper Trace:%s"), buf); } } extern "C" __declspec(dllexport) void __cdecl GetCtrlPos(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { HWND hwnd = (HWND)popint(); RECT rect; GetClientRect(hwnd, &rect); DUI__Trace(_T("%d %d %d %d %d"), hwnd, rect.left, rect.top, rect.right, rect.bottom); POINT lt = { rect.left, rect.top }; POINT rb = { rect.right, rect.bottom }; ::ClientToScreen(hwndParent, &lt); ::ClientToScreen(hwndParent, &rb); pushint(rb.y); pushint(rb.x); pushint(lt.y); pushint(lt.x); } } //=========================================== DUILIB ============================================= //許可協議介面 NSISAPI FindControl(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { char controlName[MAX_PATH]; ZeroMemory(controlName, MAX_PATH); popstring(controlName); CControlUI *pControl = static_cast<CControlUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl(controlName)); if (pControl == NULL) pushint(-1); pushint(0); } //繫結控制元件 NSISAPI OnControlBindNSISScript(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { char controlName[MAX_PATH]; ZeroMemory(controlName, MAX_PATH); popstring(controlName); int callbackID = popint(); g_pMainDlg->SaveToControlCallbackMap(controlName, callbackID);//放入所有控制元件 } NSISAPI ExitDUISetup(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { ExitProcess(0); } static UINT_PTR PluginCallback(enum NSPIM msg) { return 0; } //初始化介面--歡迎介面 NSISAPI InitDUISetup(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); extra->RegisterPluginCallback(g_hInstance, PluginCallback); { CPaintManagerUI::SetInstance(g_hInstance); char buf[512] = { 0 }; ::GetModuleFileName(NULL, buf, 512); int len = strlen(buf); --len; while (len >= 0) { if (buf[len] == '\\') break; buf[len] = '\0'; --len; } sprintf_s(buf, "%sskin", buf); //CPaintManagerUI::SetResourcePath(buf); g_pMainDlg = new CDlgMain(); g_pMainDlg->Create(NULL, _T("標題名字"), UI_WNDSTYLE_FRAME, WS_EX_STATICEDGE | WS_EX_APPWINDOW, 0, 0, 588, 384); g_pMainDlg->CenterWindow(); g_pMainDlg->ShowWindow(FALSE); pushint(int(g_pMainDlg->GetHWND())); } } //顯示頁面 NSISAPI ShowPage(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { g_pMainDlg->ShowWindow(true); //CPaintManagerUI::MessageLoop(); MSG msg = { 0 }; while (::GetMessage(&msg, NULL, 0, 0) && g_bMSGLoopFlag) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } } //設定編輯框內容 NSISAPI SetEdit(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { char buf[512] = { 0 }; popstring(buf); CEditUI *pEdit = static_cast<CEditUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl("btEdit")); if (pEdit) pEdit->SetText(buf); } } //checkBox選型 NSISAPI GetCheckboxStatus(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { char pszName[512] = { 0 }; popstring(pszName);//從nsis獲取控制元件 CCheckBoxUI *pChbAgree = static_cast<CCheckBoxUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl(pszName)); if (!pChbAgree) { pushint(-1); return; } DUI__Trace("%s status:%d", pszName, pChbAgree->GetCheck() ? 1 : 0); pushint(pChbAgree->GetCheck() ? 1 : 0);//輸出給nsis。 } } //設定進度 NSISAPI SetSliderRange(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { char buf[512] = { 0 }; //從nsis接收多個引數 popstring(buf); int iMin = popint(); int iMax = popint(); CProgressUI *pProgress = static_cast<CProgressUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl(buf)); if (pProgress == NULL) return; //設定進度 pProgress->SetMaxValue(iMax); pProgress->SetMinValue(iMin); } } //設定進度值 NSISAPI SetSliderValue(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { char buf[512] = { 0 }; popstring(buf); int iValue = popint(); CProgressUI *pProgress = static_cast<CProgressUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl(buf)); if (pProgress) pProgress->SetValue(iValue); } } //檔案路徑 NSISAPI SetDirValue(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { char buf[512] = { 0 }; popstring(buf); CEditUI *pEdit = static_cast<CEditUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl("editDir")); if (pEdit) pEdit->SetText(buf); } } NSISAPI GetDirValue(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { CDuiString strFolderPath; CEditUI *pEdit = static_cast<CEditUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl("editDir")); if (pEdit) strFolderPath = pEdit->GetText(); pushstring((char*)strFolderPath.GetData()); } } //選擇安裝路徑--點選更改彈出檔案路徑選擇對話方塊 NSISAPI SelectInstallDir(HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { //BROWSEINFO SHGetPathFromIDList 彈出資料夾選擇框 BROWSEINFO bi; memset(&bi, 0, sizeof(BROWSEINFO)); bi.hwndOwner = g_pMainDlg->GetHWND(); bi.lpszTitle = "選擇安裝路徑"; bi.ulFlags = 0x0040; char szFolderPath[MAX_PATH] = { 0 }; LPITEMIDLIST idl = SHBrowseForFolder(&bi); if (idl == NULL) { pushstring(szFolderPath); return; } SHGetPathFromIDList(idl, szFolderPath); CEditUI *pEdit = static_cast<CEditUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl("editDir")); if (pEdit) pEdit->SetText(szFolderPath); pushstring(szFolderPath); } } //更新 NSISAPI StartInstall ( HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters *extra) { NSMETHOD_INIT(hwndParent); { g_bMSGLoopFlag = false; } } BOOL CALLBACK PluginNewWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { BOOL res = 0; std::map<HWND, WNDPROC>::iterator iter = g_windowInfoMap.find( hwnd ); if( iter != g_windowInfoMap.end() ) { if (message == WM_NCCREATE || message == WM_CREATE || message == WM_PAINT || message== WM_NCPAINT) { ShowWindow( hwnd, SW_HIDE ); } else if( message == LVM_SETITEMTEXT ) { ; } else if( message == PBM_SETPOS ) { CProgressUI *pProgress = static_cast<CProgressUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl( g_progressCtrlName )); if( pProgress == NULL ) return 0; pProgress->SetMaxValue( 30000 ); pProgress->SetValue( (int)wParam); if( pProgress->GetValue() == 30000 ) { CTabLayoutUI *pTab = static_cast<CTabLayoutUI *>(g_pMainDlg->GetPaintManagerUI()->FindControl( g_tabLayoutCtrlName )); if( pTab == NULL ) return -1; int currentIndex = pTab->GetCurSel(); DUI__Trace("tabName:%s index:%d",g_tabLayoutCtrlName,currentIndex); pTab->SelectItem(2); } } else { res = CallWindowProc( iter->second, hwnd, message, wParam, lParam); } } return res; }

如下程式碼用於控制控制元件的響應檔案:

#include "DlgMain.h"


CDlgMain::CDlgMain()
{
}

CDlgMain::~CDlgMain()
{

}

void CDlgMain::Notify( TNotifyUI& msg )
{
    std::map<CDuiString, int >::iterator iter = m_controlCallbackMap.find( msg.pSender->GetName() );
    if( _tcsicmp( msg.sType, _T("click") ) == 0 ){
        if( iter != m_controlCallbackMap.end() )
            g_pluginParms->ExecuteCodeSegment( iter->second - 1, 0 );
    }
    else if( _tcsicmp( msg.sType, _T("textchanged") ) == 0 ){
        if( iter != m_controlCallbackMap.end() )
            g_pluginParms->ExecuteCodeSegment( iter->second - 1, 0 );
    } else {
        WindowImplBase::Notify(msg);
    }
}

LRESULT CDlgMain::HandleCustomMessage( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    return 0;
}

void CDlgMain::InitWindow()
{
    CRichEditUI * pRichEdit = static_cast<CRichEditUI*>(m_PaintManager.FindControl(_T("editLicense")));
    if(pRichEdit) {
        HRSRC hRsrc = FindResourceA(CPaintManagerUI::GetInstance(), MAKEINTRESOURCEA(IDR_TEXT_LICENSE), "TEXT");
        if(!hRsrc)
            return;

        DWORD dwSize = SizeofResource(CPaintManagerUI::GetInstance(), hRsrc);
        if(dwSize==0)
            return;

        HGLOBAL hGlobal = LoadResource(CPaintManagerUI::GetInstance(), hRsrc);
        if(!hGlobal)
            return;

        LPVOID lpBuffer = LockResource(hGlobal);
        if(!lpBuffer)
            return;

        pRichEdit->AppendText((char*)lpBuffer);

        FreeResource(hGlobal);
    }
}

可以看出,所有控制元件都通過繫結函式放到了集合中,在notify中找到控制元件來響應動作。
通過pop來接收nsis參進來的引數,push來給nsis傳遞引數。

在nsis中呼叫語法如下所示
這是繫結安裝介面控制元件事件格式

    nsDui::FindControl "btnSelectDir"
    Pop $0
    ${If} $0 == 0
        GetFunctionAddress $0 OnBtnSelectDir
        nsDui::OnControlBindNSISScript "btnSelectDir" $0
    ${EndIf}

在安裝邏輯中使用格式如下

Function OnBtnSelectDir
    nsDui::SelectInstallDir
    Pop $0
FunctionEnd