2008年12月30日 星期二

[Inno Setup] 如何讓程式在windows啟動後自動執行

有兩種做法,一種是直接將捷徑放在"Start Menu"的"啟動目錄"裡

[Icons]
Name: "{commonstartup}\yourapp"; Filename: "{app}\yourapp.exe"; WorkingDir: {app};
Name: "{userstartup}\yourapp"; Filename: "{app}\yourapp.exe"; WorkingDir: {app};

{commonstartup}是放在共用的"啟動目錄",所有使用者共用這一個捷徑。
{userstartup}則是放在該user的"啟動目錄"。

另一種做法是寫registry

[Registry]
Root: HKCU; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; ValueType: string; ValueName: "yourapp"; ValueData: "{app}\yourapp.exe"; Flags: uninsdeletevalue;


上面兩種方法都可以讓你的程式在每次啟動windows時自動執行。

2008年12月19日 星期五

[Inno Setup]檢查程式是否已安裝,並且詢問user是否要安裝舊版程式

我有一個用COM寫的dll,因為會給很多支程式一起發佈,這種清況下有可能使用者剛好安裝到舊版本的dll,雖然這樣不是什麼大問題,但是我還是希望使用者盡量使用最新的dll(不安裝舊版程式),因此我在Inno Setup加入一小段pascal code去解決這個問題。


[Setup]
; your setup setting

[Code]
function InitializeSetup(): Boolean;
var version: String;
begin
result := true;
if RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\yourapp', 'DisplayVersion', version) = true then begin
if CompareStr(version, '1.1.5.0') > 0 then ; 1.1.5.0是你最新版的version
if WizardSilent() = false then begin ; Silent就預設不安裝舊的
if MsgBox('Would you like to install the older version?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDNO then begin ;詢問user
result := false;
end;
end else begin
result := false;
end;
end;
end;


Note:
1. 原本是以為Inno Setup有這個選項可以設定,不過找了一下還是沒找到,所以就自己手動弄了.. XD

2008年12月18日 星期四

VC9 SP1 Hotfix For The vector> Crash

今天在Visual C++ Team Blog 看到 這一篇,官方有針對VC9 的SP1釋出一個修正檔,主要是根據這邊的bug list修正了底下5個問題

1. function<FT>::swap() was broken by the Small Functor Optimization in VC9 TR1 (the Feature Pack). This broke vector<function<FT>> in VC9 SP1. "Broken" meant "compiling but crashing".

2. vector<pair<X, string>> nonconformantly required X to have a default constructor. (This is a specific example of a general bug: vector<pair<string, X>>, vector<pair<X, vector<int>>>, etc. were also affected.)

3. vector<tuple<X, Y, string>> nonconformantly required X and Y to have default constructors. (This is a specific example of a general bug, see above.)

4. vector<array<X, N>> nonconformantly required X to have a default constructor.

5. Random distributions were broken, triggering infinite loops and emitting bogus results.

假如你有安裝VC9 sp1的話,可以更新一下 :)


References:

1. Visual C++ Team Blog

2. VC9 SP1 Hotfix For The vector> Crash

3. TR1 fixes in VC9 SP1

2008年12月16日 星期二

一個免費的安裝程式 - Inno Setup

如果你還在找一個適合的安裝程式 (ex: Visual C++ 、BCB的安裝程式),且不想用原本complier附的安裝程式的話,可以試試這一套「Inno Setup」,他是免費的,而且只需要寫一些簡單的script,我個人用起來還簡單好用的。(我有用過在Visual C++ 2005, 2008以及BCB6和WinCE的安裝)

底下我將比較常用的script列出來
[Setup]                           ; 設定頁面
AppName=MyApp ; 程式名稱
AppVerName= MyApp 2.0 ; 程式版本名稱
AppVersion=2.0.0.456 ; 版本
DefaultDirName={pf}\MyApp ; 安裝的預設目錄 {pf}是指Program Files
DefaultGroupName=MyApp ; 在「開始/程式集」的目錄
Compression=lzma ; 壓縮的方式
SolidCompression=yes ; 安裝程式是分佈多個檔案或是合併成一個檔案
OutputDir=..\..\bin\ ; 輸出安裝程式的目錄
OutputBaseFilename=MyApp 2.0.0 ; 輸出安裝程式的名稱

