My friends, my life, my style - James S.F. Hsieh

3/30/2009

Picasa Web Albums Data API

2009 03 29 大甲媽回鑾

相簿
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽
寄件者 大甲媽

3/28/2009

C++ Coding Standards

C++ Coding Standards: 101 Rules, Guidelines, and Best Practices 真是本好書有描述許多正確的觀念, 本書非常的精簡都是以 Rule 的方式在描述, 以下兩分是我貼出來要報告的投影片 pptx: 

3/27/2009

DeepZoom and MultiScaleImage control

Deep Zoom MSDN 的文章

Deep Zoom 相關的技術文章

3/25/2009

台灣軟體業的通病

  1. 交作業心態
  2. 地球是平的
  3. 考古學, 逆向工程盛行
  4. 倒練九陰真經
  5. 無法轉動的齒輪

奢華的幸福

對這個廣告挺有感覺的 分享一下嘍...

3/23/2009

2009 03 21 大甲媽遶境 大甲-彰化 44.36km 18h 4.3k/h


3/20/2009

SxS at runtime Part 2: 動態載入 Manifest 來改變 SxS 的組態

SxS 可說是種 context 用來描述 process 當下對於 DLL 載入的組態方式 所以每個 process 可以擁有自己的 SxS 組態, 這個組態稱做 Activation Context, 以 DLL 本身來說, SxS 說明了為相依的 DLL 描述了相關的版本跟 binding (WOW 用語 綁定) 的方式, 而 COM 則描述了 Class Object 的 binding 方式, 概念上比較像是建立一個 process 自己獨有的 registry database view (就像是環境變數的overwrite), 因為 CoCreateInstance 是利用 registry database 來找尋實做指定 CLSID 的 DLL 並且 Activate (建立Class Object 跟 COM object). 當然 Class Window 也是 SxS 的對象之一, 詳細可以參考 MSDN Creating Side-By-Side Windows Classes. 大部分的人都知道利用 process 的外部或是嵌入的 Manifest 來控制 process 建立時的 activation context, 這個方法可以外在的編修 Manifest 在 process 執行前完成, 好處是不用寫任何的程式碼就可以設定 activation context, 而壞處則是無法再執行時期改變組態, 在上一篇的文章我就提到一種使用的情境必須在執行時期改變 activation context, 因為調用的 COM module 並不希望被註冊也希望可以是以 plug-in 的方式掛進我們的程式. Windows API 提供了執行時期改變 activation context 的方法, 稱為 Activation Context API. Activation context 的概念比較像是個 stack 並且每個 thread 都可以有自己的 context stack (請參考 MSDN), 當我們利用 API 建立新的 context 時, 它會覆蓋掉原本的 context, 直到我們釋放我們所建立的 context. 第一個 context 由系統提供, loader 在執行 process 的第一個 thread 時會利用預設的 Manifest 來 overwrite 系統的 context. 而每一次 CreateThread 時, 建立的 new thread 都會繼承當下的 Activation context. 值得一提的是 Activation context 由於是 pre-thread 的, 所以我們必須考慮多個特定的狀況
  1. 當我們使用 SendMessage 或是 PostMessage 給別的 thread 時, Activation context 會怎麼樣? 我們知道當 message 傳遞跨不同的 thread 時, 執行 message 的 thread 將會與發送的 thread 不同.
  2. 當我們在一個不相容於 COM threading model 的 thread 建立 COM object, 系統會幫我們建立對應的 host thread (希望大家都知道這件事), 而這個 host thread 的 Activation context 會怎麼樣?
  3. COM 的 host thread 可能被多個 COM objects 所 host, 而這些建立 COM objects 的每一個 thread 可能都有不同的 Activation context, 那又會如何呢?

第 1 個問題, MSDN 上面的描述是: Windows API 會特別處理 SendMessage 或是 PostMessage 等.... 多個跟 Message queue 有關的 API, 當 Message 被傳遞時 Activation context 也會跟著傳遞, 來防止 context 衝突產生的問題, 不過沒有詳細的描述 Activation context 是何時被 push 何時被 pop, 不過我猜想是當接收 message 的 thread 呼叫 DispatchMessage 時, thread 的 Activation context 才會被 overwrite, 當 DispatchMessage 返回時則回覆 thread 原本的 context.

