• Inno Setup界面美化


    Inno Setup用Delphi写成,其官方网站同时也提供源程序免费下载。它虽不能与Installshield这类恐龙级的安装制作软件相比,但也当之无愧算是后起之秀。Inno Setup是一个免费的安装制作软件,小巧、简便、精美是其最大特点,支持pascal脚本,能快速制作出标准Windows2000风格的安装界面,足以完成一般安装任务。

    公司目前是使用的Installshield,但是我们项目原先基于innosetup做的打包脚本,所以我也就接着用InnoSetup来开发了。

    一开始以为innoseup想要做好看的界面,像百度360腾讯这些大厂的安装程序那样的界面有些不可能,但跟着https://blog.csdn.net/linxinfa/article/details/108995508?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-2&spm=1001.2101.3001.4242这篇文章的指引,发现innosetup真的强大,理论上只要能封装dll,也许拿QT写个安装界面给innosetup用也不是不可能。

    下面是我通过网上一些现成的资源改的安装脚本的实际运行GIF,为避免误会,我抹去了原先产品的logo,项目下载链接:https://files-cdn.cnblogs.com/files/suxia/test.rar

    基础知识网上有大把教程,我就不再赘述了。在开始innosetup美化工作的前面,得先扩充下一些API接口,单纯靠innosetup是做不出好看的界面的,我们得准备一些接口,主要依赖于botva2.dll和innocallback.dll,前者负责按钮图标选择框等美化控件的创建,后者则是完善这些控件操作后的回调函数。

    //*****************************************************************************************************************************************************************************************************************************
    //系统api接口
    function  SetLayeredWindowAttributes(hwnd:HWND; crKey:Longint; bAlpha:byte; dwFlags:longint ):longint;external  'SetLayeredWindowAttributes@user32 stdcall'; //函数声明
    function  SetWindowLong(Wnd: HWnd; Index: Integer; NewLong: Longint): Longint; external 'SetWindowLongA@user32.dll stdcall';
    function  CallWindowProc(lpPrevWndFunc: Longint; hWnd: HWND; Msg: UINT; wParam, lParam: Longint): Longint; external 'CallWindowProcA@user32.dll stdcall';
    function  SetTimer(hWnd: LongWord; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external 'SetTimer@user32.dll stdcall';
    function  KillTimer(hWnd: LongWord; nIDEvent: LongWord): LongWord; external 'KillTimer@user32.dll stdcall';
    function  GetWindowLong(Wnd: HWnd; Index: Integer): Longint; external 'GetWindowLongA@user32.dll stdcall';
    function  ReleaseCapture(): Longint; external 'ReleaseCapture@user32.dll stdcall';
    function  CreateRoundRectRgn(p1, p2, p3, p4, p5, p6: Integer): THandle; external 'CreateRoundRectRgn@gdi32 stdcall';
    function  SetWindowRgn(hWnd: HWND; hRgn: THandle; bRedraw: Boolean): Integer; external 'SetWindowRgn@user32 stdcall';
    function  LoadCursorFromFile(FileName: String): Cardinal; external 'LoadCursorFromFileA@user32.dll stdcall';
    function  DeleteObject(p1: Longword): BOOL; external 'DeleteObject@gdi32.dll stdcall';
    function  GetPM(nIndex:Integer):Integer; external 'GetSystemMetrics@user32.dll stdcall';
    function  GetSystemMetrics(nIndex: Integer): Integer; external 'GetSystemMetrics@user32.dll stdcall';
    //*****************************************************************************************************************************************************************************************************************************
    //botva2.dll
    function  ImgLoad(Wnd :HWND; FileName :PAnsiChar; Left, Top, Width, Height :integer; Stretch, IsBkg :boolean) :Longint; external 'ImgLoad@{tmp}otva2.dll stdcall delayload';
    procedure ImgSetVisibility(img :Longint; Visible :boolean); external 'ImgSetVisibility@{tmp}otva2.dll stdcall delayload';
    procedure ImgApplyChanges(h:HWND); external 'ImgApplyChanges@{tmp}otva2.dll stdcall delayload';
    procedure ImgSetPosition(img :Longint; NewLeft, NewTop, NewWidth, NewHeight :integer); external 'ImgSetPosition@files:botva2.dll stdcall';
    procedure ImgRelease(img :Longint); external 'ImgRelease@{tmp}otva2.dll stdcall delayload';
    procedure gdipShutdown;  external 'gdipShutdown@{tmp}otva2.dll stdcall delayload';
    function  BtnCreate(hParent:HWND; Left,Top,Width,Height:integer; FileName:PAnsiChar; ShadowWidth:integer; IsCheckBtn:boolean):HWND;  external 'BtnCreate@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetText(h:HWND; Text:PAnsiChar);  external 'BtnSetText@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetVisibility(h:HWND; Value:boolean); external 'BtnSetVisibility@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetFont(h:HWND; Font:Cardinal); external 'BtnSetFont@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetFontColor(h:HWND; NormalFontColor, FocusedFontColor, PressedFontColor, DisabledFontColor: Cardinal); external 'BtnSetFontColor@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetEvent(h:HWND; EventID:integer; Event:Longword); external 'BtnSetEvent@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetCursor(h:HWND; hCur:Cardinal); external 'BtnSetCursor@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetEnabled(h:HWND; Value:boolean); external 'BtnSetEnabled@{tmp}otva2.dll stdcall delayload';
    function  GetSysCursorHandle(id:integer):Cardinal; external 'GetSysCursorHandle@{tmp}otva2.dll stdcall delayload';
    function  BtnGetChecked(h:HWND):boolean; external 'BtnGetChecked@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetChecked(h:HWND; Value:boolean); external 'BtnSetChecked@{tmp}otva2.dll stdcall delayload';
    procedure CreateFormFromImage(h:HWND; FileName:PAnsiChar); external 'CreateFormFromImage@{tmp}otva2.dll stdcall delayload';
    procedure BtnSetPosition(h:HWND; NewLeft, NewTop, NewWidth, NewHeight: integer);  external 'BtnSetPosition@{tmp}otva2.dll stdcall delayload';
    procedure ImgSetVisiblePart(img:Longint; NewLeft, NewTop, NewWidth, NewHeight : integer); external 'ImgSetVisiblePart@files:botva2.dll stdcall';
    //*****************************************************************************************************************************************************************************************************************************
    //innocallback.dll
    type
      TBtnEventProc = procedure (h:HWND);
      TPBProc = function(h:hWnd;Msg,wParam,lParam:Longint):Longint;  //百分比
      TTimerProc = procedure(h:longword; msg:longword; idevent:longword; dwTime:longword);
    function  CallBack_Button(Callback: TBtnEventProc; ParamCount: Integer): Longword; external 'wrapcallback@{tmp}innocallback.dll stdcall delayload';
    function  CallBack_ProgressBar(P:TPBProc;ParamCount:integer):LongWord; external 'wrapcallback@files:innocallback.dll stdcall';
    function  CallBack_Timer(callback: TTimerProc; Paramcount: Integer): Longword; external 'wrapcallback@files:innocallback.dll stdcall';
    //******************************************************************************************************************************************************************************************************************************
    //退出当前安装或者卸载进程
     procedure ExitProcess(exitCode:integer);external 'ExitProcess@kernel32.dll stdcall';
    //******************************************************************************************************************************************************************************************************************************

    按我的理解,innosetup分成两条线,一条线就是显示给我们用户看的界面,还有一条线就是他的后台处理逻辑,他后台不变的就是安装和卸载的前期准备工作,还有就是安装和卸载的执行过程,这个过程我们是基本动不了什么的,innosetup会把file字段里提供的文件解压到我们的安装目录,卸载的时候会把安装的文件再删除。最后一个过程就是我们安装卸载完成后的那段时间,用户不点击完成,程序不回结束。像下面图一样,画的有些难看,凑合看下就行了,安装的三个大部分都是单向的,一旦进入到下一步了就不能回到上一步了,但是每个大过程中的每个界面是双向的,可以回到上一步,这应该是好理解的

    后台逻辑这条线什么时候跳转到下一步,完全是由innosetupde的界面控制着。比如下面这段代码,是立即安装按钮的按下的回调函数,这时候我们会调用WizardForm.NextButton.OnClick(WizardForm);这一句话等同于按下安装界面的下一步按钮,让后台逻辑进入到安装的执行过程,就是那个有进度条的界面。

    //点击立即安装按钮
    procedure btn_InstallNowOnClick(hBtn:HWND);
    begin
      if BtnGetChecked(chk_License) then
      begin
        if edit_Path.Text = '' then
        begin
          SuppressibleMsgBox('路径名不能为空!', mbConfirmation, MB_OK, IDOK);
        end
        else
        begin
          WizardForm.NextButton.OnClick(WizardForm);
        end;
      end
      else
      begin
        SuppressibleMsgBox('请先阅读并同意用户协议!', mbConfirmation, MB_OK, IDOK);
      end;

    为了做好看的界面,innosetup提供给我们的页面基本可以都隐藏掉,当然你也可以选择魔改原来的界面,这都是一样的,看你选择,然后剩下来就是我们的界面美化工作,我们只需要在我们的界面上的每个按钮响应事件里控制着innosetup后面那条处理逻辑线的运行就行了,安装程序开始就是选择安装目录界面或者一键安装界面,这时候没什么处理逻辑,就是实例化一个页面,然后把我们的图片元素都放到对应的位置,没什么技术含量,可以参考代码。

    //**********************************************************************************************************************
    //安装程序主体部分
    //使用这个事件函数启动时改变向导或向导页。你不能在它触发之后使用 InitializeSetup 事件函数,向导窗体不退出。
    procedure InitializeWizard();
    begin
      //设置页面属性
      WizardForm.OuterNotebook.hide;
      WizardForm.Bevel.Hide;
      WizardForm.BorderStyle:=bsnone;
      WizardForm.Position:=poScreenCenter;
      WizardForm.Width:=650;
      WizardForm.Height:=508;
      WizardForm.Color:=clWhite ;
      WizardForm.OnMouseDown := @WizardMouseDown;
      //从[Files]段提取指定的文件到临时目录中
      ExtractTemporaryFile('btn_n.png');
      ExtractTemporaryFile('btn_complete.png');
      ExtractTemporaryFile('btn_setup.png');
      ExtractTemporaryFile('xy.png');
      ExtractTemporaryFile('bigbg.png');
      ExtractTemporaryFile('btn_Browser.png');
      ExtractTemporaryFile('editbox_bkg.png');
      ExtractTemporaryFile('loadingbk.png');
      ExtractTemporaryFile('loading.png');
      ExtractTemporaryFile('license.png');
      ExtractTemporaryFile('loading_pic1.png');
      ExtractTemporaryFile('loading_pic2.png');
      ExtractTemporaryFile('loading_pic3.png');
      ExtractTemporaryFile('loading_pic4.png'); 
      ExtractTemporaryFile('checkbox.png');
      ExtractTemporaryFile('checkboxdeep.png'); 
      ExtractTemporaryFile('loading_pic.png');
      ExtractTemporaryFile('finish_bg.png');
      ExtractTemporaryFile('btn_close.png');
      ExtractTemporaryFile('btn_min.png');
      //ExtractTemporaryFile('vcredist_x64.exe');
      //ExtractTemporaryFile('vcredist_x86.exe');
      //ExtractTemporaryFile('ConsoleApplication12.exe');
      //初始化背景页面
      img_Background:=ImgLoad(WizardForm.Handle,ExpandConstant('{tmp}xy.png'),0,0,650,450,false,true);
      bool_custom:=true;
      //初始化编辑框背景
      img_EditBox:= ImgLoad(WizardForm.Handle,ExpandConstant('{tmp}editbox_bkg.png'),60,380,436,21,false,false);
      ImgSetVisibility(img_EditBox,false)
      //初始化关闭按钮
      btn_Close:=BtnCreate(WizardForm.Handle,627,8,17,15,ExpandConstant('{tmp}tn_close.png'),1,False)
      BtnSetEvent(btn_Close,BtnClickEventID,CallBack_Button(@btn_CloseOnClick,1));
      //初始化最小化按钮
      btn_Min:=BtnCreate(WizardForm.Handle,607,8,17,15,ExpandConstant('{tmp}tn_min.png'),1,False)
      BtnSetEvent(btn_Min,BtnClickEventID,CallBack_Button(@btn_MinOnClick,1));
      //初始化立即安装按钮
      btn_InstallNow:=BtnCreate(WizardForm.Handle,225,308,199,58,ExpandConstant('{tmp}tn_setup.png'),1,False)
      BtnSetEvent(btn_InstallNow,BtnClickEventID,CallBack_Button(@btn_InstallNowOnClick,1));
      //初始化浏览按钮
      btn_Browser:=BtnCreate(WizardForm.Handle,520,375,82,32,ExpandConstant('{tmp}tn_Browser.png'),1,False)
      BtnSetEvent(btn_Browser,BtnClickEventID,CallBack_Button(@btn_BrowserOnClick,1));
      BtnSetVisibility(btn_Browser,false)
      //初始化自定义安装按钮
      btn_CustomInstall:=BtnCreate(WizardForm.Handle,560,421,75,15,ExpandConstant('{tmp}tn_n.png'),1,False)
      BtnSetEvent(btn_CustomInstall,BtnClickEventID,CallBack_Button(@btn_CustomInstallOnClick,1));
      //初始化用户协议按钮和选择框
      btn_License:=BtnCreate(WizardForm.Handle,100,421,54,15,ExpandConstant('{tmp}license.png'),1,False)
      BtnSetEvent(btn_License,BtnClickEventID,CallBack_Button(@btn_LicenseOnClick,1));
      chk_License:=BtnCreate(WizardForm.Handle,22,421,15,15,ExpandConstant('{tmp}checkboxdeep.png'),4,TRUE)
      //设置安装进度条监听
      PBOldProc:=SetWindowLong(WizardForm.ProgressGauge.Handle,-4,CallBack_ProgressBar(@PBProc,4));
      //初始化定时器
      js1:=0
      js2:=650
      timer_Page := TTimer.Create(WizardForm);
      timer_Page.OnTimer := @func_Timer;
      //初始化安装路径编辑框
      edit_Path:= TEdit.Create(WizardForm);
      with edit_Path do
      begin
        Parent := WizardForm;
        text :=WizardForm.DirEdit.text;
        Font.Name:='宋体'
        BorderStyle:=bsNone;
        SetBounds(60,385,436,15)
        OnChange:=@edit_PathChange;
        Color := $00FFE2D0
        TabStop :=false;
      end;
      edit_Path.Hide;
      ShapeForm(WizardForm, WINDDOW_RADIUS);
      //应用图片变化
      ImgApplyChanges(WizardForm.Handle)
    end;

    这里面有些需要注意的点,你要使用的图片资源,要像下面这样设计:

    按钮从上到下要有四张,分别对应按钮什么都不做的样子,鼠标悬浮在按钮上方的样子,按钮被按下的样子,还有按钮被禁用的样子。

    选择框则对应八张,上面四张是选择框没被选中时候对应鼠标什么都没做的样子,鼠标悬浮在选择框上方的样子,鼠标按下选择框的样子,选择框被禁用的样子。然后下面四张是选择框选中时对应的四种样子。

    设计是这样设计的,具体为什么这样设计,就是botva2.dll这个dll的接口要求,我也没深究过。

                                                 

    然后还有一个计数器的概念,这个东西很关键,控制着innosetup的动画部分,后面我们会专门介绍下安装过程中的那个图片滚动动画是怎么做的,那是我们界面美化的最难的部分,需要专门开一个贴讲下逻辑。

    注意每次设置完界面都要调用下ImgApplyChanges(WizardForm.Handle),这个会通知页面刷新,不然按钮的创建移位等效果都不会生效。

  • 相关阅读:
    ffmpeg常用命令集合
    Django传文件(FILES用法)
    Flask传文件(files用法)
    docker 部署redis服务
    tomcat启动报错ContainerBase.addChild: start
    Echarts折线图 y轴刻度数值与实际值不符解决方法
    春招日记
    Leetcode5700. 使所有区间的异或结果为零(DP)
    VS2015拖动调整代码窗口奔溃
    图床搭建
  • 原文地址:https://www.cnblogs.com/suxia/p/14167606.html
Copyright © 2020-2023  润新知