[Files] ; 你要發佈的檔案清單
; Source: 來源路徑
; DestDir: 目地目徑,{app}可能是DefaultDirName或user自訂
Source: MyApp.exe; DestDir: {app};
Source: MyApp.dll; DestDir: {app};
Source: help.txt; DestDir: {app}\help; Flags:

[Dirs] ; 要建立的目錄
; Name: 要建立的目錄路徑,其中{userappdata}是指user的My Documents目錄
Name: "{userappdata}\MyApp";

[Icons] ; 要建立的shortcut
; Name: shortcut的名稱,其中{group}可能是DefaultGroupName或user自訂
; Fileanme: shortcut要對映的名稱
; WorkingDir: shortcut的預設執行目錄 (這要記得加喔!)
Name: "{group}\MyApp"; Filename: "{app}\MyApp.exe"; WorkingDir: {app};

; {uninstallexe}是指uninstall的路徑
Name: "{group}\Uninstall"; Filename: "{uninstallexe}"; WorkingDir: {app};

[Run] ; 要執行的程式
; Filename: 程式名稱
; Description: 你的程式描述
; Flags:
; postinstall: 安裝程式後才會執行
; nowait: 安裝程式不會等待程式執行完畢,並且繼續下一步動作
Filename: "{app}\MyApp.exe"; Description: "Launch application"; Flags: postinstall nowait

更多設定項目請參考Inno Setup附的example或是help。
或是參考FAQKnowledge BaseMailing List


Inno Setup可以透過簡單的pascal語法來寫一些程式,例如自訂畫面、資源檢查或是檔案的操作,如果嫌Inno Setup的指令太少,那你可以外掛一些用C寫的dll來和pascal配合,來增加Inno Setup的指令及功能。
底下是一個簡單在Welcome的畫面時顯示MsgBox。

procedure CurPageChanged(CurPageID: Integer);
begin
if (CurPageID= wpWelcome) then begin
MsgBox('Welcome to Inno Setup.', mbInformation, MB_OK);
end;
end;


另外,網路上很多人為Inno Setup寫了一些額外的套件,可以直接使用UI來設定script或是增加#include來引用已寫好的script等功能,有興趣的可以下載Third-Party QuickStart Packs來玩玩。


結論:
Inno Setup是一個免費又簡單好用的安裝程式。 :)


下載 (Downloads):
1. Inno Setup Stable Release - 主程式
2. Third-Party QuickStart Packs - 包含常用的third-party元件

一些Inno Setup的技巧:
1. Inno Setup 包裝 Visual C++ runtime library
2. Inno Setup 如何在安裝程式前,先檢查並關閉之前的程式
3. more

Reference:
1. Inno Setup

2008年12月15日 星期一

Discovery廣告- 世界無比精采! Boom De Ah Dah



歌詞:
Astronaut1:It never gets old, uh?
太空人1:地球從不會變老,對吧?
Astronaut2:Nope.
太空人2:不。
Astronaut1:it kinda make you want to break into songs?
太空人1:真令人想高歌一曲不是嗎?
Astronaut2:Yep.
太空人2:就是啊。

I love the mountains
我愛高山。
I love the clear blue skies
我愛藍天
I love big bridges(建築奇觀)
我愛大橋
I love when great whites fly
我愛飛躍的大白鯊

I love the whole world
我愛全世界
and all its sights and sounds
我愛這個聲色世界

Boom De Yada(這邊好像是漁人的搏鬥)
Boom De Yada
Boom De Yada
Boom De Yada﹗