第 2 個問題, MSDN 上面的描述是: CoCreateInstance 會自動處理 host thread 的 Activation context, 當 host thread 在乎較 LoadLibrary 或是 DllGetClassObject (之前已經被 load 進來了) 之前, 會切換 context 到 creating thread 的 Activation context.

第 3 個問題就很簡單可以回答了, 因為 host thread 是以 Message queue 的方式在接收與執行 COM client 對 COM server 的函數呼叫, 而 proxy 在產生函數呼叫是以 SendMessage 的方式傳遞給 stub, 所以 Activation context 自然的就會被轉移了.

以下是貼來的程式碼 (How to Use the Activation Context API), 說明如何使用 Activation context API, 連結中還有 C# 的使用方式:

ACTCTX actctx = {sizeof(actctx)}; actctx.lpSource = L"Lifetime.Example.Manifest"; HANDLE hActCtx = CreateActCtx(&actctx); ULONG_PTR cookie = 0; BOOL fOK = ActivateActCtx(hActCtx, &cookie); sampleDLL::IsxsSampleControl *pControl = NULL; HRESULT hr = CoCreateInstance(__uuidof(sampleDLL::CsxsSampleControl), NULL, CLSCTX_INPROC_SERVER, __uuidof(sampleDLL::IsxsSampleControl), (void**)&pControl); DeactivateActCtx(0, cookie); ReleaseActCtx(hActCtx);
How to Use the Activation Context API, Windows Side-by-Side, Using the Activation Context API, Activation Context Reference

3/18/2009

SxS at runtime Part 1: COM 可不可以不註冊又不要描述在崁入的 Manifest 中

標題是苦惱我們許久的問題, 沒想到隨便 survey 了一下就找到解法, 所以答案當然是 YES,

問題是什麼呢就讓我娓娓道來, 我想有寫 Windows 程式的人大概都知道 DLL Hell 這件事, DLL Hell 形成的原因在於不相容, 舉個例子, MFC 或是 C-Runtime 本身就有很多個版本而且各版本之間是不相容的, 同一個環境中是允許有多個版本同時存在的, 那又會發生以下的兩個問題:

  1. 版本不同但是 DLL 的檔案名稱一樣, 例如 MFC 9.0 跟 MFC 9.01 的檔案可能都叫 mfc90.dll
  2. 如果將這些檔案都放在不同的目錄, 上述的問題可以解決, 但是 AP 並不知道要載入哪個檔案才正確

因為這些因素, 微軟在 Windows XP 導入了 Side-by-Side 的概念去解決這個問題, 方法是每個 AP 都有自己的 metadata 來描述載入 DLL 的版本資訊或是方法. 而 Loader 會根據這樣的 metadata 做到路徑的重新導向來達成正確的載入, 該 metadata 稱為 Manifest.

COM 在這方面也有相同的問題, 雖然設計 COM 的初衷是希望每個 interface 的 原型, IOPE 跟 semantics 都是固定的, 這樣自然沒有衝突的問題, 但是這個目標並不容易做到, 現實上也不可能做到, 因為 IDL 只有定義 interface 的原型, 對於 IOPE 與 semantics 則無法規範. 所以 COM 也有 SxS 這樣的概念.

平常我們使用 COM SxS 都必須將該 COM 所包含 Class Object 的 CLSID, threading model, IID, proxy info 寫在 Manifest 中. 而 Manifest 則會被嵌在 DLL 或是 EXE 中. 這也意味著這些資訊是在Building time 就必須決定.

如果我們要支援以下的 case, 就會用一些非正規的手法去達成: 某 AP 允許有 Plug-in 的 module 掛到 AP 中來使用, 而 Plug-in 的 object 是使用 COM 的方式來達成, 而該 Plug-in module 又因為相容性或是部屬方便為考量不願意將 COM 註冊到系統中.

