筆記我使用 NSIS 製作 Windows 安裝檔的過程

NSIS (Nullsoft Scriptable Install System) 是一個建立安裝檔非常好用且免費的軟體。

有鑑於資訊的發展日新月異,在進入主題之前,我先註明一下本次筆記內所使用的軟硬體概況:

  1. iMac Retina 4K,21.5 英寸,2017,3.4GHz CPU,8GB 2400MHz DDR4 RAM,120 GB USBHDD
  2. Windows 10 家用版 (版本 1903)
  3. NSIS 3.07

使用前我試著翻閱中英文的說明,不得不說,這些說明反而讓人望之卻步,正當我苦惱之際,在軟體的介面上,看到「Example scripts」幾個字,順勢點進去之後,我分別開啟 example1 與 example2 這 2 個檔案,觀察一下之後,就直接拿 example2 來改,原因是 example2 把 Uninstaller 的寫法直接放進來,而這正是我需要的,服用的方式,也算容易,用 Notepad++ 打開後,我就用取代的方式,把關鍵字「Example2」換成我要安裝的應用程式名稱,除此之外,還走過下面幾個步驟:

  1. 因為要打包的程式,在執行上的需求,我也就把 AccessControl plug-in 安裝進來。

  2. 本以為,直接解壓到 NSIS 的安裝目錄就可以,結果…,因為編譯錯誤的關係,我發現要自己去把「i386-ansi」與「i386-unicode」裡面的 AccessControl.dll 分別移到「x86-ansi」與「x86-unicode」(這支 AccessControl.dll 在兩個夾子的名字都一樣,因此,移動的時候,要特別注意別搞錯)。

  3. 再經過幾次調校後,就成功打包起來。

  4. 可以安裝是一定要的。

  5. 然後,可以全權讀取與寫入,所以,程式的執行上,也沒有什麼問題。

  6. 最後,跑一下 Uninstaller,確認可以移除檔案、資料夾與捷徑。

最後,考慮到安全性的問題,我就不提供 .nsi 的 Scripts 檔下載,直接貼出來給大家參考,如果剛好符合您大致上的需求,也可以直接複製去服用。

; DNW.nsi
;
; This script is based on example1.nsi, but it remember the directory, 
; has uninstall support and (optionally) installs start menu shortcuts.
;
; It will install DNW.nsi into a directory that the user selects.
;
; See install-shared.nsi for a more robust way of checking for administrator rights.
; See install-per-user.nsi for a file association example.

;--------------------------------

; The name of the installer
Name "DNW"

; The file to write
OutFile "DNWInstaller.exe"

; Request application privileges for Windows Vista and higher
RequestExecutionLevel admin

; Build Unicode installer
Unicode True

; The default installation directory
InstallDir $PROGRAMFILES\DNW

; Registry key to check for directory (so if you install again, it will 
; overwrite the old one automatically)
InstallDirRegKey HKLM "Software\DNW" "Install_Dir"

;--------------------------------

; Pages

Page components
Page directory
Page instfiles

UninstPage uninstConfirm
UninstPage instfiles

;--------------------------------

; The stuff to install

Section "DNW (required)"

  SectionIn RO

  ; Set output path to the installation directory.
  SetOutPath $INSTDIR

  ; Put file there
  File "DNW.exe"
  File "makeData.exe"

  ; Write the installation path into the registry
  WriteRegStr HKLM SOFTWARE\DNW "Install_Dir" "$INSTDIR"

  ; Write the uninstall keys for Windows
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DNW" "DisplayName" "DNW"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DNW" "UninstallString" '"$INSTDIR\uninstall.exe"'
  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DNW" "NoModify" 1
  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DNW" "NoRepair" 1
  WriteUninstaller "$INSTDIR\uninstall.exe"

SectionEnd

Section

  SetOutPath "$INSTDIR\data"
  SetOverwrite on
  File /nonfatal /r "$data\*.*"
  AccessControl::GrantOnFile "$INSTDIR\data" "(S-1-1-0)" "FullAccess"
  AccessControl::GrantOnFile "$INSTDIR\data" "(S-1-5-32-545)" "FullAccess"
# Give all authentificated users (BUILTIN\Users) full access on
# the registry key HKEY_LOCAL_MACHINE\Software\DNW
  AccessControl::GrantOnRegKey \
    HKLM "Software\DNW\data" "(BU)" "FullAccess"
  Pop $0