I love the ocean
我愛海洋
I love the dirty things(幹盡苦差事的主持人Mike Rowew)
我愛苦差事
I love to go fast
我愛速度感
I love Egyptian kings
我愛埃及法老王

I love the whole world
我愛全世界
and all its craziness
我愛這瘋狂的世界

Boom De Yada
Boom De Yada
Boom De Yada
Boom De Yada(新時代武器的Richard Machowicz)

I love tornado(追風日誌)
我愛龍捲風
I love arachnid(Bear Grylls荒野求生密技)
我愛蜘蛛
I love hot magma
我愛岩漿
I love the giant squids
我愛大章魚

I love the whole world
我愛全世界
it's such a brilliant place
這世界實在太讚

Boom De Yada(流言終結者的亞當和傑米)
Boom De Yada(霍金博士w)
Boom De Yadaaaaaaaa
Boom De Yada
Boom De Yada
Boom De Yada
Boom De Yada
Boom De Yada
Boom De Yada
Boom De Yada

2008年12月13日 星期六

base class的virtual destructor的補充

我是習慣讓base class有virtual destructor,因為大部分我都是需要做多型刪除(polymorphic deletion)的,主要觀念是從C++編程規範來的,我節錄一下書裡編號50的準則


令base class的解構式為public virtual或protected nonvirtual
摘要(Summary):
刪還是不刪,真讓人頭疼:如果允許透過pointer to Base來刪除物件,那麼Base的解構式必須是public virtual函式。否則它應該是protected nonvirtual函式。


意思指的是


客戶要不就應該透過pointer to Base進行多型刪除(polymorphic deletion),要不就不該刪除。


如果允許多型刪除的話,那解構子就應該是public且為virtual function。
反之若不希望客戶進行多型刪除的話,那解構子就設計為nonpublic且nonvirtual。


上述這種判斷方式會比「將所有base class都固定加入virtual dtor」來得好。 :)


例外狀況


你可以讓解構式是public且nonvirtual,但必須在文件中清楚說明:B的更深層衍生物件(further-derived objects)絕對不能被多型使用(當作B)。std::unary_function就是這樣。





Notes:
1. base class指的是那些"準備要被繼承的底層類別"。

2. 並不是要將所有class都加入virtual dtor,而是如果該class可能會被繼承且需要支援多型刪除的話,那就加入virtual dtor,反之,如果該class根本不會(或是不希望)被繼承的話,那就不需要加入virtual dtor。

3. virtual dtor常常可以用來識別class能否被繼承。

4. 至於像unary_function這種class,除了文件註明外,解構子可以考慮設計為protected。 (這個書上也有提到)

Reference:
1: C++編程規範 - 101個準則、指導方針,和最佳實踐 - 侯捷/陳碩 譯
2.
Re: ‘dynamic_cast’ : ‘A’ is not a polymorphic type - http://fsfoundry.org/codefreak/
3.
http://legnaleurc.blogspot.com/2008/11/2.html
4.
'dynamic_cast' : 'A' is not a polymorphic type


因為感覺這一篇寫得不夠完整且完整表達我的想法,因此我有稍微修改過。

2008年12月9日 星期二

Ultimate Toolbox原來已經是免費的了啊

最近工作上需要使用TreeCtrl做多選(Multi-Select)的操作,因為MFC的CTreeCtrl預設是不支援多選的,因此我試著在CodeProject或是CodeGuru找尋解答,以前一直都是直接套用別人改寫的元件,但是也因為該元件並非正式的,常常都是沒有文件而且缺少維護,存在著很多bug,而我其實也沒啥時間去解那些bug,後來想說來查一下Ultimate Toolbox這套商業元件目前的售價好了,再來跟我主管商量看看要不要撥經費來購買,後來發現Ultimate Toolbox, Grid, TCP/IP都已經變成免費而且open source的了,以前小時候(咦?)的印象他是商業套件而且是需要錢的啊,怎麼會突然變免費的呢(2007年8月開始open source的),後來上網查了一下,我猜想可能是因為他的另一對手BCGSoft和微軟合作的關系,並且在Visual C++ 2008 Feature Pack加入大量且免費的BCGSoft元件,因此逼得使Ultimate Toolbox也必須改變策略而選擇open source一途吧。

