1/16/2004

Rich Edit Shortcut Keys

Rich edit controls support the following shortcut keys.

Keys Operations Comments
Shift+Backspace Generate a LRM/LRM on a bidi keyboard BiDi specific
Ctrl+Tab Tab
Ctrl+Clear Select all
Ctrl+Number Pad 5 Select all
Ctrl+A Select all
Ctrl+E Center alignment
Ctrl+J Justify alignment
Ctrl+R Right alignment
Ctrl+L Left alignment
Ctrl+C Copy
Ctrl+V Paste
Ctrl+X Cut
Ctrl+Z Undo
Ctrl+Y Redo
Ctrl+''+'' (Ctrl+Shift+''='') Superscript
Ctrl+''='' Subscript
Ctrl+1 Line spacing = 1 line.
Ctrl+2 Line spacing = 2 lines.
Ctrl+5 Line spacing = 1.5 lines.
Ctrl+'' (apostrophe) Accent acute After pressing the short cut key, press the appropriate letter (for example a, e, or u). This applies to English, French, German, Italian, and Spanish keyboards only.
Ctrl+` (grave) Accent grave See Ctrl+'' comments.
Ctrl+~ (tilde) Accent tilde See Ctrl+'' comments.
Ctrl+; (semicolon) Accent umlaut See Ctrl+'' comments.
Ctrl+Shift+6 Accent caret (circumflex) See Ctrl+'' comments.
Ctrl+, (comma) Accent cedilla See Ctrl+'' comments.
Ctrl+Shift+'' (apostrophe) Activate smart quotes
Backspace If text is protected, beep and do not delete it. Otherwise, delete previous character.
Ctrl+Backspace Delete previous word. This generates a VK_F16 code.
F16 Same as Backspace.
Ctrl+Insert Copy
Shift+Insert Paste
Insert Overwrite DBCS does not overwrite.
Ctrl+Left Arrow Move cursor one word to the left. On bidi keyboard, this depends on the direction of the text.
Ctrl+Right Arrow Move cursor one word to the right. See Ctrl+Left Arrow comments.
Ctrl+Left Shift Left alignment In BiDi documents, this is for left-to-right reading order.
Ctrl+Right Shift Right alignment In BiDi documents, this is for right-to-left reading order.
Ctrl+Up Arrow Move to the line above.
Ctrl+Down Arrow Move to the line below.
Ctrl+Home Move to the beginning of the document.
Ctrl+End Move to the end of the document.
Ctrl+Page Up Move one page up. If in SystemEditMode and Single Line control, do nothing.
Ctrl+Page Down Move one page down. See Ctrl+Page Up comments.
Ctrl+Delete Delete the next word or selected characters.
Shift+Delete Cut the selected characters.


Esc Stop drag-drop. While doing a drag-drop of text.
Alt+Esc Change the active application.
Alt+X Converts the Unicode hexadecimal value preceding the insertion point to the corresponding Unicode character.
Alt+Shift+X Converts the Unicode character preceding the insertion point to the corresponding Unicode hexadecimal value.
Alt+0xxx (Number Pad) Inserts Unicode values if xxx is greater than 255. When xxx is less than 256, ASCI range text is inserted based on the current keyboard. Must enter decimal values.


Alt+Shift+Ctrl+F12 Hex to Unicode. In case Alt+X is already taken for another use.
Alt+Shift+Ctrl+F11 Selected text will be output to the debugger window and saved to %temp%\DumpFontInfo.txt. For Debug only (need to set Flag=8 in Win.ini)
Ctrl+Shift+A Set all caps.
Ctrl+Shift+L Fiddle bullet style.
Ctrl+Shift+Right Arrow Increase font size. Font size changes by 1 point in the range 4pt-11pt; by 2points for 12pt-28pt; it changes from 28pt -> 36pt -> 48pt -> 72pt -> 80pt; it changes by 10 points in the range 80pt - 1630pt; the maximum value is 1638.
Ctrl+Shift+Left Arrow Decrease font size. See Ctrl+Shift+Right Arrow comments.

1/11/2004

让msxml4导出的文本xml缩进和换行

[url=http://expert.csdn.net/expert/Topicview2.asp?id=2637982]讨论链接[/url]
问起过好多次的问题了,手头正好在做XML的生成,就写了一下
其实,缩进和换行就是文本,在需要的位置创建文本节点就可以了。另外一个方法就是用SAX来写
[pre]










可以这么写出来
void CChatEditDlg::SendContent(BSTR bstrContent)
{
CHARFORMAT2& rcf=m_wndSend.GetCharFormatSelection();
CComBSTR bstrXML;
USES_CONVERSION;

try{
IXMLDOMDocument2Ptr pDoc;
IXMLDOMProcessingInstructionPtr pProcessingInstruction;
IXMLDOMElementPtr pLog,pMessage;
IXMLDOMElementPtr pFrom,pTo;
IXMLDOMElementPtr pUser;
IXMLDOMElementPtr pText;

HRESULT hr = pDoc.CreateInstance(__uuidof(MSXML2::DOMDocument40));
if(pDoc){
pDoc->async = false;
pDoc->resolveExternals = false;
pDoc->validateOnParse = true;
}
//pProcessingInstruction=pDoc->createProcessingInstruction(_T("xml"),
// _T("version=''1.0'' encoding=''UTF-16''"));
//pDoc->appendChild(pProcessingInstruction);
///Log
pLog=pDoc->createElement(_T("Log"));
hr=pDoc->appendChild(pLog);
AppendTextNode(pDoc,pLog,_T("\r\n\t"));
///Log/Message
pMessage=pDoc->createElement(_T("Message"));
hr=pLog->appendChild(pMessage);
AppendTextNode(pDoc,pMessage,_T("\r\n\t"));

COleDateTime t=COleDateTime::GetCurrentTime();
COleDateTime td=t;
td.m_dt=(int)td.m_dt;
COleDateTime tt=t-td;
///Log/Message/@Date
pMessage->setAttribute(L"Date",COleVariant(td));
///Log/Message/@Time
pMessage->setAttribute(L"Time",COleVariant(tt));
///Log/Message/@DateTime
pMessage->setAttribute(L"DateTime",COleVariant(t));
///Log/Message/@SessionID
pMessage->setAttribute(L"SessionID",(long)1);
///Log/Message/From
pFrom=pDoc->createElement(_T("From"));
hr=pMessage->appendChild(pFrom);
AppendTextNode(pDoc,pFrom,_T("\r\n\t\t"));
///Log/Message/From/User
pUser=pDoc->createElement(_T("User"));
hr=pFrom->appendChild(pUser);
//AppendTextNode(pDoc,pUser,_T("\r\n\t\t"));
AppendTextNode(pDoc,pFrom,_T("\r\n\t"));
AppendTextNode(pDoc,pMessage,_T("\r\n\t"));
///Log/Message/From/User/@LogonName
pUser->setAttribute(L"LogonName" ,_T("user1@somesite.com"));
///Log/Message/From/User/@FriendlyName
pUser->setAttribute(L"FriendlyName" ,_T("user1"));
///Log/Message/To
pTo=pDoc->createElement(_T("To"));
hr=pMessage->appendChild(pTo);
AppendTextNode(pDoc,pTo,_T("\r\n\t\t"));
///Log/Message/To/User
pUser=pDoc->createElement(_T("User"));
hr=pTo->appendChild(pUser);
///Log/Message/To/User/@LogonName
pUser->setAttribute(L"LogonName" ,_T("user2@somesite.com"));
///Log/Message/To/User/@FriendlyName
pUser->setAttribute(L"FriendlyName" ,_T("user2"));
AppendTextNode(pDoc,pTo,_T("\r\n\t"));
AppendTextNode(pDoc,pMessage,_T("\r\n\t"));
///Log/Message/Text
pText=pDoc->createElement(_T("Text"));
pText->put_text(bstrContent);
hr=pMessage->appendChild(pText);
pText->setAttribute(L"Style" ,(LPCTSTR)GetStyleAttrib(rcf));
AppendTextNode(pDoc,pMessage,_T("\r\n"));
//element
//date property
//time property
//datetime property
//SessionID property
// element
// element
// LogonName property
// FriendlyName property
//
// element
// LogonName property
// FriendlyName property
// element
// Style property
// element
//date property
//time property
//datetime property
//SessionID property
// element
// element
// LogonName property
// FriendlyName property
// element
// element
// Style property
//element
//date property
//time property
//datetime property
//SessionID property
// element
// element
// LogonName property
// FriendlyName property
pMessage->get_xml(&bstrXML);
TRACE(_T("%s\r\n"),OLE2T(bstrXML));
}
catch(...){
return ;
}
}
void CChatEditDlg::AppendTextNode(IXMLDOMDocument2Ptr pDoc
,IXMLDOMElementPtr pElement
,LPCTSTR lpszText)
{
IXMLDOMNodePtr pTextNode=pDoc->createTextNode(lpszText);
pElement->appendChild(pTextNode);
}[/pre]

1/07/2004

QSort

QSort大家都很熟悉了,VC运行时刻库也提供了一个函数,可以方便地使用QSort算法
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
参见
[url=http://support.microsoft.com/support/ kb/articles/Q216/8/58.ASP]216858 - HOWTO: Quick Sorting Using MFC CArray-Derived Classes[/url]
[pre]
下面是我自己写的一个实现
#include
#include

class CContact:public CObject
{
public:
CContact(){bFailLoadPic=FALSE;}
CString strNickName;
CString strUserID;
CString strFirstName;
CString strFamilyName;
CString strEmail;
CString strPic;
BOOL bFailLoadPic;
CPictureHolder pic;
int Compare(const CContact& pContact) const;
BOOL WriteToFile(LPCTSTR lpszFile,LPCTSTR lpszStyleSheet=NULL);
};
typedef int (__cdecl *GENERICCOMPAREFN)(const void * elem1, const void * elem2);
typedef int (__cdecl *CONTACTCOMPAREFN)(const CContact ** elem1, const CContact ** elem2);

class CQuickSortContactArray :public CTypedPtrArray
{
public:
CQuickSortContactArray();
virtual ~CQuickSortContactArray();

void Clear();
void Sort(CONTACTCOMPAREFN pfnCompare = Compare);
static int __cdecl Compare(const CContact ** pstr1, const CContact ** pstr2);
BOOL ReadFromFile(LPCTSTR lpszFile);
BOOL ReadFromMemory(LPVOID lpData,DWORD cbData);
BOOL ReadFromStream(IStream* pStream);
protected:
CString GetValueString(IXMLDOMNodePtr pXMLDomNode,LPCTSTR lpszChildNodeName);
HRESULT CreateXMLDOM(IXMLDOMDocument2Ptr & pIXMLDOMDocument2);
BOOL AnalysisXMLDOM(IXMLDOMDocument2Ptr pIXMLDOMDocument2);
void ReportXMLDOMError(IXMLDOMDocument2Ptr pIXMLDOMDocument2);
};
// QuickSortContactArray.cpp: implementation of the CQuickSortContactArray class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "TipControl.h"
#include "QuickSortContactArray.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
int CContact::Compare(const CContact& pContact) const
{
return strNickName.Compare(pContact.strNickName);
}
BOOL CContact::WriteToFile(LPCTSTR lpszFile,LPCTSTR lpszStyleSheet)
{
IXMLDOMDocument2Ptr pDoc;
HRESULT hr ;
try{
hr = pDoc.CreateInstance(__uuidof(MSXML2::DOMDocument40));
if(pDoc){
pDoc->async = false;
pDoc->resolveExternals = false;
pDoc->validateOnParse = true;
}
else
return FALSE;
IXMLDOMProcessingInstructionPtr pProcessingInstruction;
IXMLDOMElementPtr pRoot,pParent,pChild;
pProcessingInstruction=pDoc->createProcessingInstruction(_T("xml"),
_T("version=''1.0'' encoding=''UTF-16''"));
pDoc->appendChild(pProcessingInstruction);

if(::PathFileExists(lpszStyleSheet)){
CString strTemp;
strTemp.Format(_T("type=''text/xsl'' href=''%s''"),lpszStyleSheet);
pProcessingInstruction=pDoc->createProcessingInstruction(_T("xml-stylesheet"),
(LPCTSTR)strTemp);
pDoc->appendChild(pProcessingInstruction);
}
pRoot=pDoc->createElement(_T("Data"));
hr=pDoc->appendChild(pRoot);
TRACE(_T("%s\r\n"),(LPCTSTR)pDoc->xml);
pParent=pDoc->createElement(_T("Contact"));
hr=pRoot->appendChild(pParent);
TRACE(_T("%s\r\n"),(LPCTSTR)pDoc->xml);
pChild=pDoc->createElement(_T("NickName"));
pChild->text=(LPCTSTR)strNickName;
hr=pParent->appendChild(pChild);
TRACE(_T("%s\r\n"),(LPCTSTR)pDoc->xml);
pChild=pDoc->createElement(_T("UserID"));
pChild->text=(LPCTSTR)strUserID;
hr=pParent->appendChild(pChild);

pChild=pDoc->createElement(_T("FirstName"));
pChild->text=(LPCTSTR)strFirstName;
hr=pParent->appendChild(pChild);

pChild=pDoc->createElement(_T("FamilyName"));
pChild->text=(LPCTSTR)strFamilyName;
hr=pParent->appendChild(pChild);

pChild=pDoc->createElement(_T("Email"));
pChild->text=(LPCTSTR)strEmail;
hr=pParent->appendChild(pChild);

pChild=pDoc->createElement(_T("Pic"));
pChild->text=(LPCTSTR)strPic;
hr=pParent->appendChild(pChild);
hr = pDoc->save(lpszFile);
}
catch(...){
return FALSE;
}

return SUCCEEDED(hr);
}
CQuickSortContactArray::CQuickSortContactArray()
{

}

CQuickSortContactArray::~CQuickSortContactArray()
{
Clear();
}
int CQuickSortContactArray::Compare(const CContact** pstr1, const CContact** pstr2)
{
ASSERT(pstr1);
ASSERT(pstr2);
return (*pstr1)->Compare(*(*pstr2));
}

void CQuickSortContactArray::Sort(CONTACTCOMPAREFN pfnCompare
/*= CQuickSortContactArray::Compare */)
{
//CContact ** prgstr =(CContact**)GetData();
qsort(GetData(),GetSize(),sizeof(CContact*),(GENERICCOMPAREFN)pfnCompare);
}
void CQuickSortContactArray::Clear()
{
CContact* pContact=NULL;
for(int i=0;i pContact=GetAt(i);
if(pContact)delete pContact;
}
RemoveAll();
}
HRESULT CQuickSortContactArray::CreateXMLDOM(IXMLDOMDocument2Ptr & pIXMLDOMDocument2)
{
// Create the DOM
HRESULT hr = pIXMLDOMDocument2.CreateInstance(__uuidof(MSXML2::DOMDocument40));
if(pIXMLDOMDocument2){
pIXMLDOMDocument2->async = false;
pIXMLDOMDocument2->resolveExternals = false;
pIXMLDOMDocument2->validateOnParse = true;
}
return hr;
}
void CQuickSortContactArray::ReportXMLDOMError(IXMLDOMDocument2Ptr pIXMLDOMDocument2)
{
IXMLDOMParseErrorPtr pError;
pError = pIXMLDOMDocument2->parseError;
_bstr_t parseError =_bstr_t("At line ")+ _bstr_t(pError->Getline())
+ _bstr_t("\n")+ _bstr_t(pError->Getreason());
AfxMessageBox((LPCTSTR)parseError);
}
BOOL CQuickSortContactArray::ReadFromFile(LPCTSTR lpszFile)
{
IXMLDOMDocument2Ptr pIXMLDOMDocument2;
HRESULT hr ;
try
{
hr = CreateXMLDOM(pIXMLDOMDocument2);
// Load the document into the DOM
hr = pIXMLDOMDocument2->load(lpszFile);
// If document does not load report the parse error
if(hr!=VARIANT_TRUE){
ReportXMLDOMError(pIXMLDOMDocument2);
return FALSE;
}
if(!AnalysisXMLDOM(pIXMLDOMDocument2))
Clear();
}catch(...){
CString strError;
strError.Format(IDS_STRING102,lpszFile);
::AfxMessageBox(strError);
return FALSE;
}
return TRUE;
}
BOOL CQuickSortContactArray::ReadFromMemory(LPVOID lpData,DWORD cbData)
{
COleStreamFile osf;
if(!osf.CreateMemoryStream())return FALSE;
osf.Write(lpData,cbData);
osf.SeekToBegin();
return ReadFromStream(osf.GetStream());
}
BOOL CQuickSortContactArray::ReadFromStream(IStream* pStream)
{
IXMLDOMDocument2Ptr pIXMLDOMDocument2;
HRESULT hr ;
try
{
hr = CreateXMLDOM(pIXMLDOMDocument2);
// Load the document into the DOM
hr = pIXMLDOMDocument2->load(pStream);
// If document does not load report the parse error
if(hr!=VARIANT_TRUE){
ReportXMLDOMError(pIXMLDOMDocument2);
return FALSE;
}
if(!AnalysisXMLDOM(pIXMLDOMDocument2))
Clear();
}catch(...){
return FALSE;
}
return TRUE;
}

CString CQuickSortContactArray::GetValueString(IXMLDOMNodePtr pXMLDomNode,LPCTSTR lpszChildNodeName)
{
CString strRet;
IXMLDOMNodePtr pChild=pXMLDomNode->selectSingleNode(lpszChildNodeName);
if(pChild)
strRet=(LPCTSTR)pChild->text;
return strRet;
}
BOOL CQuickSortContactArray::AnalysisXMLDOM(IXMLDOMDocument2Ptr pIXMLDOMDocument2)
{
Clear();
IXMLDOMElementPtr pXMLDocElement = NULL;
pXMLDocElement = pIXMLDOMDocument2->documentElement;
IXMLDOMNodeListPtr pXMLDomNodeList = NULL;
pXMLDomNodeList = pXMLDocElement->selectNodes("Contact");
int count = 0;
count = pXMLDomNodeList->length;
for(int i=0;i IXMLDOMNodePtr pXMLDomNode= pXMLDomNodeList->item[i];
CContact* pContact=new CContact;
pContact->strUserID=GetValueString(pXMLDomNode,_T("UserID"));
pContact->strNickName=GetValueString(pXMLDomNode,_T("NickName"));
pContact->strFirstName=GetValueString(pXMLDomNode,_T("FirstName"));
pContact->strFamilyName=GetValueString(pXMLDomNode,_T("FamilyName"));
pContact->strEmail=GetValueString(pXMLDomNode,_T("Email"));
pContact->strPic=GetValueString(pXMLDomNode,_T("Pic"));
Add(pContact);
}
Sort();
return TRUE;
}
[/pre]

1/05/2004

分析MFC中的映射

 

分析MFC中的映射

条件查找映射

MFC中大量使用了BEGIN_XXX_MAP这样的宏,以及映射进行查找优化,例如消息映射,OLE命令映射,以及接口等等。每个映射包含一个指向基类的映射的指针。这样,当一个类需要根据一定的条件查找一个对象时,它会查找本类对象,如果没有找到,那么会查找基类,直到根基类。这类查找包含Windows消息,命令,事件和OLE命令的分发,和对象实现的接口的查询等等。

下面是函数BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)的部分代码,演示了如何根据消息的ID查找处理函数。
 

const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();
UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);
AfxLockGlobals(CRIT_WINMSGCACHE);
AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
const AFX_MSGMAP_ENTRY* lpEntry;
if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
{
// cache hit
lpEntry = pMsgCache->lpEntry;
AfxUnlockGlobals(CRIT_WINMSGCACHE);
if (lpEntry == NULL)
return FALSE;

// cache hit, and it needs to be handled
if (message < 0xC000)
goto LDispatch;
else
goto LDispatchRegistered;
}
else
{
// not in cache, look for it
pMsgCache->nMsg = message;
pMsgCache->pMessageMap = pMessageMap;

#ifdef _AFXDLL
for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;
pMessageMap = (*pMessageMap->pfnGetBaseMap)())
#else
for (/* pMessageMap already init'ed */; pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMap)
#endif
{
// Note: catch not so common but fatal mistake!!
// BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)
#ifdef _AFXDLL
ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
#else
ASSERT(pMessageMap != pMessageMap->pBaseMap);
#endif

if (message < 0xC000)
{
// constant window message
if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
message, 0, 0)) != NULL)
{
pMsgCache->lpEntry = lpEntry;
AfxUnlockGlobals(CRIT_WINMSGCACHE);
goto LDispatch;
}
}
else
{
// registered windows message
lpEntry = pMessageMap->lpEntries;
while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
{
UINT* pnID = (UINT*)(lpEntry->nSig);
ASSERT(*pnID >= 0xC000 || *pnID == 0);
// must be successfully registered
if (*pnID == message)
{
pMsgCache->lpEntry = lpEntry;
AfxUnlockGlobals(CRIT_WINMSGCACHE);
goto LDispatchRegistered;
}
lpEntry++; // keep looking past this one
}
}
}

pMsgCache->lpEntry = NULL;
AfxUnlockGlobals(CRIT_WINMSGCACHE);
return FALSE;
}

LDispatch:

注意

  1. 对查找结果的缓存可以提高查找的效率。
  2. 不要被MFC起的名字欺骗了,从数据结构上来说,查找是顺序的,而不是使用CMap类使用的散列技术,所以上面的代码中使用散列技术,缓存最近的查找结果和把最常用的映射项放在最前面通常有助于提高效率。

使用查找映射的好处是,可以方便地在派生类中扩展和覆盖映射(例如重新实现IDispatch),而不用重写/重载查找函数(消息和命令的分发,或者接口的查询);也可以不使用对资源消耗很大的虚函数表。(尽管如此,CWnd类还是有无数个虚函数,并且不出意外地看到,在MFC6到MFC7的升级中又有增加)

使用查找映射的坏处么,当然是理解上的问题和性能上的损失了。
 

句柄映射

MFC在把句柄封装成对象方面不遗余力,为了保证同一线程内对象<->句柄映射是一对一的,创建了各种各样的句柄映射,窗口,GDI对象,菜单诸如此类。为了封装GetDlgItem,SelectObject这样的API返回的临时的句柄,MFC还产生临时的对象<->句柄映射。句柄映射使得GetParentFrame这样的函数可以实现。

CFrameWnd* CWnd::GetParentFrame() const
{
if (GetSafeHwnd() == NULL) // no Window attached
return NULL;

ASSERT_VALID(this);

CWnd* pParentWnd = GetParent(); // start with one parent up
while (pParentWnd != NULL)
{
if (pParentWnd->IsFrameWnd())
return (CFrameWnd*)pParentWnd;
pParentWnd = pParentWnd->GetParent();
}
return NULL;
}
_AFXWIN_INLINE CWnd* CWnd::GetParent() const
{ ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::GetParent(m_hWnd)); }

看到了么,它首先调用API GetParent,然后去本线程的窗口<->句柄映射查找对象指针,然后调用CWnd::IsFrameWnd来决定对象是否是框架。(谢天谢地,这个函数是用虚函数而不是用CObject::IsKindOf,不然又得遍历一遍运行时类信息)。因为对象和句柄的对应的唯一性,所以可以找到唯一的父框架窗口对象。

在一些经常调用的函数里面也使用到这个映射

LRESULT CALLBACK
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
// special message which identifies the window as using AfxWndProc
if (nMsg == WM_QUERYAFXWNDPROC)
return 1;

// all other messages route through message map
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
ASSERT(pWnd != NULL);
ASSERT(pWnd->m_hWnd == hWnd);
if (pWnd == NULL || pWnd->m_hWnd != hWnd)
return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}

也就是说,它要遍历一遍afxMapHWND()返回的对象里面的永久的句柄映射。而这个函数在每个消息到达的时候都要调用。这是MFC应用程序性能损失的原因之一。

同样的,由于这些对象是被线程所拥有的,MFC的这些句柄映射的存储方式是线程局部存储(thread-local-storage ,TLS)。也就是说,对于同一个句柄,句柄映射中相应的对象可以不一致。这在多线程程序中会造成一些问题,参见微软知识库文章Q147578 CWnd Derived MFC Objects and Multi-threaded Applications

总结

MFC为了快速和方便地开发作了很多工作,例如上述的两种映射,但是性能方面有所损失。开发应用程序时,需要在快速方便和性能损失方面的权衡。(话是这么说,但是根据摩尔定律,再过两年我的话就成废话了)

1/04/2004

添加 Windows Media 支持

Jay LoomisMicrosoft New Media Platforms Division 2002 年 11 月适用于: Windows Media Format 9 Series摘要:本文介绍了 Microsoft® Windows Media® Format 9 Series 软件开发工具包 (SDK) 的新增功能,还概括介绍了 SDK 结构。(本文包含一些指向英文站点的链接。)原文链接[url=http://www.microsoft.com/china/msdn/library/dnwmt/html/AddingWindowsMediaSupportwiththeWindowsMediaFormat.asp]使用 Windows Media Format 9 Series SDK 添加 Windows Media 支持[/url]为什么微软的文章出得这么慢……偶学习WM的时候看到这篇文章就好了下面是一些其他的文章(中文的哦)
[url=http://www.microsoft.com/china/msdn/library/dnwmt/html/WMServices_9_SDK_Intro.asp]Windows Media Services SDK 简介 [/url]
[url=http://www.microsoft.com/china/msdn/library/dnwmt/html/wmp_readme.asp]Windows Media Player 9 Series 软件开发工具包自述 [/url][url=http://www.microsoft.com/china/msdn/library/dnwmt/html/WMPlayer_9_SDK_Intro.asp]Windows Media Player SDK 简介 [/url][url=http://www.microsoft.com/china/msdn/library/dnwmt/html/WM_Metadata_Usage.asp]Windows Media 元数据使用指南 [/url]
[url=http://www.microsoft.com/china/msdn/library/dnexxml/html/xml06172002.asp]在使用 XPath 查询 XML 文档时应了解和避免的事情 [/url]