8/30/2005

C++/CLI中的默认属性访问

目前版本的VC2005测试版中,default关键字不仅用于指定类级别的索引器,而且也用于访问默认属性。但是奇怪的是,默认属性的原名不能访问了,也就是说,如果要把下面的代码段从托管C++移植到VC2005附带的C++/CLI,不仅需要更改指针的类型,而且要把属性的名称更改为default:


//[System::Reflection::DefaultMemberAttribute("Fields")] interface _Recordset

//托管C++语法
//extern _Recordset* results;
Fields* ResultFields=results->Fields;

//C++/CLI语法
//extern _Recordset^ results;
Fields^ ResultFields=results->default;


如果继续使用原来名字来访问属性的话,会报告编译错误:


Fields^ ResultFields=results->Fields;//C3293: 'Fields': use 'default' to access the default property (indexer)


这是一个Breaking Change。在语言规范中,默认索引属性只使用一个名字“default”,而且只有这一个实现。更进一步,默认索引属性只能用如下方式访问:



  • obj[index]
  • obj->default[index]
  • obj->default::get(index)
  • obj->default::set(index, value)

顺便说一下,在C++/CLI中也可以使用类似C#里面的for each语句了( http://msdn2.microsoft.com/library/ms177202(en-us,vs.80).aspx),而且对于非托管的STL容器也有效,不过看起来真不习惯。


参考


8/24/2005

集成桌面搜索,模拟器