也正好,Ultimate Toolbox剛好有我需要的TreeCtrl,而且比我現在使用的網路免費的元件還強得多很多,真是謝天謝地啊 XD

後記
其實Visual Studio 2008 Service Pack 1(包含Visual C++ 2008 Feature Pack)裡加入了大量的BCG元件(MFC Hierarchy Chart),用起來方便很多,不再需要為GUI來煩惱,也不太需要上網四處搜尋元件了,可以專注在更核心的問題。
不過,為什麼還是沒有高階(完整)的TreeCtrl呢?? =.=.. 我只是要做一個Multi-Select而已啊
結果還要花時間再找解決方案以及編譯Ultimate Toolbox.. orz


ps. 我還真是後知後覺。

2008年12月7日 星期日

C++的列舉型別的type-safe問題 (2)

繼上一篇「C++的列舉型別的type-safe問題」,其實基本上我會找到bitwise這個enum class template是因為我需要enum的bitwise operations,因為C++原本的eums的bitwise operations有一些type-safe上的問題,以及缺少一些bitwsie operations。


enum Style
{
StyleA = 0x01,
StyleB = 0x02,
StyleC = 0x04,
}

enums Area
{
Area1,
Area2,
Area3,
Area4,
}

// pass
Style s1 = StyleA;

// error, can't convert int to enums 'Style'
s1 = StyleA | StyleB;


很神奇的,為什麼StyleA | StyleB的結果無法放在s1裡呢,因為StyleA | StyleB的結果會是一個int而不是enum 'Style'。主要原因應該還是因為C++裡並沒有Enum operator |(Enum s1, Enum s2),而會implicit將enum轉成int,而改呼叫int operator (int s1, int s2),所以回傳值會是個int而不是enums囉。但是這不是我們預期的結果。

下面這樣的強制轉型可以解決上面的問題

// ok
Style s1 = static_cast<Style>(StyleA | Style B);


但是如果你不小心打錯了呢?

// pass, but what is 'StyleA' | 'Area4'??
Style s1 = static_cast<Style>(Style | Area4);

這樣的強制轉型方式缺乏type-safe。

另外如果你需要Enum Enum::operator |=(Enum s2) 這種運算的話,那現有的C++是不支援的喔

// error, no |=, &= operation in enums
s1 |= StyleC;
s1 &= StyleC;


所以很自然的你會改成下面這樣

// error, can't conver int to enums 'Style'
s1 = s1 | StyleC;
s1 = s1 & StyleC;


咦~ 又不對.. XD,因為跟之前的問題一樣,你需要做強制轉型將int轉成enum,所以在做bitwise operations會常常需要強制轉型。


function的參數也是一樣會遇到要強制轉型的問題

void foo(Style style)
{
// ...
}

void main()
{
/ error, can't conver int to enums 'Style'
foo(StyleA | Style B);

// pass, but it's non type-safe
foo(static_cast<Style>(StyleA | StyelB));
}


所以會看到有些lib的function的參數就不使用enum,改成unsigned int這種整數型態。

void foo(unsigned int style)
{
// ...
}

void main()
{
// pass, but it's non type-safe
foo(StyleA | Style B);
}


假設你改用bitwise_enum這個template class的話,除了上一篇講到的功能,當你在bitwise operations時依舊可以做到type-safe。

#include "bitwise_enums.hpp"

void foo(bitwise_enum<Style> style)
{
// ...
}

void main()
{
// pass
bitwise_enum<Style> s1 = StyleA | StyleB;
s1 |= StyleC;
foo(StyleA | StyleB);

// error, your complier will say it can't convert enums 'Area' to 'Style', :)
foo(StyleA | Area1);
}