SectionEnd

; Optional section 
Section "Start Menu Shortcuts"

  CreateDirectory "$SMPROGRAMS\DNW"
  CreateShortcut "$SMPROGRAMS\DNW\Uninstall.lnk" "$INSTDIR\uninstall.exe"
  SetOutPath "$INSTDIR"
  CreateShortcut "$SMPROGRAMS\DNW\DNW.lnk" "$INSTDIR\DNW.exe"

SectionEnd

; Optional section
Section "Desktop Shortcut"

  SetOutPath "$INSTDIR"
  CreateShortcut "$DESKTOP\DNW.lnk" "$INSTDIR\DNW.exe"

SectionEnd

;--------------------------------

; Uninstaller

Section "Uninstall"

  ; Remove registry keys
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DNW"
  DeleteRegKey HKLM "SOFTWARE\DNW"

  ; Remove files and uninstaller
  Delete "$INSTDIR\*.*"
  Delete "$INSTDIR\data\*.*"

  ; Remove shortcuts, if any
  Delete "$SMPROGRAMS\DNW\*.lnk"
  Delete "$DESKTOP\DNW.lnk"

  ; Remove directories
  RMDir "$INSTDIR\DNW\data"
  RMDir "$INSTDIR\DNW"
  RMDir "$INSTDIR"
  RMDir /r "$INSTDIR"

SectionEnd

用我的 iMac 把 Windows 10 安裝到 USB 外接硬碟上

前言:在 Intel Mac 上,把系統安裝到外接硬碟(或是 SSD)裡,有幾個好處,我想到的大致如下:

  1. 減少一個發熱的來源:我的 Mac Mini 動不動就到 6、70 度,用軟體去降溫,就是把內建的風扇開到最大把熱排出去,而外接硬碟,就是讓發熱的硬碟跟主機能夠有一定的距離。同樣的方式,也讓 iMac 背板涼爽不少。
  2. 不用動到主機內的硬碟:硬碟或 SSD 格式化太多次,壽命也就自然跟著短,要換蘋果主機內的零件,要不就是送原廠或是代理商,再不就是自己到拍賣場買工具 DIY。送修就是花錢與等待;DIY 則是要承擔被自己弄壞的風險,無論是那一種,都可能在施工過程,造成一些看得到、看不到,卻不會影響功能的小缺損。外接儲存裝置安裝系統,正好是一個不錯的解套方式。
  3. 移動的作業系統:現在系統內建的驅動程式,大概都夠用,通常能夠抓到大部分的乙太網路卡,有不夠的驅動程式,只要不是太冷門的話,大部分都能夠在網路上找到,那麼,有一顆隨身帶著走的系統,帶到那一部電腦,就能夠有自己專屬的作業環境,方便很多。

這一篇,大概分享一下,如果要設定一個給自己的 Intel Mac 使用的 USB 外接儲存裝置的話,需要準備的軟硬體,以及我自己走過的程序。

軟硬體:

  1. 軟體:
    (1)啟動切換(以前叫 Bootcamp)。
    (2)WinToUSB(Free 版僅支援到家用版)。
    (3)Windows 原版的 ISO 檔。
  2. 硬體:
    (1)Windows 電腦 1 部:我是用 iMac (Retina 4K,21.5 英寸,2017),實際上 Intel Mac 或 Win PC 都可以,重點是裡頭要有 Windows。
    (2)外接硬碟 1 組:空間當然是越大越好,我是拿 1 顆老舊的硬碟,只有 120 GB。
    (3)隨身碟 1 支:建議至少 8 GB。

