﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-我的一片天-随笔分类-VC6</title><link>http://www.cnitblog.com/xkz/category/6928.html</link><description>我的一片天</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 14:55:55 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 14:55:55 GMT</pubDate><ttl>60</ttl><item><title>CAsyncSocket异步机制</title><link>http://www.cnitblog.com/xkz/archive/2008/03/21/41301.html</link><dc:creator>xyz</dc:creator><author>xyz</author><pubDate>Fri, 21 Mar 2008 04:58:00 GMT</pubDate><guid>http://www.cnitblog.com/xkz/archive/2008/03/21/41301.html</guid><wfw:comment>http://www.cnitblog.com/xkz/comments/41301.html</wfw:comment><comments>http://www.cnitblog.com/xkz/archive/2008/03/21/41301.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/xkz/comments/commentRss/41301.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/xkz/services/trackbacks/41301.html</trackback:ping><description><![CDATA[<div class=box2><span class=Tit>CAsyncSocket异步机制</span></div>
<table class="htb wr" cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>
            <div class="box2 p14">当你获得了一个异步连接后，实际上你扫除了发送动作与接收动作之<br>间的依赖性。所以你随时可以发包，也随时可能收到包。发送、接收<br>函数都是异步非阻塞的，顷刻就能完成，所以收发交错进行着，你可<br>以一直工作，保持很高的效率。<br><br>但是，正因为发送、接收函数都是异步非阻塞的，所以仅调用它们并<br>不能保障发送或接收的完成。<br><br>例如发送函数Send，调用它可能有3种结果：错误、部分完成、全部完<br>成。其中错误又分两种情况：一种是由各种网络问题导致的失败，你<br>需要马上决定是放弃本次操作，还是启用某种对策；另一种是&#8220;忙&#8221;，<br>你实际上不用马上理睬。你需要调用GetLastError来判断是哪种情况，<br>GetLastError返回WSAEWOULDBLOCK，代表&#8220;忙&#8221;，为什么当你Send得<br>到WSAEWOULDBLOCK却不用理睬呢？因为CAsyncSocket会记得你的Send<br>WSAEWOULDBLOCK了，待发送的数据会写入CAsyncSocket内部的发送缓<br>冲区，并会在不忙的时候自动调用OnSend，发送内部缓冲区里的数据。<br>同样，如果Send只完成了一部分，你也不需要理睬，尚未发送的数据<br>同样会写入CAsyncSocket内部的发送缓冲区，并在适当的时候自动调<br>用OnSend完成发送。<br><br>与OnSend协助Send完成工作一样，OnRecieve、OnConnect、OnAccept<br>也会分别协助Recieve、Connect、Accept完成工作。这一切都通过消<br>息机制完成：<br><br>在你使用CAsyncSocket之前，必须调用AfxSocketInit初始化WinSock<br>环境，而AfxSocketInit会创建一个隐藏的CSocketWnd对象，由于这个<br>对象由Cwnd派生，因此它能够接收Windows消息。一方面它会接受各个<br>CAsyncSocket的状态报告，另一方面它能捕捉系统发出的各种SOCKET<br>事件。所以它能够成为高层CAsyncSocket对象与WinSock底层之间的<br>桥梁：例如某CAsyncSocket在Send时WSAEWOULDBLOCK了，它就会发送<br>一条消息给CSocketWnd作为报告，CSocketWnd会维护一个报告登记表，<br>当它收到底层WinSock发出的空闲消息时，就会检索报告登记表，然后<br>直接调用报告者的OnSend函数。所以前文所说的CAsyncSocket会自动<br>调用OnXxx，实际上是不对的，真正的调用者是CSocketWnd——它是<br>一个CWnd对象，运行在独立的线程中。</div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/xkz/aggbug/41301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/xkz/" target="_blank">xyz</a> 2008-03-21 12:58 <a href="http://www.cnitblog.com/xkz/archive/2008/03/21/41301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]MFC程序设计之来龙去脉</title><link>http://www.cnitblog.com/xkz/archive/2008/03/21/41290.html</link><dc:creator>xyz</dc:creator><author>xyz</author><pubDate>Fri, 21 Mar 2008 03:23:00 GMT</pubDate><guid>http://www.cnitblog.com/xkz/archive/2008/03/21/41290.html</guid><wfw:comment>http://www.cnitblog.com/xkz/comments/41290.html</wfw:comment><comments>http://www.cnitblog.com/xkz/archive/2008/03/21/41290.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/xkz/comments/commentRss/41290.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/xkz/services/trackbacks/41290.html</trackback:ping><description><![CDATA[MFC程序设计 之来龙去脉<br>&nbsp;题记：前些日子一直想写这个东西，做了一个开头放在我的BLOG上(但是名为&lt;MFC框架程序WINMAIN函数分析&gt;)，到后来就没有再管了，其实那只是冰山一角.具体MFC是怎么运行的，还是没有交待清楚，虽然自己的BLOG很少有人光顾，但是本着做事儿就要做到底的心态，继续完成该文。<br>&nbsp;说明：1、本文作者在VS2003中跟踪代码，此代码为VS2003中拷贝，使用MFC7。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、不同框架的MFC程序由所不同，本文以单文档为例。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3、本文读者需要有一定的SDK的基础，不需要太多，至少知道它的基本框架和来龙去脉即可！<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 4、文章只想起到说明作用，所以代码会有一些删除。<br>&nbsp;学MFC，竟然还不知道MFC的MAIN函数在什么地方？怎么运行的？实在不高明。<br>&nbsp;看过候捷(JJHOU)老师的《深入浅出MFC》的，对它一定很熟悉。呵呵，本文是献给没有看过那本书，但是又很希望学习MFC程序设计的朋友的。（没有看过那本书的朋友还不赶快去买？）其实本文，主要是对《深入浅出MFC》第六章的一个总结和补充罢了！（本文有该书不同的地方，也有一些笔者自己的见解！）<br>&nbsp;言归正传。<br>&nbsp;假如你用AppWizard一步一步NEXT下来，然后在CLASSVIEW中去找寻WINMAIN函数，那么你只有失望。MFC最大的特点是什么？封装！MFC的确封装的太好了，以至于很多想学习MFC的人都望而却步。闲话少说，还是继续我们今天的话题，MAIN函数！实话告诉你吧，即使你搜索所有的MFC生成的文件，都无法发现WINMAIN的字眼，那么它就近在什么地方呢？<br>&nbsp;我相信你已经想到，MAIN函数应该在主要的应用程序文件中。难道是&#8220;您定义的程序名.cpp&#8221;这个文件？不错就是它。再Crtl+F一下，看有没有我们要找的WINMAIN函数？看来你又要失望了，但是你注意有这样一句：<br>&nbsp;<br>/////////////////////////////////////////////////////////////////////////////<br>// The one and only CMyApp object