一個比較惡劣的解法是: AP side 使用以下手法 (我也用過啦 不得已 要生活 :( )

  1. LoadLibrary/CoLoadLibrary的方式把 Plug-in module 給 load 進來,
  2. 然後用 GetProcAddress 的方式取得該 module DllGetClassObject 函數的位址,
  3. 呼叫 DllGetClassObject 取得 ClassObject,
  4. AP 呼叫 IClassFactory::CreateInstance 來建立 COM object.

以上聽起來都很完美, 但是有一個致命性的問題, 因為 AP 沒有任何關於該 Plug-in COM module 相關的描述, 所以也無從得知該 COM 的 threading model, 即使知道該物件的 threading model 也無法在需要時建立出適當的 proxy/stub. 然而 CoCreateInstance 可是會根據註冊的資訊與當下 thread 的 threading model 自動判斷與建立對應的 proxy/stub, 簡單的說 該方法讓 COM 的 threading model 崩潰了, 在 thread-safety 方面會產生不少問題, 更大的問題是該方法不會有任何的警告或錯誤, 所以大多數的 programming 都不知道這樣是會有問題.

正確的方法當然就是我的主題了, 使用 Activation Context API 這樣的正規守法,不過這個好東西似乎鮮少人知道, 連 Codeproject 都沒有人在分享, 我也是仔細的看了 MSDN 之後才知道, 附帶一提 Manifest 機制就是使用 Activation Context API 去實做的喔. 詳細的描述請看下篇, 或是自己參考一下的 reference 喔. :)

How to Use the Activation Context API, Windows Side-by-Side, Using the Activation Context API, Activation Context Reference

3/17/2009

x64 Part 2: Calling convention 小記

以下是 MSDN 收集來的資訊 用來說明 Calling convention 之間的差異

下表用來說明暫存器在 Windows X64 下函式呼叫的使用條件

以下是 Windows x64 下的 Stack layout, Function B 呼叫 Function C 時, 必須將 Fun B 所規定的引數設定於暫存器與推入堆疊, 當 Fun B 是用 asm "call Fun C" 時, 該指令會將對應的返回位址也推入堆疊. 值得一提的是 Windows x64 有下述的數個規定:

  1. 我們所知, RCX, RDX, R8, R9 可以用來傳遞前四個整數型態的引數, 當然這些引數必須對其 1, 2, 4, 8 個 bytes 對齊, 否則將以 reference 的方式傳遞, 而 MMX0~MMX3 可以用來傳遞浮點數類型的引數. 而 16 bytes 的數值引數也必須以 reference 的方式傳遞.
  2. 呼叫函數不管是否傳遞引數, 呼叫端都會固定推入 32 個 Bytes 做為 RCX ~ R9 的堆疊暫存空間. 所以每次呼叫函數都至少會推入 32 Bytes 做為 Spilling space 或稱 shadow space, 和 8 Bytes 的返回位址.
  3. 函數呼叫堆疊清除的責任是由被呼叫這來負責, 傳回值如果是整數則放在 RAX, 是浮點數或是 16 bytes 的數值則放在 MMX0, 對於非 1, 2, 4, 8, 16 bytes 對齊的傳回值則以指標方式傳遞, 指標放置於 RAX.
  4. volatile 類型的暫存器可以隨意由函數使用, 當然有些用來傳遞引數.
  5. non-volatile 類型的暫存器可以以隨意由函數使用, 但是 return 時必須還原原本的數值.
  6. Stack 空間的配置 (用來傳遞引數. non-volatile 暫存器 或是存放區域變數) 必須對齊 16 Bytes.

Argument Passing and Naming Conventions, Overview of x64 Calling Conventions, win64 Structured Exception Handling, The history of calling conventions, part 5: amd64, Moving to Windows Vista x64

3/15/2009

2009 03 15 鹿崛坪 磺嘴山

原本打算只到鹿崛坪的旅程, 結果意外的走到了多年沒去的磺嘴山, 算一算應該也有九年了吧 (日子過得還真的很快呢), 登頂的時, 我真的非常興奮, 因為那裏很多青少年時期的探險回憶. 這次旅程其實整體的路程並不遠, 從鹿崛坪到磺嘴山大概是 2.22 km 左右, 垂直高度大概 340 m, 不過以我現在的體力也花了兩個小時九分鐘才到山頂 (包含找路的時間). 囧rz

鹿崛坪古道沿著頭前溪向上延伸, 整條古道因為溪流的關係大部份的石子都披著一片綠綠的青苔, 四周綠樹成樹蔭, 走起來令人心曠神怡, 路程高度的落差大約有 100m 左右, 許多人喜歡來這裡朔溪, 路程中會遇到頭前溪瀑布, 因為地勢平緩的原因鹿崛坪旁的頭前溪則是條漂亮的娟娟小溪, 附近為草原地形植披以草地矮樹居多, 當陽光灑下來時, 非常美麗.