安裝流程:

  1. 取得驅動程式:這裡要使用的是「啟動切換」。
    (1)點「動作」→「下載 Windows 支援軟體」(下圖)

    (2)點完之後,給 Windows 支援軟體一個下載儲存的路徑與名稱,再放到預先準備的隨身碟上面(下圖)
  2. 使用 WinToUSB 把 Windows 10 預先安裝到外接硬碟上。
    (1)我比較建議另外找一台 Win PC 來處理,我自己是認為這樣比較好跟上面的程序:「取得驅動程式」一起跑。
    (2)這個步驟,因為 WinToUSB 有很多人用過,也有一些分享,所以,我就不再另外寫,建議搜尋文章來參考,或是直接連到我找到的這一篇來參考參考:WinToUSB將Windows安裝到USB外接硬碟 @ 軟體使用教學 :: 隨意窩 Xuite日誌(步驟 1 至 11) 。
  3. 把外接硬碟接到 Mac 上,然後重新開機。
    (1)按住 Opt(Alt) 鍵,到下面這個畫面。

    (2)選擇「EFI Boot」。
  4. 進入 Windows 之前,會有一些設定,可以參考安裝Windows 10 @ 軟體使用教學 :: 隨意窩 Xuite日誌的步驟 16 至 25 。
  5. 進入 Windows 之後,第一件事,就是把剛才那些驅動程式安裝起來,我會建議直接執行 Bootcamp 資料夾裡面的 Setup.exe,把「啟動切換」與驅動程式都一併安裝起來。
  6. 搞定後,就可以在 macOS 與 Windows 10 上面,使用「啟動切換」來選擇要用來開機的系統。

後記:網路上有人分享過,把 iMac 2017 的 CPU 升級,硬碟改成 SSD,卻因為散熱不佳,導致升級感有限,我想,外接硬碟或許會是一個解套的方式。

用我的 iMac 把 Clear Linux 安裝到 USB 外接硬碟上

iMac (Retina 4K,21.5 英寸,2017)

一般而言,要安裝 Linux,又要保留原本的系統,就得再先瞭解一下硬碟分割,通常都會以 GRUB 來管理開機列表,如果這些事情我都不想做,或許,用外接硬碟安裝 Linux 是一個方法。

考量到 Clear Linux 的特性後,我準備好一支 Clear Linux Live USB,還有一顆老舊的硬碟,找到一個很早以前買的外接盒,給它裝在一起,就開啟這一次的安裝之路。

從 Live USB 進到 Clear Linux 之後,我的機器上面,只有 Fusion Drive 與外接硬碟,執行安裝時就選 sdb (也就是外接硬碟),然後,放著讓它跑完整個安裝流程。

因為沒有用 GRUB,所以,重開機之後,要按住 Opt (或 Alt) 鍵來選擇要進入的系統 (…喜不喜歡這樣的方式,就見人見智)。

之前在 Mac Mini 沒有抓到無線網卡,這一次換 iMac 竟然抓得到,至於藍牙的部分,再重開機一次之後,就可以啟用,不過,它一直抓不到我的鍵盤:Logitech Keyboard K480。

我下了一道指令:

lspci -vnn -d 14e4:

觀察之後,決定把這顆硬碟,拿到 Mac Mini 上試跑看看。

實際上是能夠運作的,只是無線網卡的驅動程式,仍然沒有 On 起來,需要另外處理。

以上,零零總總的紀錄,對於想要嘗試 Linux,卻又擔心無法習慣 Linux 後,不容易回到 macOS 的人,希望這一篇能夠做為一個小小的參考。

後記:iMac 上面的 ubuntu 20.04 跟 Windows 10 都被我處理掉,才會有這一篇。

C++ 彰雲嘉區複賽106-4 變位字判斷

題目連結:e524: 106 彰雲嘉區複賽 – Q4 變位字判斷

用map很方便(如果是C++11更方便)

#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<string>
#include<cstdlib>
using namespace std;

int main(){
    ios::sync_with_stdio(0);cin.tie(0);

    string a,b;
    int n;
    cin>>n;
    getline(cin,a);
    while(n--){
        getline(cin,a);
        getline(cin,b);
        map<char,int> ma,mb;

        for(int i=0;i<a.size();i++){
            char c=a[i];
            if(islower(c)){
                ma[c]++;
            }else if(isupper(c)){
                ma[tolower(c)]++;
            }
        }

        for(int i=0;i<b.size();i++){
            char c=b[i];
            if(islower(c)){
                mb[c]++;
            }else if(isupper(c)){
                mb[tolower(c)]++;
            }
        }

        bool isv=true;
        map<char,int>::iterator ta=ma.begin(),tb=mb.begin();
        for(ta=ma.begin();ta!=ma.end() && tb!=mb.end();ta++){
            pair<char,int> pa=(*ta),pb=(*tb);
            if(pa!=pb){
                isv=false;
            }
            tb++;
        }
        if(ma.size()!=mb.size()) isv=false;
        cout<<isv<<"\n";
    }
    return 0;
}

C++ 彰雲嘉區複賽106-3 費波南希數列

題目連結:e523: 106 彰雲嘉區複賽 – Q3 費波南希數列