微软的桌面搜索API推出也有一段时间了,但是网上可以找到的相关技术资料还不多。官方的资料在http://addins.msn.com/devguide.aspx可以看到,而且网页上有SDK和一个C#的示例供下载。
使用搜索API非常的简单,首先创建一个桌面搜索对象
BOOL CWDSSampleView::PreCreateWindow(CREATESTRUCT& cs)
{
 // TODO: Modify the Window class or styles here by modifying
 //  the CREATESTRUCT cs
 if(m_pSearchDesktop.CreateInstance(CLSID_SearchDesktop))
 {
  AfxMessageBox(IDS_FAILED_TO_CREATE_SEARCH_ENGINE);
  return FALSE;
 }


……
之后就可以开始执行搜索了。桌面搜索对象有两个方法,ExecuteSQLQuery和ExecuteQuery,都返回一个ADO记录集对象。ExecuteQuery是对用户比较友好的版本,参数虽然比较多,但是不需要自己构建SQL;而ExecuteSQLQuery是底层版本,只有一个参数——需要自己构造的SQL。相信我,你不会渴望自己来创建SQL的。传递给ExecuteQuery的参数就已经够长的了。字符串表中IDS_COLUMNS_GENERAL的内容是:
DocTitle,DocFormat,Url,DocAuthor,PrimaryDate,FileName, FileExt,IsAttachment,Characterization,Rank,PerceivedType, HasAttach,DocTitlePrefix,FileExtDesc,DisplayFolder, DocKeywords,DocComments,ConversationID,Size, Create,Write。


void CWDSSampleView::Search(LPCTSTR lpszQuery,LPCTSTR lpszSort)
{
 CString strQuery(lpszQuery);if(strQuery.IsEmpty())return;
 CString strSort(lpszSort);
 USES_CONVERSION;
 HRESULT hr=S_OK;
 GetListCtrl().SetItemCount(0);
 ClearCache();
 try{
  CString strColumns;
  VERIFY(strColumns.LoadString(IDS_COLUMNS_GENERAL));
  if(strSort.IsEmpty())
   m_pRecordset=m_pSearchDesktop->ExecuteQuery(T2OLE(strQuery),
    T2OLE(strColumns),NULL,NULL);
  else
   m_pRecordset=m_pSearchDesktop->ExecuteQuery(T2OLE(strQuery),
    T2OLE(strColumns),T2OLE(strSort),NULL);
  int nItemCount=m_pRecordset->GetRecordCount();
  GetListCtrl().SetItemCount(nItemCount);
  
 }
 catch(_com_error&e)
 {
  ……
 }
}


Windows Desktop Search Example(VC/MFC)
但是,访问返回的记录集的速度比访问数据库要慢。我不得不用虚列表和缓存来提高性能。在搜索结果很多(例如关键字选择"Microsoft")时程序有假死现象——当然也不排除我选择的字段过多的原因。


最近在写一个14位CPU的模拟器,CPU指令长度是固定的——13字节,十分的不吉利^_^b,而且CPU指令集中一些特定指令会根据上下文判断是否跳过下一个指令。但是在Intel系统上没有这样的指令,而且指令长度是可变的,所以无法知道下一个指令的长度来跳过它。我现在是在内存中设置一个标志,在执行每个指令之前检查这个标志来判断前一个指令是否指明跳过当前指令——低效,但是可以正常工作。


现在我知道一些模拟器为什么慢得像乌龟爬了……

8/20/2005

MFC,欢乐与痛苦

MFC提供了许多十分有用的类和对象,在很多时候在Office插件、BHO、常规DLL这样的工程中加入MFC支持是一个不错的选择。但是,MFC中的很多功能,例如资源查找,消息预处理等等都依赖于在进程或者线程创建时被初始化的MFC内部数据;而对于需要添加MFC支持的工程,这些数据并不会被自动地初始化。这时候使用一些MFC的功能,例如使用CString从字符串表加载一个字符串,或者使用CDialog::DoModal()创建一个模态对话框,都会有断言错误,用ATL向导创建的支持MFC的程序也没有多少改善,在CWinApp的DLL版本中没有初始化线程数据,所以调用AfxGetThread会返回空指针。解决这个问题的一个办法是使用AfxBeginThread来启动一个MFC线程,这样MFC会初始化线程相关的数据。在下面的示例中,我在线程初始化时建立了一个模态对话框,以避免直接创建模态对话框会触发的断言失败信息。为了模拟模态对话框的效果,在CDialogThread::WaitForDoModal()这个函数中创建了一个消息循环来等待线程结束,同时用MsgWaitForMultipleObjects来避免死锁。因为MFC中和进程相关的数据并不总是被正确初始化,在调用模态对话框之前也需要手动设置一下。

//如果这段代码可以工作,那么它的作者是Jiangsheng
//否则我不知道它的作者
void __stdcall CFrontPageAddin::OnClickButtonExportCHM(IDispatch* /*Office::_CommandBarButton*
*/ Ctrl,VARIANT_BOOL * CancelDefault)
{
AtlTrace(_T(" CFPAnt::OnClickButtonExportCHM\n"));
//create a thread to avoid assert failure
CDialogThread* pDialogThread=
(CDialogThread*)AfxBeginThread(RUNTIME_CLASS(CDialogThread),
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED,NULL);
if(pDialogThread)
{
pDialogThread->m_prc=RUNTIME_CLASS(CExportCHMSheet);
pDialogThread->ResumeThread();
pDialogThread->WaitForDoModal();
delete pDialogThread;
}
}
class CDialogThread : public CWinThread
{
......
int m_nModalResult;
CRuntimeClass* m_prc;
void WaitForDoModal();
......
};
CDialogThread::CDialogThread()
{
m_bAutoDelete=FALSE;
m_prc=NULL;
m_nModalResult=0;
}
BOOL CDialogThread::InitInstance()
{
// TODO: perform and per-thread initialization here
AFX_MANAGE_STATE(AfxGetAppModuleState());
AFX_MODULE_STATE* pModuleState=AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle=_Module.GetModuleInstance();
AfxSetResourceHandle(_Module.GetModuleInstance());
if(m_prc)
{
if(m_prc->IsDerivedFrom(RUNTIME_CLASS(CDialog)))
{
CDialog* pDialog=(CDialog*)m_prc->CreateObject();
if(pDialog)
{
m_pMainWnd=pDialog;
m_nModalResult=pDialog->DoModal();
}
}
else if(m_prc->IsDerivedFrom(RUNTIME_CLASS(CPropertySheet)))
{
CPropertySheet* pDialog=(CPropertySheet*)m_prc->CreateObject();
if(pDialog)
{
m_pMainWnd=pDialog;
m_nModalResult=pDialog->DoModal();
}
}
}
return FALSE;
}
void CDialogThread::WaitForDoModal()
{
//from http://blogs.msdn.com/oldnewthing/archive/2005/02/17/375307.aspx
MSG msg;
UINT cRecords = 0;
while (true) {
switch (MsgWaitForMultipleObjects(1, &m_hThread,
FALSE, INFINITE, QS_ALLINPUT)) {
case WAIT_OBJECT_0:
return ; // event has been signalled
break;
case WAIT_OBJECT_0+1:
// we have a message - peek and dispatch it
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
break;
default:
return ; // unexpected failure
}
}
}


使用VC6.0编译通过。

上面的方法只对于DLL之类的组件比较有用。对于使用MFC的应用程序来说,上面的方法比较繁琐。比较简单的方法还是使用MFC向导来创建应用程序,然后再添加ATL或者.Net之类的额外支持。参考微软知识库文章Q181505和Q824480。
参考

BUG: You receive an "ASSERT in wincore.cpp" assert when an MFC application calls a function in MFC regular DLL in Visual C++(http://support.microsoft.com/kb/194300)
PRB: ATL COM AppWizard Doesn't Offer MFC Support for .EXE(http://support.microsoft.com/kb/181505)
PRB:为 C++ DLL 项目建立托管扩展时出现链接器警告(http://support.microsoft.com/kb/814472)
BUG: "HRESULT - 0x80010106" Error When You Run a Managed C++ Application(http://support.microsoft.com/kb/824480)