目錄:
2009-2010小結(一)畢業前夕
2009-2010小結(二)初入職場
2009-2010小結(三)加班考驗
2009-2010小結(四)抑郁重重
2009-2010小結(五)離職始末
前一陣子看到好些人都發小結了,長的有10年的,短的有1年的,洋洋灑灑,淋漓盡致,各顯神通,看得好不暢快,其中也不乏值得回味的、讓人深思的東西。哥覺得哥不是那樣子牛逼的人,所以就不湊那個熱鬧了^_^ 不過,總結還是要的,有總結才有進步,在這個尋常的夜晚,哥來個遲到的總結。
就從找工作說起吧。2008年秋季,對應的是我的應屆生招聘的時期。9月底,各大IT公司就紛紛來開宣講會了。最開始的是淘寶,可是那時候哥還沉浸在開學的喜悅中,完全不知道,錯過了。然后才開始誠惶誠恐地收集這些信息。接下來支付寶啊,百度啊,騰訊啊,阿里阿,網易啊,接踵而至,一直持續到10月底才告一段落。遺憾的是,哥是個菜逼+傻逼,基本上都是在一面或者更早掛掉。分享幾個事例證明我的傻逼:
1、去支付寶面試的路上,接到個電話,趕緊問:是支付寶嗎?對方回答,我是百度的。沒聽清,繼續問:是支付寶嗎?答:是百度……
2、有個測試崗位,面試官問:你對測試有什么了解?很自信地回答:基本上沒什么了解。
一線公司就這樣全部錯過了。現在還保留的一些記憶是,百度的面試官很耐心,但老是問些聽也沒聽過的破算法;騰訊的面試官說話語速很快,面試也很快,大概10幾分鐘搞定,也不知道說錯什么了導致被這么快否定,哥稱之為“閃電面”;阿里的那期試卷出得相當爛,很多題目重復,還不止一次,還是錯的,直接做不下去,一定要狠狠鄙視。
10月中以后,大大小小的聽過沒聽過的公司基本上都來了。也去過幾場現場招聘的,發放了一些簡歷。其中有一個公司比較叼,忘了叫什么名字的。到攤位上問,你們是做哪方面的?結果那人說:你坐下來自我介紹一下。繼續問:你們做哪方面的我都不知道,我怎么知道適不適合啊?那人語重心長的說,你一個勁地問做什么的,無非是技術啊業務啊,這些有那么重要么?你就沒有信心介紹一下自己么?直接無語,站起來走人。同行的一同學覺得這個方式對口,就去自我介紹了。
還面了2家小公司,一個是在百腦匯附近的,可能跟金融有點關系。招聘會上我已經注意到了那個人,特別認真,說話也比較中肯,后來面試也是那個人。筆試的試卷比較靠譜,做得很順利。當然不是說我做得順利才說它靠譜,這點客觀性哥還是有的。他們很看重數學的樣子,可能覺得我數學系出身,應該可以吧。所以面試的時候叫了個數學系的人來面我數學,問一些求導啊,概率啊之類的,暈死。分析方面的淺顯問題,還算馬馬虎虎能搞定,其他的就。。。后來我直接說,不要問我概率啊統計啊,數理統計我可是考了50分的。那哥們不好意思的走了。他們還要看成績單!哥給他一份真實的成績單,讓他直接苦笑數聲!曬下當年的成績吧:

最后的結果是備胎,他們要等前面的人回應。不過他們這么看重成績單以及數學,讓哥當時也想好了,總之哥沖著編程去的,不是沖著金融去的。
另外一家,就是后來給了我第一份工作的那一家。面試在玉泉附近的百合花酒店。說實話,這次是我那一陣子最舒心的一次面試,好歹也看我簡歷上寫了什么,也算是問了些簡歷上涉及的東西。早上一面,當天下午二面。之前我還沒有經歷過二面,于是打電話給HR姐姐問二面面的是技術還是談別的,被告知面技術。結果被談人生談理想了。其中他說公司主要業務應該算是做Linix的,如果讓你做Linux,有什么想法?我回想了一下,前不久我好像問過百度GG類似的問題(大概說我擔心Linux經驗不足會影響工作blabla),于是就把他的回答有一句沒一句的給復述了一遍。結尾比較尷尬,對方一個勁的問還有問題沒,我想來想去前前后后想了解的都問完了,這可怎么辦,正在這時,外面催了一下,于是看了看表,說,下面還有好幾個人吧,我就問到這里吧。。。逃離。。。
到此為止一個offer也沒拿到,著手準備考研了。分析了一下,政治是靠覺悟的,背書沒用;英語是靠人品的,背單詞我才不干;數學考的應該沒平時學的深,難度上問題不大,可是那些很實用的計算技巧數學系的人反而是一無所知的,需要復習;計算機是人家的專業課我的業余課,除了C/C++/數據結構,其他的也要復習。于是準備了數學和計算機的參考書,準備好好復習。第一天,呆圖書館整天,不錯;第二天,堅持;第三天;繼續堅持。第四天,外出有點事,結果接到了電話,是那家公司給了offer。這下哥可是欣喜若狂啊,馬上打電話向室友啊誰啊宣布。手拿一把offer的牛人請不要笑,我等從未見過offer的土人,當時就是很興奮的,很歇斯底里的,很神經質的。。。
找工作真是太累了,這邊跑那邊跑,找信息,琢磨簡歷,還要等待。。。真是讓人心力憔悴。于是決定告一段落,不要找工作了,三方神馬的全部簽了。至于考研?那當然要考的,錢都交了。復習?算了吧。。彪悍的考生是不需要復習的。
那個寒假過的還是蠻舒心的,后事已定,無憂無慮。只是HR打了一次電話,問什么時候能去實習。新學期開始,又打了一次電話。真是的,本來就說好3月份的,還差大半個月,急什么呢。我為我畢設的選擇感到驕傲,那可是數學系帶畢設的老師中唯一一個做計算機相關的啊!我們要做的是使用ASP.Net做一個學術會議管理系統。在畢設之前,我就咨詢了下帶課導師,把之前寫的ASP的、ASP.Net的破系統發給他鑒定,所以在畢設團隊的地位哥還是蠻高的。有一件比較為難的事情是,倒數第二學期的小畢設,每個人都要上臺講課的,有個同學,還是我隔壁寢室的哥們,是后來選的,老師不知道講給他安排什么主題了,最后讓他講下我之前發給他的一個博客系統。這。。。害得我好幾次小心翼翼的問老師,這可以么?真的可以么?
我把租房、搬東西這類事情都搞定后,3月2日正式去實習了。(天色已暗,欲知后事如何,且聽下回分解。)
posted @
2011-01-16 00:42 溪流 閱讀(2143) |
評論 (7) |
編輯 收藏
前言:
DLL 是個很久遠的文件格式,以至于它只支持導出函數(請忽略 .net 的 DLL)。至于導出 class,也是由于編譯系統的支持才勉勉強強能進行,只能靜態加載,實際上對于DLL文件來說它導出的還是函數。——以上,個人的一點淺顯理解。
問題:
有沒有存在一種好的方式,讓DLL能夠被動態加載,并且能夠方便地得到里面的 C++ class 信息?
備選:
1、別想了,老老實實地用吧,還是導出純C函數= =
2、大膽的導出 class 吧,如果動態加載,自己去拼那些編譯后名字吧。。
3、COM 形式?可是,要注冊到系統中去,憑空多了系統注冊表依賴
4、還有嗎?
5、甚至可以拋開DLL,有沒有類似的一種方式,可用于二進制代碼的模塊劃分以及閉源的代碼重用?
(至于跨平臺啥的先不考慮吧,暫定Windows平臺下吧)
請不吝指教~
posted @
2010-12-18 22:35 溪流 閱讀(2247) |
評論 (15) |
編輯 收藏
首先,寫這玩意兒的目的有
- 看到 MetaWeblog API,覺得蠻有趣,想玩玩看;
- 用下之前寫的 xl::Array、xl::List、xl::Map、xl::String、xl::QIPtr,以檢驗可靠性;
- 曬曬去年這個時候寫的 XmlParser,這個很久的將來肯定重寫過的然后才能進 xlLib,這個版本只是拿來玩玩的;
- 熟悉下 WinHttp API,WinINet 時不時冒出個 bug,揪心;(這個……其實一開始想用socket寫然后同時支持linux的,可是想著想著突然記起件事情,linux上界面我不會寫啊,所以。。跨毛平臺啊)
- 某同事在 CSDN 寫博客,一會兒說“我自己寫自己的,不要別人看”,一會兒卻又抱怨沒人看不好玩,我說來吧搬到CppBlog吧這兒很熱鬧很好玩~!嗯,先把工具準備好,接下來可以天天曉之以理動之以情。
由于 google code 上已經有一個玩意兒叫 Blog Mover 了,所以我只好起個很繞口的名字 Blog Transporter 了,簡稱 BlogTrans。
源代碼:http://blogtrans.codeplex.com/
下載:http://blogtrans.codeplex.com/releases/54948/download/162649
功能有:抓下最近的 N 篇博文,包括圖片,轉發到新的位置。兩個博客都要求支持 MetaWeblog API。
嗯,開始用了~~假設我要把我的 cppblog 里的頭 5 篇文章轉到 csdn 上去。先填好兩邊的賬號信息:

中間填個數字,表示要抓幾篇。。。只能從最新的往下數多少篇。。然后那個勾勾表示是否要抓下圖片傳到目標博客上。好,按下 Start!

然后就開始了,等啊等,就好了。于是,現在我的 CSDN 博客就有了 5 篇文章,大家可以看下效果:http://blog.csdn.net/xixiaoliu
格式神馬的,應該都還在吧。。。就是有一個不好,Windows Live Writer 發的圖片,他會在圖片上再加個鏈接鏈到自己,我目前沒有去分析這個鏈接。
每篇博客之間我設置了 5 秒間隔,這是有原因的。。剛才,我測試的時候把 CppBlog 上的 37 篇全搞到 CSDN 上去,一刻不停地搞。。。結果發了 33 篇,后面 4 篇全出錯。。。用瀏覽器訪問,403 forbidden。。。果斷重啟路由器,才重新打開。。。
好啦,廣告完畢!大家多吐口水多幫忙找bug,然后把親朋好友都拉到 CppBlog 來打架吧~~~!
順便,360快點推出安全聊天吧,周教主果然眼光犀利打遍全網無敵手啊~
posted @
2010-10-30 01:57 溪流 閱讀(1724) |
評論 (4) |
編輯 收藏
由于 C++ 成員函數的調用機制問題,對C語言回調函數的 C++ 封裝是件比較棘手的事。為了保持C++對象的獨立性,理想情況是將回調函數設置到成員函數,而一般的回調函數格式通常是普通的C函數,尤其是 Windows API 中的。好在有些回調函數中留出了一個額外參數,這樣便可以由這個通道將 this 指針傳入。比如線程函數的定義為:
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
LPVOID lpThreadParameter
);
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
這樣,當我們實現線程類的時候,就可以:
class Thread
{
private:
HANDLE m_hThread;
public:
BOOL Create()
{
m_hThread = CreateThread(NULL, 0, StaticThreadProc, (LPVOID)this, 0, NULL);
return m_hThread != NULL;
}
private:
DWORD WINAPI ThreadProc()
{
// TODO
return 0;
}
private:
static DWORD WINAPI StaticThreadProc(LPVOID lpThreadParameter)
{
((Thread *)lpThreadParameter)->ThreadProc();
}
};
不過,這樣,成員函數 ThreadProc() 便喪失了一個參數,這通常無傷大雅,任何原本需要從參數傳入的信息都可以作為成員變量讓 ThreadProc 來讀寫。如果一定有些什么是非從參數傳入不可的,那也可以,一種做法,創建線程的時候傳入一個包含 this 指針信息的結構。第二種做法,對該 class 作單例限制——如果現實情況允許的話。
所以,有額外參數的回調函數都好處理。不幸的是,Windows 的窗口回調函數沒有這樣一個額外參數:
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
這使得對窗口的 C++ 封裝變得困難。為了解決這個問題,一個很自然的想法是,維護一份全局的窗口句柄到窗口類的對應關系,如:
#include <map>
class Window
{
public:
Window();
~Window();
public:
BOOL Create();
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hWnd;
protected:
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static std::map<HWND, Window *> m_sWindows;
};
在 Create 的時候,指定 StaticWndProc 為窗口回調函數,并將 hWnd 與 this 存入 m_sWindows:
BOOL Window::Create()
{
LPCTSTR lpszClassName = _T("ClassName");
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.lpfnWndProc = StaticWndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = lpszClassName;
RegisterClassEx(&wcex);
m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (m_hWnd == NULL)
{
return FALSE;
}
m_sWindows.insert(std::make_pair(m_hWnd, this));
ShowWindow(m_hWnd, SW_SHOW);
UpdateWindow(m_hWnd);
return TRUE;
}
在 StaticWindowProc 中,由 hWnd 找到 this,然后轉發給成員函數:
LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::map<HWND, Window *>::iterator it = m_sWindows.find(hWnd);
assert(it != m_sWindows.end() && it->second != NULL);
return it->second->WndProc(message, wParam, lParam);
}
(m_sWindows 的多線程保護略過,下同)
據說 MFC 采用的就是類似的做法。缺點是,每次 StaticWndProc 都要從 m_sWindows 中去找 this。由于窗口類一般會保存窗口句柄,回調函數里的 hWnd 就沒多大作用了,如果這個 hWnd 能夠被用來存 this 指針就好了,那么就能寫成這樣:
LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}
這樣看上去就爽多了。傳說中 WTL 所采取的 thunk 技術就是這么干的。之前,只是聽過這遙遠的傳說,今天,終于有機會走進這個傳說去看一看。參考資料是一篇不知原始出處的文章《深入剖析WTL—WTL框架窗口分析》,以及部分 WTL 8.0 代碼,還有其他亂七八糟的文章。
WTL 的思路是,每次在系統調用 WndProc 的時候,讓它鬼使神差地先走到我們的另一處代碼,讓我們有機會修改堆棧中的 hWnd。這處代碼可能是類似這樣的:
__asm
{
mov dword ptr [esp+4], pThis ;調用 WndProc 時,堆棧結構為:RetAddr, hWnd, message, wParam, lParam, ... 故 [esp+4]
jmp WndProc
}
由于 pThis 和 WndProc 需要被事先修改(但又無法在編譯前定好),所以我們需要運行的時候去修改這部分代碼。先弄一個小程序探測下這兩行語句的機器碼:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return 0;
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL, NULL, NULL, MB_OK);
__asm
{
mov dword ptr [esp+4], 1
jmp WndProc
}
return 0;
}
最前面的 MessageBox 是為了等下調試的時候容易找到進入點。
然后使用 OllyDbg,在 MessageBoxW 上設置斷點,執行到該函數返回:

這里我們看到,mov dword ptr [esp+4] 的機器碼為 C7 44 24 04,后面緊接著的一個 DWORD 是 mov 的第二個操作數。jmp 的機器碼是 e9,后面緊接著的一個 DWORD 是跳轉的相對地址。其中 00061000h - 0006102Bh = FFFFFFD5h。
于是定義這樣一個結構:
#pragma pack(push,1)
typedef struct _StdCallThunk
{
DWORD m_mov; // = 0x042444C7
DWORD m_this; // = this
BYTE m_jmp; // = 0xe9
DWORD m_relproc; // = relative distance
} StdCallThunk;
#pragma pack(pop)
這個結構可以作為窗口類的成員變量存在。我們的窗口類現在變成了這樣子:
class Window
{
public:
Window();
~Window();
public:
BOOL Create();
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hWnd;
StdCallThunk m_thunk;
protected:
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};
似乎少了點什么……創建窗口的時候,我們是不能直接把回調函數設到 StaticWndPorc 中去的,因為這個函數是希望被寫成這樣子的:
LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}
那么至少需要一個臨時的回調函數,在這個函數里去設置新的回調函數(設到 m_thunk 上),再由 m_thunk 來調用 StaticWndProc,StaticWndProc 再去調用 WndProc,這樣整個過程就通了。
但是,臨時回調函數還是需要知道從 hWnd 到 this 的對應關系。可是現在我們不能照搬用剛才的 m_sWindows 了。因為窗口在創建過程中就會調用到回調函數,需要使用到 m_sWindows 里的 this,而窗口被成功創建之前,我們沒法提前拿到 HWND 存入 m_sWindows。現在,換個方法,存當前線程 ID 與 this 的對應關系。這樣,這個類變成了:
#include <map>
class Window
{
public:
Window();
~Window();
public:
BOOL Create();
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hWnd;
StdCallThunk m_thunk;
protected:
static LRESULT CALLBACK TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static std::map<DWORD, Window *> m_sWindows;
}; 然后實現 Create 和 TempWndProc:
BOOL Window::Create()
{
LPCTSTR lpszClassName = _T("ClassName");
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.lpfnWndProc = TempWndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = lpszClassName;
RegisterClassEx(&wcex);
DWORD dwThreadId = GetCurrentThreadId();
m_sWindows.insert(std::make_pair(dwThreadId, this));
m_thunk.m_mov = 0x042444c7;
m_thunk.m_jmp = 0xe9;
m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (m_hWnd == NULL)
{
return FALSE;
}
ShowWindow(m_hWnd, SW_SHOW);
UpdateWindow(m_hWnd);
return TRUE;
}
LRESULT CALLBACK Window::TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::map<DWORD, Window *>::iterator it = m_sWindows.find(GetCurrentThreadId());
assert(it != m_sWindows.end() && it->second != NULL);
Window *pThis = it->second;
m_sWindows.erase(it);
WNDPROC pWndProc = (WNDPROC)&pThis->m_thunk;
pThis->m_thunk.m_this = (DWORD)pThis;
pThis->m_thunk.m_relproc = (DWORD)&Window::StaticWndProc - ((DWORD)&pThis->m_thunk + sizeof(StdCallThunk));
m_hWnd = hWnd;
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pWndProc);
return pWndProc(hWnd, message, wParam, lParam);
}
差不多可以了,調試一下。結果,在 thunk 的第一行出錯了。我原以為地址算錯了神馬的,嘗試把 thunk.m_mov 改為 0x90909090,再運行,還是出錯。于是傻掉了……過了好一會兒才意識到,可能是因為 thunk 在數據段,無法被執行。可是,很久很久以前偶滴一個敬愛的老師在 TC 中鼓搗程序運行時改變自身代碼時,貌似無此問題啊。。。然后查呀查,原來是 Windows 在的數據執行保護搞的鬼。于是,需要用 VirtualAlloc 來申請一段有執行權限的內存。WTL 里面也是這么做的,不過它似乎維護了一塊較大的可執行內存區作為 thunk 內存池,我們這里從簡。最后,整個流程終于跑通了。最終代碼清單如下:
#include <Windows.h>
#include <assert.h>
#include <map>
#include <tchar.h>
#pragma pack(push,1)
typedef struct _StdCallThunk
{
DWORD m_mov;
DWORD m_this;
BYTE m_jmp;
DWORD m_relproc;
} StdCallThunk;
#pragma pack(pop)
class Window
{
public:
Window();
~Window();
public:
BOOL Create();
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hWnd;
StdCallThunk *m_pThunk;
protected:
static LRESULT CALLBACK TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static std::map<DWORD, Window *> m_sWindows;
};
std::map<DWORD, Window *> Window::m_sWindows;
Window::Window()
{
}
Window::~Window()
{
VirtualFree(m_pThunk, sizeof(StdCallThunk), MEM_RELEASE);
}
BOOL Window::Create()
{
LPCTSTR lpszClassName = _T("ClassName");
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.lpfnWndProc = TempWndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = lpszClassName;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClassEx(&wcex);
DWORD dwThreadId = GetCurrentThreadId();
m_sWindows.insert(std::make_pair(dwThreadId, this));
m_pThunk = (StdCallThunk *)VirtualAlloc(NULL, sizeof(StdCallThunk), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
m_pThunk->m_mov = 0x042444c7;
m_pThunk->m_jmp = 0xe9;
m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (m_hWnd == NULL)
{
return FALSE;
}
ShowWindow(m_hWnd, SW_SHOW);
UpdateWindow(m_hWnd);
return TRUE;
}
LRESULT Window::WndProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
MessageBox(m_hWnd, _T("LButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
break;
case WM_RBUTTONUP:
MessageBox(m_hWnd, _T("RButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
break;
}
return DefWindowProc(m_hWnd, message, wParam, lParam);
}
LRESULT CALLBACK Window::TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::map<DWORD, Window *>::iterator it = m_sWindows.find(GetCurrentThreadId());
assert(it != m_sWindows.end() && it->second != NULL);
Window *pThis = it->second;
m_sWindows.erase(it);
WNDPROC pWndProc = (WNDPROC)pThis->m_pThunk;
pThis->m_pThunk->m_this = (DWORD)pThis;
pThis->m_pThunk->m_relproc = (DWORD)&Window::StaticWndProc - ((DWORD)pThis->m_pThunk + sizeof(StdCallThunk));
pThis->m_hWnd = hWnd;
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pWndProc);
return pWndProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
Window wnd;
wnd.Create();
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
剛才有一處,存 this 指針的時候,我很武斷地把它與當前線程 ID 關聯起來了,其實這正是 WTL 本身的做法。它用 CAtlWinModule::AddCreateWndData 存的 this,最終會把當前線程 ID 和 this 作關聯。我是這么理解的吧,同一線程不可能同時有兩處在調用 CreateWindow,所以這樣取回來的 this 是可靠的。
好了,到此為止,邊試驗邊記錄的,不知道理解是否正確。歡迎指出不當之處,也歡迎提出相關的問題來考我,歡迎介紹有關此問題的新方法、新思路,等等,總之,請各位看官多指教哈。
posted @
2010-10-24 16:44 溪流 閱讀(6605) |
評論 (40) |
編輯 收藏
我自認為一向是很不感冒Linux那些東東的,也不知道為什么,前兩天突然就心血來潮去搞一番LFS。于是很有紀念意義,特此記錄。
起先準備搞的是 LFS 6.1,因為只有 6.1 有官方中文手冊。但是我的宿主系統是 Arch Linux 2010.05,也許太新了,剛開始編譯 gcc 4.0.3 就過不了。后來就放棄了,換 6.7 的玩。
說到底這是件很無聊的事情。打過的最多的命令就是
tar -xzvf ...
tar -xvjf ...
./configure ...
make
make install
rm -rf ...
這么一套操作重復個百來下,加上無休止的等待,就成了。
以為成了,結果出狀況了:

似乎好像大概可能它找不到硬盤,而且我明明要 sda2 的,它卻找了 sdb2。
第一,在 8.4.2 grub-mkconfig -o /boot/grub/grub.cfg 的時候,grub的配置文件是利用它的命令自動生成的,結果它找錯了。可能是因為我一開始裝的時候拿塊硬盤是sdb,它就認sdb了。或者是之前那條命令 grub-install --grub-setup=/bin/true /dev/sda 我自作聰明地以為它要實際操作,把最后的sda換成了sdb的緣故吧。
第二是因為我在 VMWare 上跑,虛擬硬盤是 SCSI 的,編譯內核之前沒配置對。后來看到了 http://www.cnblogs.com/benben7466/archive/2009/04/01/1427404.html,于是把 fusion mpt 中的全選上了(文章中的 Fusion MPT (base + ScsiHost) drivers 我沒找到,于是全選了= =),重新編譯內核,啟動成功。
謹以此截圖留念:

流水賬結束了。正文開始。
我想談談對 LFS 中的工具鏈切換的理解。請允許我把 binutil 和 gcc 簡稱為編譯系統,把 glibc 簡稱為運行庫。用下面這張圖簡單表示一下:

首先,利用宿主系統的編譯系統編譯出一個依賴于宿主運行庫的新的編譯系統(Pass1),和獨立的新的運行庫(Pass1)。然后再利用運行在宿主運行庫上的新的編譯系統(Pass1)編譯出依賴于新的運行庫(Pass1)的新的編譯系統(Pass2)。這樣,產生了一個脫離宿主的編譯環境,利用這個編譯環境編譯出其他工具,一起作為臨時系統使用。
再在臨時系統中,編譯出目標系統中要用的運行庫(Pass2)和依賴于目標運行庫(Pass2)的編譯系統(Pass3)。目標系統中的編譯環境搭建完畢。最后使用這個編譯環境編譯出目標系統上的其他軟件。
不知道這個陳述有沒有問題?如果沒說錯的話,問題來了。其實,得到的臨時系統,已經是一個不依賴于宿主的系統了,何不把這個作為 LFS 的目標系統呢?理由似乎只有“使它更純凈”之類的了。如果追求純凈,多搞一遍是不夠的,還是不純凈的;既然反正不純凈,為啥多做一遍呢?
由此,我想到了挺久以前我一直壓抑在心里的問題:同一個環境下的編譯器的升級問題。加入已經有了 1.0 版的編譯器執行文件和 2.0 版的編譯器源代碼,要如何產生 2.0 版的編譯器的執行文件呢?是拿 1.0 版的去編譯 2.0 的源代碼,然后直接發布?還是再用新的 2.0 版的編譯器再編譯一遍(兩遍、三遍)?6.1 版的 LFS 手冊部分解決了這個疑問,它提到了在 gcc pass1 的時候做 bootstrap,即編譯一次后用產生的新編譯器編譯第二遍,再用產生的新的編譯器編譯第三遍,比較第二遍與第三遍結果是否相同。(LFS 6.7無此要求。)不知道這里的相同是指逐字節相同嗎?如果是,這在理論上可能嗎?我的想法是,已有的1.0版可能存在一個固有問題(或者不稱為問題,叫“特征”吧),它可能將影響到后面的一切,2.0 的編譯器不管自舉幾遍,或許總是無法完全消滅來自 1.0 的某些影響?
不知道現在理論上是怎樣回答這個問題的。工程上又是如何對待這個問題的呢?
這也許是個比較深層次的問題。抑或只是一個很膚淺的問題,只是我心生執念罷了。期待解惑 ~_~
posted @
2010-10-19 00:59 溪流 閱讀(2693) |
評論 (13) |
編輯 收藏