用到最簡單DP和二分搜(都可以不用)

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
    ios::sync_with_stdio(0);cin.tie(0);

    int n,f[200];
    cin>>n;

    f[0]=-1;f[1]=1;f[2]=1;
    for(int i=3;i<100;i++){
        f[i]=f[i-1]+f[i-2];
        if(f[i]<0) f[i]=0x3f3f3f3f;
    }

    while(n--){
        int a;
        cin>>a;
        int it=lower_bound(f,f+100,a)-f;
        if(f[it]==a){
            cout<<it;
        }else{
            cout<<-1;
        }
        cout<<"\n";
    }
    return 0;
}

C++ 彰雲嘉區複賽106-2 跑長編碼與資料壓縮

題目連結:e522: 106 彰雲嘉區複賽 – Q2 跑長編碼與資料壓縮

字串基本應用

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

string num[10]={"000","001","010","011","100","101","110","111"};

string print(int sum,char bit){
    string re(1,bit);
    re+=num[sum];
    re+=" ";
    return re;
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);

    int iptNum=0;
    cin>>iptNum;

    string bit,out;
    getline(cin,bit);

    while(iptNum--){
        out="";
        getline(cin,bit);
        int j,sum=1;
        long long rate=0;
        for(j=1;j<bit.size();j++){
            if(bit[j]!='0' && bit[j]!='1'){
                cout<<"-1\n";
                j++;
                break;
            }

            if(bit[j-1]==bit[j] && sum<7){
                sum++;
            }else{
                out+=print(sum,bit[j-1]);
                rate+=4;
                sum=1;
            }
        }
        out+=print(sum,bit[j-1]);
        rate+=4;
        rate*=1000;
        rate/=bit.size();
        rate=(rate+5)/10;

        if(bit[j-1]=='0' || bit[j-1]=='1'){
            cout<<out<<rate<<"\n";
        }
    }
    return 0;
}

C++ 彰雲嘉區複賽106-1 三角形

題目連結:e521: 106 彰雲嘉區複賽 – Q1 三角形

簡單的三角形判別

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
    ios::sync_with_stdio(0);cin.tie(0);

    int n;
    cin>>n;
    while(n--){
        int d[3];
        for(int i=0;i<3;i++) cin>>d[i];
        sort(d,d+3);
        if(d[0]+d[1]<=d[2]){
            cout<<0<<"\n";
        }else{
            cout<<1<<" ";
            if(d[0]==d[1] || d[1]==d[2]) cout<<1;
            else cout<<0;
            cout<<"\n";
        }
    }
    return 0;
}

把 Clear Linux 安裝到我的 Mac mini (Late 2014)

Mac mini (2014 年末) 這一部機器,我剛入手的時候,主要拿來上網、文書處理以及製作網路用圖片,系統版本一路從 OS X Yosemite 到 macOS Mojave,效能就是越來越不好,即使降刷版本到 OS X El Capitan 也快不到多少。

況且,有不少程式,也都不再支援這些舊的系統,或是,就算可以讓我們安裝舊版本的程式,功能要不是有些欠缺,就是一直提醒我們要更新系統與程式。

煩啊~

所以,不久前我安裝了幾支 Linux Distro,目的就是想要看看能不能讓這部 Mac mini 能夠在保有效能的前提下,達到上面說的幾個功能,然而,我都是在 Virtual Box 上面安裝,真要安裝到 Mac mini 上面之前,我對「Linux 驅動程式支援普遍來說都不足夠」,仍有一定程度的心理準備。

蘋果有不少零件都是使用 Broadcom 的,包括:讀卡機、網路卡等,這一點我認為即使不是跑超高速網路 (Gigabit Ethernet) 也沒有關係,只要能夠上網就好;無線網路就算抓不到,我也不打算去處理;藍牙也是可有可無,都無關緊要。

安裝之前,我做了 2 件事。

  1. 把系統上的 iCloud 先登出。
  2. 把硬碟裡面的資料全備出來,同時看一下硬碟空間概況,因為…,我其實有點想要安裝成雙系統,後來覺得切來切去也不方便,也就做罷。

