随笔-90  评论-947  文章-0  trackbacks-0
 

目录:

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 溪流 阅读(2110) | 评论 (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 溪流 阅读(2209) | 评论 (15)编辑 收藏

首先,写这玩意儿的目的有

  1. 看到 MetaWeblog API,觉得蛮有趣,想玩玩看;
  2. 用下之前写的 xl::Array、xl::List、xl::Map、xl::String、xl::QIPtr,以检验可靠性;
  3. 晒晒去年这个时候写的 XmlParser,这个很久的将来肯定重写过的然后才能进 xlLib,这个版本只是拿来玩玩的;
  4. 熟悉下 WinHttp API,WinINet 时不时冒出个 bug,揪心;(这个……其实一开始想用socket写然后同时支持linux的,可是想着想着突然记起件事情,linux上界面我不会写啊,所以。。跨毛平台啊)
  5. 某同事在 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 上去。先填好两边的账号信息:

image

中间填个数字,表示要抓几篇。。。只能从最新的往下数多少篇。。然后那个勾勾表示是否要抓下图片传到目标博客上。好,按下 Start!

image

然后就开始了,等啊等,就好了。于是,现在我的 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 溪流 阅读(1700) | 评论 (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 上设置断点,执行到该函数返回:

image

这里我们看到,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 溪流 阅读(6405) | 评论 (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 简称为运行库。用下面这张图简单表示一下:

image

首先,利用宿主系统的编译系统编译出一个依赖于宿主运行库的新的编译系统(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 溪流 阅读(2670) | 评论 (13)编辑 收藏
仅列出标题
共18页: First 7 8 9 10 11 12 13 14 15 Last