磺嘴山是個充滿我回憶的有趣地方, 它是七星山以東的最高峰所以可以看到金山萬里基隆的整個海岸, 磺嘴山系的首峰海拔 912 m 是座 A 級山, 山頂成馬蹄形是個火山的噴火口, 中間有個火口湖稱為磺嘴池, 當夏季多雨時可以清楚的看到整個湖面, 冬季少雨時則如沼澤一般 (我跨越過喔), 上面有許多野生的牛群, 具當地人說, 這些牛群在冬季會由北面的或是東面的山坡下山避冬. 北面山坡有個廢棄的養蜂場與礦場, 礦場主要是開採硫磺, 當年還真的很謝謝那位開砂石車的司機大哥呢, 能夠載我們一程從礦場到有人煙的地方 :)

這次整個行程大約走了 7.98 km , 垂直高度落 520 m, 從上午 9:51 出發 下午 5:46 回到停車地點


3/10/2009

Thread, Message queue and COM



3/05/2009

x64 Part 5: 在 Windows 64 (LLP64) 下會改變大小的幾個 type

ANSI C 定義的 type Specify standard memory model size_t: 用來描述一結構的大小 (數值型態 無號數) ptrdiff_t: 用來描述兩指標間的距離 (數值型態 有號數) intptr_t, uintptr_t: 整數的指標型態 (指標型態) time_t: 描述一段時間的總秒數 (數值型態 有號數)

當中最值得拿來提的是 size_t, 因為, sizeof 的回傳值與 STL 標準 allocator 的計量單位都是 size_t, 因此, 只要使用標準的 allocator, 則 STL 中所有的 size_type define size_t, 而 capacity, size, count 都是以 size_t 做為計量單位.

// definition of file xmemory #define _SIZT size_t #define _PDFT ptrdiff_t // definition of std::allocator typedef _SIZT size_type; typedef _PDFT difference_type; // definition of std::vector typedef typename _Alloc::size_type size_type; typedef typename _Alloc::difference_type difference_type;

Win32 API 定義的 type INT_PTR, UINT_PTR, WPARAM (數值型態) LONG_PTR, ULONG_PTR, LPARAM, LRESULT (數值型態) SHANDLE_PTR, HANDLE_PTR (數值型態)

PINT_PTR, PUINT_PTR (指標型態) PLONG_PTR, PULONG_PTR (指標型態) HANDLE, HMODULE, HINSTANCE, HGDIOBJ ... (指標型態) HWND, HGDIOBJ, HDC ... (指標型態)

Windows 中有許多將指標轉換成數值的方式在 API 間傳遞, 以 thread 的 message queue 為例, 以下是訊息處理函式的定義:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

LRESULT, WPARAM, LPARAM 通常可以用來傳遞數值或是指標, 該 type 的大小在 32bit 和 64bit 的環境中, 大小就有所不同, 值得一提的是 HWND, HGDIOBJ, HDC 等 GDI32 的 type 也改變了大小, 但是它們都有特別的定義(如下), 但是 HWND 的數值似乎永遠都不大於 32 個 bit, 即使是 Spy ++ 64bit (spyxx_amd64.exe) 顯示 64bit process 的 HWND時, 還是只顯示 32 個 bit 但 Window Proc 在 64 或是 WOW64 下大小就有所不同, 可見 HWND 是個 GDI32 產生的數值, 而 HANDLE 或是 HMODULE 則是個記憶體位址. 以 LoadLibrary 為例 HMODULE 的數值其實是 DLL 載入到 Process 中的基底位址.

#define DECLARE_HANDLE(name) struct name##__ { int unused; }; \ typedef struct name##__ *name \ DECLARE_HANDLE (HWND); DECLARE_HANDLE (HHOOK);

3/02/2009

x64 Part 4: Survey C/C++ code analyzer for detect 64-bit portability issues

