2012年11月30日 星期五

在 vim 的 command line 模式貼上文件中的內容

用 vim 編輯文件時 複製貼上非常方便
不過有時會遇到這種情況 想要在 vim command line 模式中使用文件中的某段內容

例如寫 code 時發現某個 function name 取得不好 想要修改
用 vim 的 replace 指令非常方便

%s/old_fn_name/new_fn_name/gc

這樣就把整個文件中 old_fn_name 都換成 new_fn_name
但是自己手打 old_fn_name 實在太煩人
無所不能的 vim 絕對可以解決這個問題

首先把 old_fn_name yank 起來
然後在 command line 模式打 ctrl + r
此時應該會看到雙引號 " 出現 這時再按數字鍵 0
old_fn_name 就會出現了

Windows system 上的各種 XMLHTTP 元件

Windows 上有好多版本的 XMLHTTP 元件 列舉如下

Microsoft.XMLHTTP
Microsoft.XMLHTTP.1.0
Msxml2.ServerXMLHTTP
Msxml2.ServerXMLHTTP.3.0
Msxml2.ServerXMLHTTP.4.0
Msxml2.ServerXMLHTTP.5.0
Msxml2.ServerXMLHTTP.6.0
Msxml2.XMLHTTP
Msxml2.XMLHTTP.3.0
Msxml2.XMLHTTP.4.0
Msxml2.XMLHTTP.5.0
Msxml2.XMLHTTP.6.0


這麼多的元件看起來令人混淆 還好這篇文章幫我們分析過了

大意是說
Microsoft.XMLHTTP:
        最老的元件 不要再用它了

Msxml2.ServerXMLHTTP:
        是給 server 端用的

Msxml2.XMLHTTP:
        是給 client 端用的

Msxml2.ServerXMLHTTP 和 Msxml2.XMLHTTP 其實差不多
Msxml2.ServerXMLHTTP功能強大一點 可以送 cookie

MSDN blog 告訴我們 Msxml2.XMLHTTP 最好用 6.0 或 3.0 版本
4.0 和 5.0 有 issue

除了這些元件之外 還有一個類似的元件 WinHttp 也可以讓你送 HTTP request
這個元件的介面跟 XMLHTTP 不太一樣 而且它有一個 XMLHTTP 作不到的功能
就是它可以送 Referer
var http = new ActiveXObject('WinHttp.WinHttpRequest.5.1');
http.Open('GET', 'http://xxx.yyy.zzz', false);
http.SetRequestHeader('Referer','http://xxx.yyy.zzz')http.Send();

2012年11月27日 星期二

strncpy 不是用來取代 strcpy

strncpy 被創造出來的目的並非是為了要有一個安全版本的 strcpy
而是因為當時撰寫 unix 檔案系統需要一個方便的 "拷貝固定長度字串" 的函式
有了這個函式就可以用這種寫法寫檔案系統的 code
strncpy(inode->d_name, filename, 14);
用這種寫法 假設 source buffer 字串大小小於 14 例如說是 8
strncpy 會把 dest buffer 的第 9 ~ 14 的 buffer 清為 0

很多時候我們只是想複製字串 這多餘的行為我們不需要
若要說 strcpy 的安全版本 snprintf 也許更合適

snprintf(dest, n, "%s", src);
reference:
http://felix021.com/blog/read.php?2081

2012年11月16日 星期五

windows 8.3 filename

8.3 filename 是 DOS 時代的檔案命名規定  那時的 DOS FAT file system 只能支援最長 8 字元的檔名跟最長 3 字元的副檔名
不過到了 windows 時代的 FAT 跟 NTFS file system 就沒有這個限制了

但是為了相容的緣故 windows 還是支援 8.3 filename
例如創造一個長檔名的檔案 abcdefghi.xyz
windows 會幫忙建一個相對的 8.3 filename 存在 disk 上
像這樣 abcdef~1.xyz

所以有時會看到帶有 ~ 符號的檔名 而 ~ 後面的數字代表這個檔案有幾個
例如假設有兩個檔案
1. abcdefgh123.xyz
2. abcdefgh456.xyz

這兩個檔案相對的 8.3 filename 會是
1. abcdef~1.xyz
2. abcdef~2.xyz

不過這個功能是可以關掉的 所以不能認為每個系統上的 long filename file 都會有 8.3 filename
MSDN 上有詳細描述

2012年11月14日 星期三

java 的 volatile 和 synchronized

在 java 中有 volatile 和 synchronized 關鍵字
synchronized 應該蠻常使用到的 大家都知道是甚麼意思
但是 volatile 就可能比較少用 這個關鍵字是用來修飾 "基本型態" 的變數
保證這個變數在多執行緒時的同步

這邊這邊有講到很有用的觀念
大意是在 java 中有主記憶體區域 而 thread 可以有自己私有的記憶體區域
某個 thread 中存取一個全域變數 可能會在存取完後將這個變數的值保存在自己私有的記憶體區域
這樣會造成多個 thread 存取全域變數時結果不同步
但是如果一個變數事前有用 volatile 修飾
則保證所有 thread 對這個變數做完存取時會將變數值寫回到主記憶體區域
這樣看似只要用 volatile 就可以完全保護好變數  其實不然
因為除非對這個變數的操作是 atomic operation
否則還是會出問題
像在第一個連結就有提到  java 中的 ++ 運算不是 atomic operation
所以如果多執行緒對某變數作 ++ 運算  就算用 volatile 還是不能保證變數的同步
因為 volatile 只能保證 thread 將值寫回主記憶體
並不能保證同一時間只有一個 thread 去存取這個變數
像是 ++ 不是 atomic operation 若是有個 thread 的 ++ 運算還沒做完 另一個 thread 是可以對這個變數作 ++ 的
只是他們都會把做完 ++ 的結果寫回主記憶體而已
這樣變數就未必會同步了

