﻿<?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>C++博客-提琴协奏-随笔分类-C++</title><link>http://www.cppblog.com/flagman/category/15574.html</link><description> 唐亮的个人技术博客
【欢迎转载，但请标明原作者】</description><language>zh-cn</language><lastBuildDate>Mon, 12 Dec 2011 16:19:44 GMT</lastBuildDate><pubDate>Mon, 12 Dec 2011 16:19:44 GMT</pubDate><ttl>60</ttl><item><title>C++ library series -- in the MFC multiple-thread environment, how to quit worker-thread safely which begins with AfxBeginThread</title><link>http://www.cppblog.com/flagman/archive/2011/12/11/how_to_quit_worker-thread_safely_which_begins_with_AfxBeginThread.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Sun, 11 Dec 2011 12:35:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2011/12/11/how_to_quit_worker-thread_safely_which_begins_with_AfxBeginThread.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/161934.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2011/12/11/how_to_quit_worker-thread_safely_which_begins_with_AfxBeginThread.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/161934.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/161934.html</trackback:ping><description><![CDATA[<div><div>&nbsp; In the MFC environment, normally, thread should be launched with AfxBeginThread for taking usage of MFC multiple-thread mechanism; In such mechanism, those datastructures, such as AFX_MODULE_STATE, would be used by MFC framework to maintain related thread information. It runs well when threads, launched with AfxBeginThread, quit before the main thread, which is responsible for initializing C run-time, but if such main thread quit before any other thread launched by AfxBeginThread, the current application would crash.</div><div>&nbsp; Such crash comes from the _afxThreadData (CThreadSlotData* _afxThreadData, which is defined in AFXTLS.cpp as global data structure) has been destructed while the main thread quits and it will invoke related function to clean up global data structures, including _afxThreadData definitely.</div><div>&nbsp; Consequently, serious developer should prepare for such case (other worker thread quits before main thread).</div><div>&nbsp;&nbsp;</div><div>&nbsp; The reasonable resolve for such issue, would ensure any other threads should quit before the main thread.&nbsp;</div><div>&nbsp;&nbsp;</div><div>.h file&nbsp;</div><div>&nbsp; /////////////////////////////////////////////////////////////////////////////</div><div>// CSafeEnterLeaveThread thread</div><div></div><div>class CSafeEnterLeaveThread : public CWinThread</div><div>{</div><div><span style="white-space:pre">	</span>DECLARE_DYNCREATE(CSafeEnterLeaveThread)</div><div>protected:</div><div><span style="white-space:pre">	</span>CSafeEnterLeaveThread(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // protected constructor used by dynamic creation</div><div></div><div>// Attributes</div><div>public:</div><div></div><div>// Operations</div><div>public:</div><div></div><div>// Overrides</div><div><span style="white-space:pre">	</span>// ClassWizard generated virtual function overrides</div><div><span style="white-space:pre">	</span>//{{AFX_VIRTUAL(CSafeEnterLeaveThread)</div><div><span style="white-space:pre">	</span>public:</div><div><span style="white-space:pre">	</span>virtual BOOL InitInstance();</div><div><span style="white-space:pre">	</span>virtual int ExitInstance();</div><div><span style="white-space:pre">	</span>//}}AFX_VIRTUAL</div><div></div><div>// Implementation</div><div>protected:</div><div><span style="white-space:pre">	</span>virtual ~CSafeEnterLeaveThread();</div><div></div><div><span style="white-space:pre">	</span>// Generated message map functions</div><div><span style="white-space:pre">	</span>//{{AFX_MSG(CSafeEnterLeaveThread)</div><div><span style="white-space:pre">		</span>// NOTE - the ClassWizard will add and remove member functions here.</div><div><span style="white-space:pre">	</span>//}}AFX_MSG</div><div></div><div><span style="white-space:pre">	</span>DECLARE_MESSAGE_MAP()</div><div>};</div><div></div><div>.cpp file&nbsp;</div><div>/////////////////////////////////////////////////////////////////////////////</div><div>// CSafeEnterLeaveThread</div><div></div><div>IMPLEMENT_DYNCREATE(CSafeEnterLeaveThread, CWinThread)</div><div></div><div>CSafeEnterLeaveThread::CSafeEnterLeaveThread()</div><div>{</div><div>}</div><div></div><div>CSafeEnterLeaveThread::~CSafeEnterLeaveThread()</div><div>{</div><div>}</div><div></div><div>BOOL CSafeEnterLeaveThread::InitInstance()</div><div>{</div><div><span style="white-space:pre">	</span>// TODO: &nbsp;perform and per-thread initialization here</div><div><span style="white-space:pre">	</span>ASSERT(this-&gt;m_hThread);</div><div><span style="white-space:pre">	</span>CMainApp::RegisterMFCThread(this-&gt;m_hThread);</div><div><span style="white-space:pre">	</span>return TRUE;</div><div>}</div><div></div><div>int CSafeEnterLeaveThread::ExitInstance()</div><div>{</div><div><span style="white-space:pre">	</span>// TODO: &nbsp;perform any per-thread cleanup here</div><div><span style="white-space:pre">	</span>ASSERT(this-&gt;m_hThread);</div><div><span style="white-space:pre">	</span>CMainApp::UnRegisterMFCThread(this-&gt;m_hThread);</div><div><span style="white-space:pre">	</span>return CWinThread::ExitInstance();</div><div>}</div><div></div><div>BEGIN_MESSAGE_MAP(CSafeEnterLeaveThread, CWinThread)</div><div><span style="white-space:pre">	</span>//{{AFX_MSG_MAP(CSafeEnterLeaveThread)</div><div><span style="white-space:pre">		</span>// NOTE - the ClassWizard will add and remove mapping macros here.</div><div><span style="white-space:pre">	</span>//}}AFX_MSG_MAP</div><div>END_MESSAGE_MAP()</div><div></div><div></div><div></div><div>And in the CMainApp,</div><div></div><div></div><div>set&lt;HANDLE&gt; g_ThreadHandleSet;</div><div>HANDLE g_ThreadHandleArray[MAXIMUM_WAIT_OBJECTS];</div><div>CCriticalSection g_csGlobalData;</div><div></div><div>void CAccgbApp::CheckAllOtherMFCThreadsLeave()</div><div>{</div><div><span style="white-space:pre">	</span>int count = g_ThreadHandleSet.size();</div><div><span style="white-space:pre">	</span>if (count == 0) return;</div><div><span style="white-space:pre">	</span>set&lt;HANDLE&gt;::iterator it;</div><div><span style="white-space:pre">	</span>int idx = 0;</div><div><span style="white-space:pre">	</span>for (it = g_ThreadHandleSet.begin(); it != g_ThreadHandleSet.end() &amp;&amp; idx &lt; MAXIMUM_WAIT_OBJECTS; it++, idx++)</div><div><span style="white-space:pre">	</span>{</div><div><span style="white-space:pre">		</span>g_ThreadHandleArray[idx] = *it;</div><div><span style="white-space:pre">	</span>}</div><div><span style="white-space:pre">	</span>if (count &gt; idx) count = idx;</div><div></div><div><span style="white-space:pre">	</span>::WaitForMultipleObjects(count, g_ThreadHandleArray, TRUE, INFINITE);</div><div>}</div><div></div><div>void CAccgbApp::CleanupGlobalData()</div><div>{</div><div><span style="white-space:pre">	</span>g_csGlobalData.Lock();</div><div><span style="white-space:pre">	</span>g_ThreadHandleSet.empty();</div><div><span style="white-space:pre">	</span>g_csGlobalData.Unlock();</div><div>}</div><div></div><div>BOOL CAccgbApp::RegisterMFCThread(HANDLE hThread)</div><div>{</div><div><span style="white-space:pre">	</span>if (hThread == NULL) return FALSE;</div><div><span style="white-space:pre">	</span></div><div><span style="white-space:pre">	</span>g_csGlobalData.Lock();</div><div><span style="white-space:pre">	</span>if (g_ThreadHandleSet.find(hThread) == g_ThreadHandleSet.end())&nbsp;</div><div><span style="white-space:pre">		</span>g_ThreadHandleSet.insert(hThread);</div><div><span style="white-space:pre">	</span>g_csGlobalData.Unlock();</div><div></div><div><span style="white-space:pre">	</span>return TRUE;</div><div>}</div><div></div><div>void CAccgbApp::UnRegisterMFCThread(HANDLE hThread)</div><div>{</div><div><span style="white-space:pre">	</span>if (hThread == NULL) return;</div><div></div><div><span style="white-space:pre">	</span>g_csGlobalData.Lock();</div><div><span style="white-space:pre">	</span>if (g_ThreadHandleSet.find(hThread) != g_ThreadHandleSet.end())</div><div><span style="white-space:pre">		</span>g_ThreadHandleSet.erase(hThread);</div><div><span style="white-space:pre">	</span>g_csGlobalData.Unlock();</div><div>}</div></div><img src ="http://www.cppblog.com/flagman/aggbug/161934.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2011-12-11 20:35 <a href="http://www.cppblog.com/flagman/archive/2011/12/11/how_to_quit_worker-thread_safely_which_begins_with_AfxBeginThread.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>操作系统怎么根据一个HWND句柄，找到相应的代码</title><link>http://www.cppblog.com/flagman/archive/2011/04/04/Mapping_BetweenCppObjectAndHWND_InMFC.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Mon, 04 Apr 2011 06:16:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2011/04/04/Mapping_BetweenCppObjectAndHWND_InMFC.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/143398.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2011/04/04/Mapping_BetweenCppObjectAndHWND_InMFC.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/143398.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/143398.html</trackback:ping><description><![CDATA[<p><em>【 在 某 的大作中提到: 】</em></p>
<p><em>: 比如我有一个CMyButton的类，我现在有他的一个handle<br>: 编译器怎么根据这个句柄找到CMyButton的代码的？</em></p>
<p><em>【 在 某某 的大作中提到: 】<br>: 这个和OS/Compiler没关系，是库起的作用<br>: 以从某个文章里看的，说MFC用了一个大map，没验证过<br>: 有本讲GDI的书里，用了WNDCLASS里的extra bytes来实现的这个映射</em></p>
<p>&nbsp;</p>
<p><br>MFC的应用里，每个MFC线程（必须要使用MFC方式启动的线程）都维护有一个MFC object和HWND之间的</p>
<p>mapping，整个MFC框架就是使用这个机制来实现应用级C++对象和系统级原生窗口内核对象之间的关联；</p>
<p>因为这个mapping是以线程为单位来维护的，每个线程间互不关联，所以，一个应用里对于涉及UI窗口的</p>
<p>任务最好是都放在同一个线程里面，一般就是当前进程的主线程，否则可能出现MFC object和HWND之间</p>
<p>关联不上的问题，而且这样的问题还很隐蔽。<br></p>
<p>至于WNDCLASS结构自带的extra bytes域，是以前缺乏应用框架的时代，使用Win32 API直接开发时，让每个</p>
<p>窗口类（这里的类，不是C++ class的概念，而是Windows系统窗口定义时的一种数据结构）都能有个附</p>
<p>带一些额外的自定义数据的空间，这个空间往往被用来存放与当前窗口类相关的用户数据，通常是指向</p>
<p>某个内存区域的指针，当程序操作这个属于这个窗口类的窗口时就可以根据这个附带的自定义数据（或</p>
<p>者指针）来操作对应的关联自定义数据；很多后来出现的框架，也都使用了这个extra bytes域，来存放</p>
<p>框架本身的一些和窗口类相关联的数据结构。从目前趋势看，直接使用WNDCLASS以及extra bytes的可能</p>
<p>性是微乎其微了，但是如果要做好原生应用的开发，很多底层的实现细节最要还是要知道一下，以便于</p>
<p>优化结构和性能，以及出错时的调试处理；因为无论是Winform/WPF，还是跨平台的WTL/QT/WxWindows等</p>
<p>等新型的机制或者框架、类库，只要是在Windows平台上搭建的，那都是基于前面说过的这套最基本也是</p>
<p>最核心的Win32 API基础之上。</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/flagman/aggbug/143398.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2011-04-04 14:16 <a href="http://www.cppblog.com/flagman/archive/2011/04/04/Mapping_BetweenCppObjectAndHWND_InMFC.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ library系列 -- static destructors in multiple threads</title><link>http://www.cppblog.com/flagman/archive/2011/02/08/static_destructors_in_multiple_threads.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Tue, 08 Feb 2011 12:57:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2011/02/08/static_destructors_in_multiple_threads.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/139812.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2011/02/08/static_destructors_in_multiple_threads.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/139812.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/139812.html</trackback:ping><description><![CDATA[<p>&nbsp; In VC++ 8.0, while&nbsp; code compiled with /clr or /clr:pure, static destructors sometimes would not being properly called before process exites in multiple threads.</p>
<p>&nbsp; CRT incorrectly set a lock at _global_unlock which resulted in such issue.</p>
<p>&nbsp; In CLR-mixed mode, during the inialization of static local object, CRT would call _atexit_m(_CPVFV func) in msilexit.cpp to register a special __clrcall callback function which would be called back to destroy such static object when the current AppDomain quited.</p>
<p>&nbsp; In the multithread environment, _atexit_helper which was invoked by _atexit_m, could register such callbace function successfully because it had been guarded by __global_lock() and __global_unlock(). But in the same environment, the _atexit_m would fail to assign the correct value to __onexitbegin_m and __onexitend_m.</p>
<p>&nbsp; __onexitbegin_m and __onexitend_m were shared by the different threads; It's the key point of such issue. For example, the following statements,</p>
<p>&nbsp; __onexitbegin_m = (_CPVFV *)_encode_pointer(onexitbegin_m);<br>&nbsp; __onexitend_m = (_CPVFV *)_encode_pointer(onexitend_m);</p>
<p>should also guarded by __global_lock() and __global_unlock() or other syn primitives.</p>
<p><br>__global_lock();<br>__onexitbegin_m = (_CPVFV *)_encode_pointer(onexitbegin_m);<br>__onexitend_m&nbsp;&nbsp; = (_CPVFV *)_encode_pointer(onexitend_m);<br>__global_unlock();</p>
<p><br>extern "C" int __clrcall _atexit_m(_CPVFV func)<br>{<br>&nbsp;MANAGED_ASSERT(AppDomain::CurrentDomain-&gt;IsDefaultAppDomain(), "This fuction must be called in the default domain");</p>
<p>&nbsp;__global_lock();<br>&nbsp;_CPVFV* onexitbegin_m = (_CPVFV*)_decode_pointer(__onexitbegin_m);<br>&nbsp;_CPVFV* onexitend_m = (_CPVFV*)_decode_pointer(__onexitend_m);<br>&nbsp;__global_unlock();</p>
<p>&nbsp;int retval = _atexit_helper((_CPVFV)_encode_pointer(func), &amp;__exit_list_size, &amp;onexitend_m, &amp;onexitbegin_m);</p>
<p>&nbsp;__global_lock();<br>&nbsp;__onexitbegin_m = (_CPVFV*)_encode_pointer(onexitbegin_m);<br>&nbsp;__onexitend_m&nbsp; = (_CPVFV*)_encode_pointer(onexitend_m);<br>&nbsp;__global_unlock();</p>
<p>&nbsp;return retval;<br>}</p>
<img src ="http://www.cppblog.com/flagman/aggbug/139812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2011-02-08 20:57 <a href="http://www.cppblog.com/flagman/archive/2011/02/08/static_destructors_in_multiple_threads.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ library系列 -- STL实现中的ODR “one-definition-rule” for types</title><link>http://www.cppblog.com/flagman/archive/2010/12/19/136923.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Sun, 19 Dec 2010 03:09:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2010/12/19/136923.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/136923.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2010/12/19/136923.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/136923.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/136923.html</trackback:ping><description><![CDATA[<p>Linking issue<br>- While different modules (.obj) using istreambuf_iterator/ostreambuf_iterator, compiled with different options on HID/no-HID and SCL/no-SCL, these modules could not be linked successfully;</p>
<p>The error comes directly from the CLR when a type has multiple definitions that are not consistent based upon the ODR, one-definition-rule for types. And, the linker itself isn't involved.</p>
<p>For example, with one module compile with /D_SECURE_SCL=0, while another is compiled with _SECURE_SCL=1;</p>
<p>At first, it's found that with _SECURE_SCL, the only thing that could be different as following,</p>
<p>#if _SECURE_SCL<br>&nbsp;&nbsp;&nbsp; typedef _Range_checked_iterator_tag _Checked_iterator_category;<br>#endif</p>
<p>But, actually, it's not the typedef that changed the layout the these iterators (istreambuf_iterator/ostreambuf_iterator), and further they don't really use the extra pointer that _SECURE_SCL adds.</p>
<p>Finally, it's found the root cause is that, these iterators, istreambuf_iterator/ostreambuf_iterator&nbsp; had been moved from &lt;xutility&gt; to &lt;streambuf&gt;, and their ultimate base class had been changed from _Iterator_base_secure to _Iterator_base. And, the layout of _Iterator_base would be different between HID and no-HID, and between SCL and no-SCL. It is the cause where the issue comes from.</p>
<p>What we can learn from such issue,<br>These iterators really shouldn't derive from either _Iterator_base_secure or _Iterator_base, because these classes contain data members (pointers) which are entirely unused. It would result in unnecessary bloat and extra work being performed in ctor/dtor etc.</p>
<p>Introduce a new class, _Iterator_base_universal, which is defined identically regardless of HID/no-HID and SCL/no-SCL. It would contains the three internal typedefs that all standard iterators need to have, and nothing else. And _Iterator_base (in all of its variants) and _Iterator_base_secure now should derive from _Iterator_base_universal to get these typedefs.</p>
<p>Now, when an iterator wants these typedefs, but not the data members of _Iterator_base and _Iterator_base_secure, it could derive from _Iterator_base_universal. And istreambuf_iterator and ostreambuf_iterator are now as small as possible, and keep identical representations or layout across HID/no-HID, SCL/no-SCL.<br></p>
<img src ="http://www.cppblog.com/flagman/aggbug/136923.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2010-12-19 11:09 <a href="http://www.cppblog.com/flagman/archive/2010/12/19/136923.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于COM和.net的思考</title><link>http://www.cppblog.com/flagman/archive/2010/12/19/AboutComAndDotNet.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Sun, 19 Dec 2010 03:04:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2010/12/19/AboutComAndDotNet.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/136922.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2010/12/19/AboutComAndDotNet.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/136922.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/136922.html</trackback:ping><description><![CDATA[<p><em>【 某某提到: 】<br>: 一般说COM复杂，首先是名词太多，其次是基于ATL的实现比较难懂<br>: 这并不是COM本身复杂，而是C++已经落后于时代了。所以ATL看起来才会像天书一般</em></p>
<p><br>虽然对于全新的工程项目，推荐通过.net实现，但是，只要你工作在Windows平台上，必然会遇到和COM相关的技术和机制，无论是大量的legacy的工程和代码，还是作为OS重要功能以及native组件的首选交互形式和接口暴露方式，比如DirectX API，比如一些WMI的API；最有趣的是，即使是.net的核心CLR本身也是一个COM组件，可以通过Host相关接口让native应用来加载，以在当前进程中启动整个CLR的虚拟执行环境或者叫托管执行环境(managed executive environment)。</p>
<p>把握COM有两点很关键，<br>1）Interface-based design，从设计和编码思路上就是要完全基于接口；<br>2）VirtualTable-based binary compatibility, 实现上无论何种语言或者机制，只要符合基于虚表的二进制兼容规范，就都可以实施；</p>
<p>COM仅仅是个规范，基于COM的具体技术非常之多，OLE，Automation，Structural storage，ActiveX...汗牛充栋，还有COM+，这个是提供企业级开发必备的一些基础功能和设施，比如，事务管理机制，对象池，安全管理，消息队列...需要指出，目前即便是.net Framework也没有实现COM+所提供这些机制，只是简单的封装了后者。</p>
<p>COM技术中可能有一些比较困难的地方，接口的一致性，对象的聚合和生命周期，套间，跨套间的接口访问，名字对象，等等；这些并不是COM规范人为制造的困难，而是为了设计和提供，可以跨进程和机器边界，跨异构平台（当然必须实现了COM所规定的基础服务），透明化具体对象类型及对象生命周期，便于统一部署和版本管理的组件技术，所必须付出的代价，这个代价从开发人员角度看具体表现为，概念理解的困难以及具体二进制实现的困难；</p>
<p>不过从另一个角度看，COM已经很容易了，<br>a) COM规范已把要达致这些目标的系统，所必须提供的接口和特性抽象了出来，只不过为了表达这些抽象的概念而新造的术语名词有些陌生和突兀；如果让遇到相似问题的每一个设计和开发人员都自己来做抽象，未必会生成更好的方案；</p>
<p>b) 为了帮助设计和开发人员，人们提供了很多的开发库，以提高COM开发的正确性和效率；最显著的就是MFC中关于COM/OLE的辅助类和函数，以及为了COM而生的ATL；从本质上看，这些类库都是把COM规范中必须实现的，Windows平台本身没有提供，具体设计和开发人员实际实施时会重复实现的，同时又非常容易出错的那部分功能，集中到了这些类库里统一实现，让具体设计和开发人员以代码重用的形式来实现COM规范；</p>
<p>当然人们也意识到了COM这样的一些问题，特别是具体实现时设计和开发人员必须要关注几乎所有的二进制细节，于是.net就诞生了，把这些规范的许多复杂性都封装在了虚拟机里面，把这些目标功能（跨边界、透明性等等）通过一致而又平滑的平台接口和自描述的meta data，以一种让设计和开发人员更易接受的风格开放了出来；</p>
<p>COM的影响是非常广大的，比如XPCOM ，Firefox上的一种插件技术标准，就是根据COM的思想和原则制定的；许多评论说，Firefox的成功是因为它插件是如此的成功，这也算是COM本身所意料不到的贡献之一。</p>
<p>在.net的平台上，即使是.net CLR/SSCLI的具体实现也大量运用了COM的思想和机制，可以说.net就是搭建在COM二进制组件平台之上的虚拟机托管平台。</p>
<p>最后，.net开始时的内部编号是COM 2.0</p>
<p>&nbsp;</p>
<p>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</p>
<p>*) 关于&#8220;名词太多&#8221;<br>这是要实现可以跨进程和机器边界，跨异构平台（当然必须实现了COM所规定的基础服务），透明化具体对象类型及对象生命周期，便于统一部署和版本管理的组件技术，所必须付出的代价。</p>
<p>COM规范已把要达致这些目标的系统，所必须提供的接口和特性抽象了出来，只不过为了表达这些抽象的概念而新造的术语名词有些陌生和突兀；如果让遇到相似问题的每一个设计和开发人员都自己来做抽象，未必会生成更好的方案；</p>
<p>举个例子，apartment，套间，就是为了抽象传统OS中进程和线程的实现而新造的术语名词和概念；任何人要抽象这样的一些概念，不新造术语，是非常困难的，对比.net，后者用了CLR虚拟机来封装了大多数的实现细节，并用让人更容易接受的风格来开放接口，可事实上仍然新造了一些名词和概念，如类似范畴的AppDomain；</p>
<p>*) 关于&#8220;基于ATL的实现比较难懂&#8221;<br>ATL主要使用了template技术，COM接口智能指针，用静态转换来模拟动态绑定，等等，实际并不是很复杂，只能算c++实现机制的中等难度，主要涉及Modern C++ design一书中一些相关设计理念的运用。对比Boost中某些库的实现，ATL很人道了。</p>
<p>*) 关于&#8220;这并不是COM本身复杂，而是C++已经落后于时代了&#8221;<br>首先COM的规范的确是复杂的，为啥？第一点已经说了，就是为了要抽象出跨边界和对象透明的组件技术；.net表象上看比较&#8220;简单容易&#8221;，风格亲近设计和开发人员，实际上复杂事务和实现细节都被划分到CLR那个层面上去实现了；去看一下CLR的开源实现SSCLI，你会发现，整个虚拟机平台的实现，大量运用了COM的思想和机制，就是一个巨型系统平台级的COM server；</p>
<p>其次，COM规范本身是独立于实现语言的，只要构建出的组件符合规范制定的二进制兼容，系统就可以运作，这和C++是否落后时代没有关系。如果开发人员认为，.net才够先进，也完全可以用.net中的托管语言，如C#来实现COM组件；</p>
<p>最后，每种语言都有其适用的范围，现在可以这么说&#8220;如果有一个全新的项目需求，要达致跨边界和对象透明组件，并且没有太过严苛的性能需求，那么.net平台及其上的托管语言来实现，比用C++及相关辅助类库来以COM组件形式来实现，要更合适，也更快速便捷和节省预算。&#8221;但是，在这个判断上我们加了很多严格的约束，一旦需求变更，特别是项目的非功能性需求，要求高性能运算或者更顺畅的与legacy的native系统相互，那么&#8220;使用native语言来实现性能关键以及legacy交互功能，通过COM封装，再由COMInterop交.net托管应用调用&#8221;可能是更现实的方案。C++是一门活的语言，不断发展的语言，即使在最新的托管时代里，C#成为标准主流，但C++/CLI仍然是托管语言里功能最完整的语言。</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/flagman/aggbug/136922.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2010-12-19 11:04 <a href="http://www.cppblog.com/flagman/archive/2010/12/19/AboutComAndDotNet.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CLR系列--探索SSCLI【1】</title><link>http://www.cppblog.com/flagman/archive/2010/12/13/136255.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Mon, 13 Dec 2010 01:02:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2010/12/13/136255.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/136255.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2010/12/13/136255.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/136255.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/136255.html</trackback:ping><description><![CDATA[<p>Fusion is one of the most importants features among ones in the runtime implementation of CLI.</p>
<p>In the fusion, or any other components or modules, how to retrieve the execution engine instance and how to generate such engine?</p>
<p>UtilExecutionEngine, implemented as COM object, support Queryinterface/AddRef/Release, and exposed via interface IExecutionEngine.</p>
<p>With SELF_NO_HOST defined, <br>BYTE g_ExecutionEngineInstance[sizeof(UtilExecutionEngine)];<br>g_ExecutionEngineInstance would be the singleton instance of current execution engine,</p>
<p>otherwise, without SELF_NO_HOST, the 'sscoree' dll would be loaded and try to get the exported function, which is named 'IEE' from such dll. Here, it is the well-known shim, in .net CLR, such module is named 'mscoree'. Further, if 'IEE' could not be found in such dll, system would try to locate another exported function, named 'LoadLibraryShim', and use such function to load the 'mscorwks' module, and try to locate the 'IEE' exportd functionin it.</p>
<p>It's very obvious that Rotor has implemented its own execution engine, but it also gives or make space for implementation of execution engine from 3rd party. Here, .net CLR is a good candidate definitely, Rotor might load the mscorwks.dll module for its usage.</p>
<p>PAL, PALAPI, for example, HeapAlloc, one famous WIN32 API, has been implemented as one PALAPI (defined in Heap.c), to make it possible that the CLI/Rotor be ported smoothly to other OS, such freebsd/mac os.</p>
<p>CRT routines are also reimplemented, such as memcpy, it has been implemented as GCSafeMemCpy</p>
<p>There're many macros in fuctions, such as SCAN_IGNORE_FAULT/STATIC_CONTRACT_NOTHROW/STATIC_CONTRACT_NOTRIGGER, they are for static analysis tool to scan, analyse and figour out the potential issues in code.</p>
<p>From view point of the execution model by CLI, the act of compiling (including JIT) high-level type descriptions would be separated from the act of turning these type descriptions into processor-specific code and memory structures.</p>
<p>And such executino model, in other word, the well-known 'managed execution', would defer the loading, verification and compilation of components until runtime really needs; At the same time, the type-loading is the key trigger that causes CLI's tool chain to be engaged at runtime. Deferred compilation(lead to JIT)/linking/loading would get better portability to different target platform and be ready for version change; The whole deferred process would driven by well-defined metadata and policy, and it would be very robust for building a virtual execution environment;</p>
<p>At the top of such CLI tool chain, fusion is reponsible for not only finding and binding related assemblies, which are via assembly reference defined in assembly, fusion also takes another important role, loader, and its part of functionality is implemented in PEAssembly, ClassLoader classes. For example, ClassLoader::LoadTypeHandleForTypeKey.</p>
<p>For types in virtual execution environment of CLI, rotor defines four kinds of elements for internal conducting, <br>ELEMENT_TYPE_CLASS for ordinary classes and generic instantiations(including value types);<br>ELEMENT_TYPE_ARRAY AND ELEMENT_TYPE_SZARRAY for array types<br>ELEMENT_TYPE_PRT and ELEMENT_TYPE_BYREF for pointer types<br>ELEMENT_TYPE_FNPTR for function pointer types</p>
<p>every type would be assigned unique ulong-typed token, and such token would be used to look up in m_TypeDefToMethodTableMap (Linear mapping from TypeDef token to MethodTable *)which is maintained by current module; If there it is, the pointer to method table of such type would be retrieved, or it would look up in the loader module, where the method table should exist in while it's JIT loaded, not launched from NGEN image;</p>
<p>And all the unresolved typed would be maintained in a hash table, PendingTypeLoadTable; Types and only those types that are needed, such as dependencies, including parent types, are loaded in runtime, such type is fully loaded and ready for further execution, and other unresolved types would be kept in the previous hash table.</p>
<img src ="http://www.cppblog.com/flagman/aggbug/136255.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2010-12-13 09:02 <a href="http://www.cppblog.com/flagman/archive/2010/12/13/136255.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为何C++中的类成员函数没有采用类似Java中的“全虚”设计</title><link>http://www.cppblog.com/flagman/archive/2010/12/13/WhyNotCPPadoptAllVirtualFuction.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Mon, 13 Dec 2010 00:57:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2010/12/13/WhyNotCPPadoptAllVirtualFuction.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/136254.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2010/12/13/WhyNotCPPadoptAllVirtualFuction.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/136254.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/136254.html</trackback:ping><description><![CDATA[<p>关于程序设计语言本身的设计有许多有趣的话题，比如，为何C++中的类成员函数没有采用类似Java中的&#8220;全虚&#8221;设计？<br><br>1) 从语言本身设计上看，<br>效率定然是c++当初设计时考虑的重点之一，举个例子，为了节省不必要的VTable开销，ATL用template技术静态转换来模拟动态绑定以支持COM特性的实现；和C的兼容，就VTable角度看，问题不大，因为后者可以用函数指针数组来模拟；</p>
<p>2) 再从大多数应用中常见的类继承体系上看，<br>除了整个继承体系所统一开放出来的接口集（也就是由虚函数所组成），在继承体系的每个层面另外会有大量的其他辅助成员函数（其数量通常比虚函数多的多），这些成员函数完全没必要设计成虚函数；</p>
<p>3) 从其他语言看，<br>即使较新的虚拟机语言C#(Java算是较老的虚拟机语言),反而定义了比C++更为严格更为显式的成员方法实现或覆盖或重载或新建的规则；这是非常重要的对C++以及Java设计思想的反思。</p>
<p>4) 从语言的适用场合看，<br>我们现在的讨论，绝大多数情况下带有一个非常重要的默认前提，那就是在用户态模式下使用C++，如果放宽这个约束，在内核模式下使用C++，那情况又完全不同了。<br>引用下面这个文档的观点，<a href="http://www.microsoft.com/china/whdc/driver/kernel/KMcode.mspx">http://www.microsoft.com/china/whdc/driver/kernel/KMcode.mspx</a><br>首先，用户态下非常廉价几乎不用考虑的资源，在内核中是非常昂贵的，比如内核堆栈一般就3个page；</p>
<p>在内核不能分页(paging)时必须保证将被执行的所有代码和数据必须有效的驻留在物理内存中，如果这时需要多驻留几张虚表以及虚表指针那还是显得非常昂贵的，同时编译器为虚函数，模板等生成代码的方式，让开发人员很难确定要执行一个函数所需要的所有代码的所在位置，因此也无法直接控制用于安置这些代码的节（个人认为可能通过progma segment/datasegment/codesegment对于代码和数据进行集中控制），因此在需要这些代码时，可能已经被page out了；</p>
<p>所有涉及类层次结构，模板，异常等等这样的一些语言结构在内核态中都可能是不安全的，最好是把类的使用限定为POD类，回到我们的主题虚函数，也就是说内核态下类设计中没有虚函数。</p>
<img src ="http://www.cppblog.com/flagman/aggbug/136254.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2010-12-13 08:57 <a href="http://www.cppblog.com/flagman/archive/2010/12/13/WhyNotCPPadoptAllVirtualFuction.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>