Microsoft VC++ 2005 /Wp64 最簡單的方式是使用 VC++2005 內建的 analyzer, 可以在 "Project property pages\Configuration properties\C/C++\Detect 64-bit portability issues" 來 Enable 這個選項 /Wp64, 它是是利用一個特殊的修飾字 _W64 與指標與數值間的轉換來檢查 x86 migration 到 x64 的相容性. 這個關鍵的修飾字會告知 analyzer 某個在 x86 下的 type 在 x64 下大小會改變, 利用這樣的描述來找尋潛在可能的錯誤, 不過這個功能在 VC++ 2008 已經被拿掉了, 舉個例子:

// typedefs #ifdef _WIN64 typedef unsigned __int64 size_t; #else typedef _W64 unsigned int size_t; #endif

以下的程式碼就會被檢查出錯誤, 但是檢查的功能可以說是非常的局限

size_t size = 10; DWORD value = size; // warning C4244: 'initializing' : // conversion from '__w64 int' to 'DWORD', possible loss of data

以下還有幾個可以診斷這類型錯誤的工具 Viva64, Gimpel PC-Lint, Parasoft C++test™, CodeCheck 當然, 這些工具都要價不斐, 以 Viva64 為例, Standard License 一年要 €3500, 相當於 NT 154820.

Out-of-process COM server

大家應該很常撰寫 in-process COM server (COM DLL) 吧, 因為它簡單好用也符合大部分的使用狀況跟期望, 對於 out-of-process COM server (COM EXE) 就比較少有機會撰寫了. 其實有幾個主要的原因:
  1. Out-of-process COM server 必須完全實做正確的 IDL 與 Proxy/Stub: 因為只有正確寫作 IDL 才可能產生正確的 P/S, 跨行程的 Marshalling 才可能正常運作, 雖然 COM DLL 應該也要如此, 但多數人根本不遵守, 所以在 .NET 環境使用這些 COM 時, 將為非常痛苦.
  2. 跨行程的 Marshalling 有一定的限制: 很多人會在 COM 的界面上傳遞 HMODULE, Callback function pointer 等等, 然而這些 handle 是 process-wide 的物件, 跨了行程之後就不具意義了.
  3. 管理 Out-of-process COM server 並不容易: 通常使用 COM EXE 都有些特殊的目的, 譬如塑模出 System-wide 的 singleton COM object, 如果用 DLL 來實做可就非常複雜, 因為各個 COM object 在各自行程的記憶體空間中所以無法互相直接存取, 當然可以用 share-memory 或是 RPC 等行程通訊的方式互相合作塑模出 singleton COM object, 但這並不容易, 相較之下 COM EXE 就容易與值觀的多, 不過在控制 COM object 的反應時間與執行效率上就是門工夫了.
  4. Side by Side (SxS) 不可行: 畢竟 COM EXE server 的特性是任何一個 client 有需要這個 COM 的服務時, 都是由同一個 EXE process 來服務, 所以當第一次 COM EXE 被執行時, COM EXE 會利用 CoRegisterClassObject 的方式來註冊 Class object, 方式上跟 DOT 很類似, 而 Client 則是以 CoGetClassObject 的方式取得 Class object 的 Proxy, 而這些過程已經不只是向 Win32 Registry database 查表 DLL 的路徑然後 load DLL 而已, 簡單的說 SxS 比較像是模擬一個私有的 Registry database 來重新導向, 這自然對 COM EXE 無效.
  5. 相關文件過少: MSDN上 Out-of-process COM server 相關文件實在少的可憐

其實 Out-of-process COM server 有幾個好處

  1. 因為 client 跟 server 是兩個不同的 process, 所以 client 不能存取 server 私有的記憶體空間.
  2. 如果不幸兩者其中有支 process crash 了, 也不會直接的影響另一方的執行 (Fault isolation).
  3. Out-of-process COM server 可以在遠端的機器上運作.

壞處則是

  1. 效率不彰, 因為所有的資料都要利用 ORPC 來進行傳遞, RPC 的速度必定遠慢於 function invoke 千倍,
  2. 典型的 Out-of-process COM server 會以 main thread 利用 message loop 的方式推動運作,利用單一 thread 運作概念與實作上很簡單, 但是當 client 變多時, 反應速度勢必下降.

值得一提 DLL Server Surrogates 是個有趣的東西, Surrogate 可以使 in-process COM server 執行於 client 以外其他的 process 中, 這帶來強固性與安全性的好處, 當然, 效率上是必付出代價. 詳細可以參考 DLL Surrogates

Out-of-Process Server Implementation Helpers