2012年11月12日 星期一

FindExecutable 對於尋找 file associated program 並不可靠

windows 系統會記住許多文件的預設處理程式 例如 txt 檔案預設就是用 notepad 開啟
如果要在程式中找出某個文件的預設處理程式 有一個很直觀的 API FindExecutable 可以用

但是這個 API 並不太可靠 一個 known issue 是在 XP 上對於附檔名 accdb 的文件就找不到處理程式
網路上的討論是這個 API 對於 3 個字元以上副檔名的檔案處理可能會發生問題

在我的 XP 系統上 不只 accdb 出問題 rmvb 檔案也出問題找不到預設處理程式
網路上有人用 procmon 監控發現這個 FindExecutable 在查詢 registry 時會把副檔名弄錯
我試了一下果然如此

當用 rmvb 為副檔名時 FindExecutable 用 rmv 去查 registry
當用 accdb 為副檔名時 FindExecutable 用 acc 去查 registry
不過為什麼會有這種 bug 就不知道了

一個替代方案是用 AssocQueryString API
TCHAR exe_path_buf[MAX_PATH] = {0};DWORD buf_size = MAX_PATH;AssocQueryString(0, ASSOCSTR_EXECUTABLE, ".accdb", "open", exe_path_buf, &buf_size);
經測試 rmvb 跟 accdb 都可以正常工作

2012年11月11日 星期日

GOT與PLT的運作

ELF(Executable and Linking)格式的執行檔 是利用PLT(Procedure Linkage Table)與 GOT(Global Offset Table)來處理函式庫中的函式呼叫
(呼叫使用者自定義函式則不會使用到PLT及GOT)
例如 若是一個執行檔中有呼叫到printf函式 程式在第一次呼叫到此函式時 會跳到PLT中的printf函式entry
組合語言長的像這樣:
080482c8 < printf@plt >: 80482c8:       ff 25 d4 95 04 08       jmp    *0x80495d4 80482ce:       68 18 00 00 00          push   $0x18 80482d3:       e9 b0 ff ff ff          jmp    8048288 <_init+0x18>

第一個jump指令 跳到GOT中與printf相關的entry(0x80495d4)所包含的記憶體位址 這個位址在ELF執行檔load time的時候會被dynamic linker填入第二個push指令的位址
這個push指令將某個資料push入stack(這個資料之後dynamic linker會用到) 然後第三個jump指令跳到PLT0的位址(代表PLT第一個entry)
所以 任何函式 在"第一次"執行時 都會直接跳到第二個push指令 然後跳到PLT0去執行 PLT0長的像這樣
08048288 <__gmon_start__@plt-0x10>: 8048288:       ff 35 c0 95 04 08       pushl  0x80495c0 804828e:       ff 25 c4 95 04 08       jmp    *0x80495c4 8048294:       00 00                   add    %al,(%eax)

PLT0所做的事情 第一個push也是將某個資料push入stack(之後dynamic linker會用到)然後跳到dynamic linker去解析出printf函式的位址
作解析的動作需要額外資訊 也就是先前那兩個push指令push到stack的資料 等解析出printf函式的位址後 將位址存放到GOT中的printf相關entry 也就是0x80495d4中
之後再呼叫到printf時 一樣跳到0x80495d4中存放的位址 此時這個位址已經是printf函式的位址 也就不會跳到PLT0 一切正常運作

而為什麼要這樣作 根據linker and loader中所說 因為一個program可能會包含很多他有可能不會呼叫到的函式
所以 為了加速程式的起始速度 在load time不會解析出所有函式的位址
改而在run time第一次執行到該函式時 才解析出函式位址來用 而這稱為lazy evaluation

printf, wprintf output unicode to console

連結
一篇說明 printf 跟 wprintf 輸出 unicode 到 console 的一篇文章
解釋的非常詳細

2012年11月2日 星期五

windows API StringCchCopy

HRESULT StringCchCopy( _Out_ LPTSTR pszDest, _In_ size_t cchDest, _In_ LPCTSTR pszSrc);

這個 API 有一個回傳值是
STRSAFE_E_INSUFFICIENT_BUFFER

這個值代表的意思是 dest buffer 太小了不足以放入 source buffer 裡面的字串
但是這個API還是會幫你把 source 裡的字串截斷後放入 dest buffer 裡面

我有點好奇
StringCchCopy 知道 dest buffer 大小就算了 它怎麼會知道 source buffer裡面的字串大小?

後來經過實驗 它會去偵測 source buffer 裡面的內容直到找到 '\0' 結尾 以此判斷來源字串大小
以下的 code 得以證實
第二次的結果就不會是 STRSAFE_E_INSUFFICIENT_BUFFER

#include <cstdio>#include <tchar.h>#include <StrSafe.h>int main(){  TCHAR buf[5] = {'a', 'b', 'c', 'd', 'e'};  TCHAR src[6] = {'1', '2', '3', '4', '5', '\0'};    HRESULT result = StringCchCopy(buf, 5, src);  if (STRSAFE_E_INSUFFICIENT_BUFFER == result)    puts("STRSAFE_E_INSUFFICIENT_BUFFER");  else    printf("%s\n", buf);  TCHAR src2[6] = {'1', '2', '3', '4', '\0', '5'};  result = StringCchCopy(buf, 5, src2);  if (STRSAFE_E_INSUFFICIENT_BUFFER == result)    puts("STRSAFE_E_INSUFFICIENT_BUFFER");  else    printf("%s\n", buf);  return 0;}