至於選用 Linux Distro 的部分,我想在 Clear Linux 與 ubuntu 20.04 兩者之間,實際在 Mac mini 上面安裝測試過後,才做出決定,我比較的點分別如下:

  1. 效能:第一印象是用我的感覺來評分,Clear Linux 跑起來的感覺有比較順,我再打開「系統監控」,Clear Linux 在記憶體的使用上,含蓄不少,而且桌面動畫也是 Clear Linux 比較沒有卡頓的情形。
  2. 使用者介面:ubuntu 的介面用起來比較順手,不過,我就是想讓自己知道正在使用一個不一樣的系統,算是我的小癖好。
  3. 藍牙連線:本來像我上面說的,對驅動程式並沒有什麼期待,剛安裝好的 Clear Linux,藍牙是沒有反應的,系統甚至顯示成飛航模式,我安裝 hardware-wifi 後,WiFi 沒有順利搞定,倒是藍牙自動啟用,啟用後的藍牙連線表現比 ubuntu 穩定,ubuntu 常常會跟我目前的滑鼠斷訊,Clear Linux 非但不會,還可以在系統睡眠時喚醒(但,我還是把無線裝著,反正,這一台應該其他的 USB 頂多就是再插個隨身碟與隨身硬碟,插槽肯定夠用)。
  4. 無線上網:另外一個要說明的是 ubuntu 可以透過圖形介面程式,把 WiFi 的驅動裝進系統,相較之下是方便很多,只可惜,我一開始就決定有線方式連網,對 WiFi 沒有特別期待。

綜合以上,我就選用 Clear Linux。


目前在系統上安裝了幾個程式,分別是:

  1. Ungoogled Chromium
  2. Google Chrome
  3. Inkscape
  4. Steam
  5. VLC

LibreOffice 還沒有安裝,原因是我想撐到非得開 Office 的檔案時,再來處理。再說,要安裝的話,用 swupd 滿快就可以搞定的。

至於嘸蝦米呢?

在 Clear Linux 上面,我直接選用 iBus 的官方表格來服用。


C++ 最短路徑問題 Dijkstra應用

題目連結:Sprout Online Judge No. 393

這題用 Bellman-Ford 必定 TLE

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int dr[4]{1,0,-1,0},dc[4]{0,1,0,-1};

int dt[2005][2005];
string mp[2005];
bitset<2005> v[2005];

struct position{
    int r,c;
    bool operator==(position a){
        return r==a.r && c==a.c;
    }
};
bool operator<(position a,position b){
    return dt[a.r][a.c]>dt[b.r][b.c];
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);

    int n,m,ans=INF;
    cin>>n>>m;

    for(int i=0;i<n;i++) for(int j=0;j<m;j++) dt[i][j]=INF;

    for(int i=0;i<n;i++){
        cin>>mp[i];
    }

    position ps,pe;
    cin>>ps.r>>ps.c;ps.r--;ps.c--;
    cin>>pe.r>>pe.c;pe.r--;pe.c--;
    dt[ps.r][ps.c]=0;

    priority_queue<position> bfs;
    bfs.push(ps);

    int ct;
    position now,next,mnPos;
    while(!bfs.empty()){
        now=bfs.top();
        bfs.pop();
        v[now.r][now.c]=1;
        for(int i=0;i<4;i++){
            next.r=now.r+dr[i];
            next.c=now.c+dc[i];
            if(next.r>=0 && next.c>=0 && next.r<n && next.c<m){
                ct=dt[now.r][now.c];
                if(mp[next.r][next.c]=='.')
                    ct++;
                if(pe==next)
                    ans=min(ans,ct);
                if(!v[next.r][next.c]){
                    v[next.r][next.c]=1;
                    dt[next.r][next.c]=ct;
                    bfs.push(next);
                }
            }
        }
    }
    cout<<ans<<"\n";
    return 0;
}

HackerRank Sorting 5.Counting Inversions

題目連結:Merge Sort: Counting Inversions | HackerRank

這就是反序數對了(Merge Sort)

vector<long> dt;

long msort(long l,long r){
    if(l>=r-1) return 0;
    long mid=(l+r)/2,j=mid;
    long sum=msort(l,mid)+msort(mid,r);
    long tmp[r-l],k=0;
    for(long i=l;i<mid;i++){
        while(j<r && dt[j]<dt[i]){
            tmp[k++]=dt[j++];
        }
        tmp[k++]=dt[i];
        sum+=j-mid;
    }
    while(j<r){
        tmp[k++]=dt[j++];
    }
    j=0;
    for(long i=l;i<r;i++){
        dt[i]=tmp[j];
        j++;
    }
    return sum;
}