不過用bitwise_enum有一些缺點,因為bitwise_enum有implict ctor自動將enum轉成bitwise_enum,因此當你要判斷某個bit是否為1時,你不能用簡單的 and 運算,而需要呼叫他的member function。

bitwise_enum<Style> s1 = StyleA | StyleB;
if (s1.has_bits(StyleC)
{
// ...
}


Note:
1. 跟前一篇一樣,如果我們不去擔心type-safe的問題的話,那用強制轉型這樣其實也沒什麼問題的。

Reference:
1. bitwise_enums
2. bitwise_enums ComplierErrors

C++的列舉型別的type-safe問題

C++的enums(列舉型別)相當實用,特別是和#define, const int比起來
1. 型別檢查 (type-safe),這是#define, const做不到的。
2. 將相關的type放在別一個enums,不會像#define, const 定義的常數一樣,沒有group的概念。


enum Style
{
StyleA,
StyleB,
StyleC,
}

enum Area
{
Area1,
Area2,
Area3,
Area4,
}

// pass
Style s1 = StyleA;

// error, can't convert from int to enums 'Style'
s1 = 2;

// error, can't convert from enums 'Area' to 'Sytle'
s1 = Area3;

// pass, if you really need do it (it's non type-safe)
s1 = (Style)10;
s1 = (Sytle)Area4;

// ok, but it's non type-safe
int s2 = Area1;
s2 = Style1;


enums可以幫你在assignment operation時做型別檢查,避免你在寫程式時指定錯誤的type到enums裡,但是enums也不是所有的型別檢查都會做,考慮以下的清況:

Style s1 = Style1;

// pass and the complier will not say any errors
if (s1 == Area1)
{
// ...
}

為什麼會這樣呢,我想是C++因為沒有Implement operator==(Enum s1, Enum s2)的關係,且Enum可以implicit轉型為int,因此實際上呼叫的是operator==(int s1, int s2)的運算,這樣的結果會造成在enums的equal operation時缺少type-safe,而我認為type-safe對一個常常要mantain多個程式的programmer來說是非常重要的(其實即使只寫一個簡單的程式,我還是希望complier會幫我做type-safe XD),我之前就是碰到類似的問題,我拿別的enums來和我正在使用的enums來比較,並且complier也過了,因此我認為我的程式碼沒有問題了,如果有問題的話我的complier"應該"會告訴我才對,很不辛的,這個問題造成我的程式在客戶端才被發現錯誤(黑箱測試時並沒有測出這個bug orz),而且是在release數個月之後才由user發現的,這所花的成本我想遠比改寫程式為type-safe還花得高,因此後來我也慎重的告誡我們公司的程式設計師,這個type-safe的重要性。

那要怎麼解決這個問題呢,網路上有人寫了一個enums template class來包裝enums,他提供operator==(Enum s1, Enum s2)的運算,並且增加多個enums的bitwise運算(這個下一篇文章再提好了),強化我們的complier的錯誤檢查能力,並提升type-safe。

我使用的是這一個bitwise_enum class
google code的網址是:http://code.google.com/p/bitwise-enum/
目前的版本是1.2版 (http://bitwise-enum.googlecode.com/files/bitwise_enum-1.2.tgz)

#include "bitwise_enums.hpp"

// pass
bitwise_enums s1 = Style1;

// pass
s1 = Style2;

// pass
if (s1 == Style2)
{
// ...
}

// error, your complier will say it can't convert enums 'Area' to 'Style' for equal operation
if (s1 == Area1)
{
// ...
}


Notes:
1.
如果你打算使用bitwise_enums取代現有的寫法的話,因為bitwise_enum用了一些隱喻轉換的constrctor以及non-member operation function,可能會造成你現有的程式碼錯誤,如果要避開這個問題的話,可以將bitwise_enums用namespace包起來。


namesapce utils
{
#include "bitwise_enums.hpp"
}

utils::bitwise_enum s1 = Style1;


2. 如果你不擔心enums的type-safe問題的話,那你也就不用這麼麻煩了。 :)

References:
1. bitwise_enums
2. bitwise_enums ComplierErrors

2008年12月5日 星期五

ISO 文件

最近公司在用ISO文件,今年是大檢,而且特別是工程部這邊,因為我很不小心的接下了這個工作,所以現在是一個頭兩個大,前幾年我都沒有接觸ISO啊,只有在去年是輔助的角色幫忙整理資料而已,結果今年我就硬扛下來了,我命苦了。 >_<~

另外,小公司的ISO感覺上常常都是做好玩的,都是做給所謂的ISO"老師"看得而已,然後再找一個比較會啦賽的去跟"老師"啦,再找個漂亮的行政專員陪陪老師開開會就ok了(咦~ 好黑暗? XD),這種做表面功夫的ISO我感覺比真實做ISO還累人 (應該說12月份特別累)。

明天又是忙錄的週末。

2008年12月2日 星期二

std::exception 沒有支援wchar_t的介面

很可惜的,如果你的程式是Unicode的話,exception::what並不會因此而改成傳回const wchar_t *

void log(const wchar_t* text);

void main()
{
try
{
throw std::runtime_error();
}
catch (std::exception& ex)
{
const char * message = ex.what(); // 只有const char *的版本
log(message);
}
}


一種做法是不要透過what去取得error message
(適用在寫自己的exception,底層的exception還是沒辦法)

class your_exception : public std::exception
{
public:
your_exception(std::wstring what) : m_what(what) {}
virtual ~your_exception() {}

virtual const wchar_t * message() const
{
return m_what.c_str();
}

private:
std::wstring m_what;
};

void log(const wchar_t* text);

void main()
{
try
{
throw your_exception("something error..");
}
catch (your_exception& ex)
{
const wchar_t * message = ex.message();
log(message);
}
}


但是這樣寫會有幾個問題
1. C++提供的std::exception沒辦法改寫出wchar_t的版本。
2. 如果底層真的會丟出std::exception,那你也要catch (std::exception& ex)才攔得下來,根本問題還是沒有解決。

另一種方法是後端再去把const char * 轉成wchar_t

std::wstring ToWString(const char* text)
{
std::wstring str;
size_t size = strlen(text) + 1;
str.resize(size );
MultiByteToWideChar(CP_ACP, 0, text, -1, &str[0], size);
return str;
}

void log(const wchar_t* text);

void main()
{
try
{
throw std::runtime_error();
}
catch (std::exception& ex)
{
const char * message = ex.what();
log(ToWString(message));
}


後記
1. 其實這個對有經驗的programmer根本不是問題,而且解法可能沒有太多種。
2. 所以這篇只是要做一個memo而已。 :)

2008年12月1日 星期一

小小大星球的快打旋風造型!!

哈哈,最近小小大星球的一個國外網站貼出快打旋風的造型圖片喔~
每一隻都好可愛喔 >////<... 呵呵... 如果到時候要錢才可以下載的話,那我會買下去的吧... XD













boost 的serialization export問題

今天在寫一個專案,用到了很多繼承的物件要透過boost::serialization來儲存,但是我的程式碼明明就有加入BOOST_CLASS_EXPORT選項,可是就是不明白為什麼有的class可以正常被serialization,有的則會丟出excetipn(unregistered class),後來找到這一篇文章,他裡頭有提到

为使用这些宏,必须包含boost/serialization/export.hpp,在包含该头文件之前,必须首先包含所有要使用的archive类的头文件。

嗯嗯,的確我就是在某一個.h裡include "export.hpp",而又在另一個.cpp檔include "binary_iarchive.hpp",一直都不知道boost原來在使用上還有這些細節,造成我今天半天的時候都在處理這個超乎我想像的問題。 orz....


或許改天真的要來了解一下boost::serialization的底層設計吧,加深自己對boost的認識。

LinkWithin

Related Posts with Thumbnails