<p>&nbsp;CMyApp theApp;&nbsp;&nbsp; //本人建立的工程名为My。<br>&nbsp;<br>&nbsp;<br>&nbsp;是不是很特别，再注意一下那句注释&#8220;The one and only CMyApp object&#8221;，每个应用程序有且只用一个CMyApp对象。我想你应该想到了，WinMain函数每个程序也只能有一个，那么这个全局对象跟WinMain函数肯定有莫大的关系？没错，相信你的直觉。<br>&nbsp;特别注意：深晓C++细节的人一定知道，全局对象优先于MAIN函数执行的道理。如果你不知道也没关系，那么我在这里告诉你：&#8220;全局对象优先于MIAN函数执行，且构建于栈中，切记，切记！&#8221;<br>&nbsp;现在，我们该深入WinMain运行机制了，确切的说，应该是MFC的机制！<br>&nbsp;首先，看看MFC的库文件把，它能给我们带来许多惊喜。（vc6的相应的目录是\Microsoft Visual Studio\VC98\MFC\SRC；VC7相应的目录是\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc）<br>&nbsp;现在我们就从这个全局下手，开始今天的旅途。<br>&nbsp;CMyApp theApp; <br>&nbsp;此时，系统会执行CMyApp的父类（CWinApp）构造函数，再执行CMyApp的构造函数。（先有老爹，再有儿子！），此时就会调用CWinApp的构造函数。<br>&nbsp;<br>&nbsp;CWinApp的构造函数（在VC提供的MFC代码中以&#8220;文中的一个字或词组&#8221;的方式查询关键字，此时打开APPCORE.CPP，以下使用相同搜索方式，不再复述。）找到以下内容：<br>&nbsp;CWinApp::CWinApp(LPCTSTR lpszAppName)<br>&nbsp;{<br>&nbsp;&nbsp;if (lpszAppName != NULL)<br>&nbsp;&nbsp;&nbsp;m_pszAppName = _tcsdup(lpszAppName);<br>&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;m_pszAppName = NULL;<br>&nbsp;<br>&nbsp;&nbsp;// initialize CWinThread state<br>&nbsp;&nbsp;AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();<br>&nbsp;&nbsp;AFX_MODULE_THREAD_STATE* pThreadState = pModuleState-&gt;m_thread;<br>&nbsp;&nbsp;ASSERT(AfxGetThread() == NULL);<br>&nbsp;&nbsp;pThreadState-&gt;m_pCurrentWinThread = this;<br>&nbsp;&nbsp;ASSERT(AfxGetThread() == this);<br>&nbsp;&nbsp;m_hThread = ::GetCurrentThread();<br>&nbsp;&nbsp;m_nThreadID = ::GetCurrentThreadId();<br>&nbsp;<br>&nbsp;&nbsp;// initialize CWinApp state<br>&nbsp;&nbsp;ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please<br>&nbsp;&nbsp;pModuleState-&gt;m_pCurrentWinApp = this;<br>&nbsp;&nbsp;ASSERT(AfxGetApp() == this);<br>&nbsp;... ...<br>&nbsp;}<br>&nbsp;OK，就到这里就可以了，仔细看上面代码，它已经完成了应用程序线程额的启动，它给予了我们程序的生命。现在请注意：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pThreadState-&gt;m_pCurrentWinThread = this;<br>&nbsp;pModuleState-&gt;m_pCurrentWinApp = this;<br>&nbsp;&nbsp;&nbsp;&nbsp; 这两行代码其实都是做的一件事儿。<br>&nbsp;&nbsp;&nbsp;&nbsp; 这段代码的意思是，获得了CMyApp的全局对象的this指针。(此时你肯定要疑问，为什么是CMyApp的指针？this目前是在CWinApp中啊？&nbsp;&nbsp; 对此我的答案是，可是你是由CMyApp的对象引发的CWinApp的构造啊！！)这个指针可非一般的人物，稍后我们的很多工作都要靠它完成。<br>&nbsp;&nbsp;&nbsp;&nbsp; CWinApp之中的成员变量将因为theApp这个全局对象的诞生而获得配置和初始值。<br>&nbsp; 构造完父类，现在构造子类。可是我们看到，AppWizard给我们的子类里它什么也没做？是的，这一切都听从你的安排！<br>&nbsp;&nbsp;&nbsp; CMyApp::CMyApp()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;// TODO: add construction code here,<br>&nbsp;// Place all significant initialization in InitInstance<br>&nbsp;}<br>&nbsp;<br>&nbsp;<br>&nbsp;<br>&nbsp; 接下来就是今天的主角儿了，搜索关键字&#8220;WinMain&#8221;，出现很多文件。别急，因为现在我们应该先看看WinMain的声明。打开appmodul.cpp：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,<br>&nbsp;LPTSTR lpCmdLine, int nCmdShow)<br>&nbsp;{<br>&nbsp;// call shared/exported WinMain<br>&nbsp;return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);<br>&nbsp;}<br>&nbsp;<br>这里_tWinMain是为了支持UNICODE而命名的一个宏，真正起作用的是AfxWinMain，注意看看它的参数，是不是和SDK的WinMain函数一样？<br>&nbsp;现在再搜索下AfxWinMain，其实在winmain.cpp中：<br>&nbsp;<br>int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,<br>&nbsp;LPTSTR lpCmdLine, int nCmdShow)<br>{<br>&nbsp;ASSERT(hPrevInstance == NULL);</p>
<p>&nbsp;int nReturnCode = -1;<br>&nbsp;CWinThread* pThread = AfxGetThread();<br>&nbsp;CWinApp* pApp = AfxGetApp();</p>
<p>&nbsp;// AFX internal initialization<br>&nbsp;if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))<br>&nbsp;&nbsp;goto InitFailure;</p>
<p>&nbsp;// App global initializations (rare)<br>&nbsp;if (pApp != NULL &amp;&amp; !pApp-&gt;InitApplication())<br>&nbsp;&nbsp;goto InitFailure;</p>
<p>&nbsp;// Perform specific initializations<br>&nbsp;if (!pThread-&gt;InitInstance())<br>&nbsp;{<br>&nbsp;&nbsp;if (pThread-&gt;m_pMainWnd != NULL)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");<br>&nbsp;&nbsp;&nbsp;pThread-&gt;m_pMainWnd-&gt;DestroyWindow();<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;nReturnCode = pThread-&gt;ExitInstance();<br>&nbsp;&nbsp;goto InitFailure;<br>&nbsp;}<br>&nbsp;nReturnCode = pThread-&gt;Run();<br>... ...<br>}<br>&nbsp;此段代码注意五个细节：<br>&nbsp;CWinApp* pApp = AfxGetApp(); <br>&nbsp;意为获得对象指针，其实就是刚才那个THIS。不记得了？指向CMyApp的那个！还值得注意的是，Afx意是全局的，随时你都可以调用它。(AFX就是MFC开发小组的开发代号，意为Application Framework 传说X只是为了好看，没实在意思？！)<br>&nbsp;if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))<br>&nbsp;AfxWinInit完成了线程的初始化和窗框类的注册。具体参看appinit.cpp中的定义。<br>&nbsp;if (pApp != NULL &amp;&amp; !pApp-&gt;InitApplication())<br>&nbsp;其实pApp和pThread是同一个指针，都是指向CMyApp的指针，这里因为CMyApp中没有定义InitApplication，实际上就调用的CWinApp::InitApplication（），完成了MFC的内容管理。<br>&nbsp;if (!pThread-&gt;InitInstance())<br>&nbsp;因为CMyApp中改写了它，所以调用CMyApp中的，其实它也是初始化工作。此时也完成了默认窗口类的定义。假如你熟悉SDK编程的话，一定不会忘记窗口类的设计、注册、创建、现实及更新的步骤，此时MFC以为你设计好了默认的窗口类。<br>&nbsp;现在你不禁要疑问,InitApplication()和InitInstance()有何不同？<br>&nbsp;答案是，假如你执行一个程序，于是两个函数都会被调用；当你在不关闭前一个程序的前提下，再执行一个程序，那么就只执行后一个函数。<br>&nbsp;nReturnCode = pThread-&gt;Run();<br>&nbsp;这个一步骤在《深入浅出MFC》中被成为程序的活水源头，在我看来它就是你开车踩油门的步骤。待会我们会具体阐述！<br>&nbsp;<br>&nbsp;在设计窗口类以后，就应该是注册，MFC自动调用(跳转到)AfxEndDeferRegisterClass（WINCORE.CPP中），为你注册了五个窗口类，分别是：AfxWnd，AfxCreateBar，AfxMDIFrame，AfxFrameOrView，AfxOleControl以上窗口类MFC将自动转化成独立无二的类名，供其调用。<br>&nbsp;在窗口的注册以后，就应该是窗口的创建工作，此时会调用CFrameWnd::Create(),该代码位于WINFRM.Cpp中<br>BOOL CFrameWnd::Create(LPCTSTR lpszClassName,<br>&nbsp;LPCTSTR lpszWindowName,<br>&nbsp;DWORD dwStyle,<br>&nbsp;const RECT&amp; rect,<br>&nbsp;CWnd* pParentWnd,<br>&nbsp;LPCTSTR lpszMenuName,<br>&nbsp;DWORD dwExStyle,<br>&nbsp;CCreateContext* pContext)<br>{<br>&nbsp;HMENU hMenu = NULL;<br>&nbsp;if (lpszMenuName != NULL)<br>&nbsp;{<br>&nbsp;&nbsp;// load in a menu that will get destroyed when window gets destroyed<br>&nbsp;&nbsp;HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);<br>&nbsp;&nbsp;if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");<br>&nbsp;&nbsp;&nbsp;PostNcDestroy();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // perhaps delete the C++ object<br>&nbsp;&nbsp;&nbsp;return FALSE;<br>&nbsp;&nbsp;}<br>&nbsp;}</p>
<p>&nbsp;m_strTitle = lpszWindowName;&nbsp;&nbsp;&nbsp; // save title for later</p>
<p>&nbsp;if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,<br>&nbsp;&nbsp;rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,<br>&nbsp;&nbsp;pParentWnd-&gt;GetSafeHwnd(), hMenu, (LPVOID)pContext))<br>&nbsp;{<br>&nbsp;&nbsp;TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");<br>&nbsp;&nbsp;if (hMenu != NULL)<br>&nbsp;&nbsp;&nbsp;DestroyMenu(hMenu);<br>&nbsp;&nbsp;return FALSE;<br>&nbsp;}</p>
<p>&nbsp;return TRUE;<br>}<br>&nbsp;<br>&nbsp;其中完成了窗口的创建工作，里面还涉及扩展风格的调用CreateEx，具体细节请参看MSDN。<br>&nbsp;<br>&nbsp;此时你不禁要问，我们的事儿都让MFC做完了？工业化生产出来的窗口都是千篇一律啊，我要有我自己的风格！<br>&nbsp;别急，MFC给用户提供了一个修改窗口设计的机会那就是：PreCreateWindow(CREATESTRUCT&amp; cs) 你在MSDN中查询一下CREATESTRUCT这个结构体，你会发现它和我们的CreateWindow几乎是一模一样，这个就是MFC留给你修改窗口的一个机会。在PreCreateWindow时，会跳到CWnd::PreCreateWindow，里面有一个宏：AfxDeferRegisterClass，它的作用是：如果该窗口类没有被注册，那么就注册它；如果注册了，就什么也不管！<br>&nbsp;窗口类的设计、注册、创建都已经完成，现在只剩下更新和显示了。这些工作都交由 CMyApp::InitInstance()完成：<br>&nbsp;&nbsp;m_pMainWnd-&gt;ShowWindow(SW_SHOW);<br>&nbsp;&nbsp;m_pMainWnd-&gt;UpdateWindow();<br>&nbsp;现在if (!pThread-&gt;InitInstance())的工作已经完成，按照MAIN函数的内容，接下来该：nReturnCode = pThread-&gt;Run()了<br>&nbsp;此时应该调用CMyApp的Run()函数，但是在CMyApp类中，根本没有声明或定义这样一个函数，根据多态性的原来，指针迁升，指向CWinApp::Run()，其代码位于APPCORE.CPP中：<br>&nbsp;<br>&nbsp;int CWinApp::Run()<br>&nbsp;{<br>&nbsp;&nbsp;if (m_pMainWnd == NULL &amp;&amp; AfxOleGetUserCtrl())<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;// Not launched /Embedding or /Automation, but has no main window!<br>&nbsp;&nbsp;&nbsp;TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");<br>&nbsp;&nbsp;&nbsp;AfxPostQuitMessage(0);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;return CWinThread::Run();<br>&nbsp;}<br>&nbsp;<br>&nbsp;最后你会发现，它由调用了一个CWinThread::Run()，此时你就看不到CWinThread::Run()的代码了（至少笔者没有找到，因为微软只提供了部分MFC代码。）但是你可以在MSDN中找到CWinThread::Run()的描述：<br>&nbsp;Run 控制线程的函数。包含消息泵。一般不重写。 <br>&nbsp;再具体点就是：<br>&nbsp;Run acquires and dispatches Windows messages until the application receives a WM_QUIT message. If the thread's message queue currently contains no messages, Run calls OnIdle to perform idle-time processing. Incoming messages go to the PreTranslateMessage member function for special processing and then to the Windows function TranslateMessage for standard keyboard translation. Finally, the DispatchMessage Windows function is called. <br>&nbsp;Run is rarely overridden, but you can override it to implement special behavior.<br>&nbsp;This member function is used only in user-interface threads.<br>&nbsp;原来它把消息循环包装了一下，在MFC中称为消息映射（message map）的东西！至于消息映射的具体细节本人会另写文章说明！<br>&nbsp;OK，MFC不再神秘，掌握了它的来龙去脉，再看其他的MFC书籍的时候，就知道我该怎么做？为什么我要这样做？起到了知其然又知其所以然的效果，这就是我所追求的技术境界。<br></p>
<img src ="http://www.cnitblog.com/xkz/aggbug/41290.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/xkz/" target="_blank">xyz</a> 2008-03-21 11:23 <a href="http://www.cnitblog.com/xkz/archive/2008/03/21/41290.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>