首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

Windows 訊息值、托管字串字面值與混淆代碼:C++ 專欄深度解析

     編輯:關於VC++

讀者來信指正專欄中使用 WM_USER+1 與對話框訊息 DM_SETDEFID 衝突,並詢問 WM_APP 與註冊訊息的適用場景;另有讀者探討 C++/CLI 中 S"..." 托管字串字面值與傳統 _T("...") 的效能差異;還有讀者嘗試用 #pragma unmanaged 保護演算法知識產權。本期專欄逐一深度解答。

訊息值範圍的正確劃分

圖 1 總結 Windows 訊息值正式分類(每十年必複習):

範圍用途
0 ~ WM_USER-1 (0x03FF)系統保留(WM_CREATE 等)
WM_USER ~ 0x7FFF私有視窗類別專用(如狀態列 SB_SETTEXTA = WM_USER+1,對話框 DM_SETDEFID = WM_USER+1)
WM_APP (0x8000) ~ 0xBFFF應用程式自定義訊息,不與系統/控件衝突(Win95/NT 4.0+ 支援)
0xC000 ~ 0xFFFF註冊訊息(RegisterWindowMessage),跨進程全域唯一

錯誤修正:專欄中用 WM_USER+1 確實與 DM_SETDEFID 衝突。CMyOpenDlg 作為可複用擴展類別,宜用 WM_USER+10 等較大偏移避開標準控件訊息,或視情況採用 WM_APP / 註冊訊息。

WM_APP vs 註冊訊息:如何選擇?

  • WM_APP 範圍:僅在自身應用程式視窗間傳遞,無跨進程需求時首選。
  • 註冊訊息:需與其他應用程式協作通訊(如協同管理工具互訊),用 RegisterWindowMessage(_T("WmSayHello")) 取得全域唯一值,同一會話中所有註冊者獲得相同值。

托管字串字面值 S"..." 為何更快?

編寫測試程式 StrLit.cpp,對比 IL 代碼(ILDASM 反組譯):

傳統 C++ 字面值 _T("...")

// String* c2 = new String(_T("This is a TCHAR string"));
ldsflda valuetype $ArrayType$... '?A0x...unnamed-global-0'
newobj instance void [mscorlib]System.String::.ctor(int8*)
  • 編譯器產生非托管字元陣列($ArrayType$),呼叫 String::.ctor(int8*) 建構函數。
  • 每次執行都配置新物件,觸發 GC 壓力。

托管字面值 S"..."

// String* s1 = new String(S"This is a String literal");
ldstr "This is a String literal"
  • 單一 ldstr 指令,直接從中繼資料推入**已駐留**的字串物件參考。
  • CLI 規範保證相同字面值只對應同一物件(String Interning)。

實測效能差異(5000 萬次循環)

方式耗時記憶體實際分配物件數
_T("foo")32,797 ms190 KB5000 萬(GC 疯狂回收)
S"foo"100 ms43 KB1(全複用同一物件)

結論:S"..." 不僅避免建構函數呼叫,更因字串駐留機制極大降低配置/GC 開銷。凡需傳遞 System.String 的托管 API,優先使用 S"..."。

托管與原生字串互轉陷阱

  • 托管字串永遠是 Unicode(wchar_t),無自動轉換至 TCHAR/char*。
  • 需用 vcclr.h 提供的 PtrToStringChars 取得固定指標:const WCHAR __pin* p = PtrToStringChars(str);

保護演算法 IP:#pragma unmanaged 實戰

/clr 編譯預設產生 MSIL。若要隱藏核心演算法:

  1. 用 #pragma unmanaged 將核心函數編譯為原生機器碼。
  2. 原生函數**不能**直接操作托管類型(__gc class、String^ 等)。
  3. __gc 類別方法負責參數封送(如 PtrToStringChars),再呼叫原生函數。
#pragma unmanaged
void DoSecretAlgorithm(const WCHAR* pstr) { ... } // 純原生實作
#pragma managed

__gc class Widget {
public:
    void MyMethod(String* s) {
        const WCHAR __pin* pstr = PtrToStringChars(s);
        DoSecretAlgorithm(pstr); // 呼叫原生核心
    }
};

注意:含 __asm、setjmp/longjmp、可變參數的函數會被編譯器**強制**編譯為原生碼。

混合模組鏈結機制

/clr 隱含 /MT(多執行緒 CRT 靜態連結),確保與 CRT 靜態連結,因 GC 終結器在獨立執行緒運行。呼叫 printf、memset 等 CRT 函數時會自動從靜態庫鏈結。

C++/CLI 是微軟唯一允許單一檔案混合原生碼與 MSIL 的語言,善用此特性可兼顧效能與安全性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved