Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1049890
  • 博文数量: 288
  • 博客积分: 10306
  • 博客等级: 上将
  • 技术积分: 3182
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-12 17:00
文章分类

全部博文(288)

文章存档

2011年(19)

2010年(38)

2009年(135)

2008年(96)

我的朋友

分类: 系统运维

2009-04-16 16:57:33

   我是个ACE菜鸟,前一阵子头痛于万事开头难的问题,很多问题相当SB,不过考虑到很多初学者和我一样被老板骂得焦头烂额,这里还是把学习 ACE第一周遇到的问题贴上来,希望对刚刚接触ACE的鸟伴有所帮助(-:<。本贴讨论的范围:

1、解决5.6版本的ACE使用 ACE_HAS_MFC 后提示WIN32_NT版本过低的问题。

2、MFC应用程序调用ACE库,不进行任何操作还存在内存泄露的问题;

3、MFC程序加载的动态链接库隐含调用ACE方法导致的内存泄露;

4、解决宽字符集和窄字符集的编译器自动兼容问题;


下面逐一解决:

1、解决Visual Studio 2008 编译5.6版本的ACE使用 ACE_HAS_MFC 后提示WIN32_WINNT版本过低的问题。
 原因在config-win32-common的这一句:
#if !defined (_WIN32_WINNT)
# define _WIN32_WINNT 0x0400
#endif
改为
#if !defined (_WIN32_WINNT)
# define _WIN32_WINNT 0x0600
#endif
即可


2、MFC应用程序调用ACE库,不进行任何操作还存在内存泄露的问题;

    这种情况需要解决两个问题:

    首先是由于ACE的Object_Manager的工作方式造成不以main()入口的进程无法自动初始化和清理。在以main()函数为入口的程序(控制台之类),或者使用了宽字符的_w_main入口的程序中,ACE库会自动在入口处调用 ACE::init()完成对象的初始化,在结束时调用ACE::fini()进行对象清理。而MFC程序和以WinMain入口的程序,ACE不会自己调用。因此,需要手工添加。重载CWinApp的InitInstance()和ExitInstance(),在所有步骤开始前初始化ACE,在所有工作结束后终止ACE,范例:
BOOL CMyApp::InitInstance()
{
 ...
 CWinApp::InitInstance();
 ...
 ACE::init();
 ....
}
int CMyApp::ExitInstance()
{
 ACE::fini();
 return CWinApp::ExitInstance();
}
    如果在Dll中,不要试图在DllMain之类的入口进行初始化,会导致内存泄露。在Dll中导出两个方法,一个用于初始化,一个用于终止,分别调用ACE::init();ACE::fini();就可以。范例:

int MY_EXT_DLL MY_init(void)
{
 ...
 return ACE::init();
}
int MY_EXT_DLL MY_fini(void)
{
 ...
 return ACE::fini();
}
主程序:
BOOL CMyApp::InitInstance()
{
 ...
 CWinApp::InitInstance();
 ...
 MY_init();
 ....
}
int CMyApp::ExitInstance()
{
 MY_fini();
 return CWinApp::ExitInstance();
}


    其次,ACE编译的时候,默认的config.h中没有定义ACE_HAS_MFC,导致ACE的线程并不是派生自MFC的CWinThread,于是在程序结束时维护线程的额外信息无法清理。这需要在编译ACE时指名MFC选项。
#ifndef ACE_HAS_MFC
#define ACE_HAS_MFC 1
#endif
    这种做法有缺点。这样做后,编译出的ACEDLL将需要MFC库,更要命的是,如果打算在DLL中使用,必须创建基于MFC的DLL。一般的适用案例:
 MFCApp->ACE(MFC)
 MFCApp->MFCDLL->ACE(MFC)
    不过解决了这两个问题,一般就能解决MFC程序中的内存泄露了!


3、MFC程序加载的动态链接库隐含调用ACE方法导致的内存泄露;
    首先,如果加载ACE的动态链接库是标准WindowsDll,而使用该Dll的是MFC程序,MFCApp->Win32Dll->ACE(Win32),就比较麻烦。一般这种应用方式带来的是一次性的内存泄露,不影响工作。只是让人很不爽罢了。如果还允许修改工程结构,则把调用过程改做:MFCApp->MFCDLL->ACE(MFC),其他步骤就如第一个问题中所述。


4、解决宽字符集和窄字符集的兼容问题;
    自从Visual Studio 2005后,编译器默认字符集就是宽字符。包括CString在内的多数对象均采用Unicode字符集。一般为了在宽字符集和窄字符集中切换,程序员会使用一系列的模版方法,比如_tcscpy()与TCHAR混用来克服一致性的问题,使得在编译时切换字符集不必重写代码。
    如果没有在ACE编译时指定ACE_USES_WCHAR,ACE所有函数入口都是窄字符的,将增加编程的复杂性。比如,我定义了一个ACE内存映射对象,需要初始化当前文件的名字,为了兼容Unicode字符集和窄字符可能这样写:
class CMyClass
{
 ...
 ACE_Mem_Map m_memmap;
 ...
}
void CMyClass::MyFun1()
{
 CString strDiskFileName(_T("NoName"));

 //获取文件名
 CFileDialog filedlg(FALSE,_T(".map"),0,4|2,_T("映像文件(*.map)|*.map|所有文件(*.*)|*.*|"));
 if (filedlg.DoModal()==IDCANCEL)
  return;
 strDiskFileName = filedlg.GetPathName();

 //打开文件
#ifdef _UNICODE
--> if (m_memmap.map(CW2A(strDiskFileName),nSize,O_RDWR | O_CREAT)==-1)
 {
  if (m_memmap.map(nSize)==-1)
   return NULL;
 }
#else
--> if (m_memmap.map(strDiskFileName,nSize,O_RDWR | O_CREAT)==-1)
 {
  if (m_memmap.map(nSize)==-1)
   return NULL;
 }
#endif
 ...
}
    这段代码使用了MFC的CW2A类,如果程序中多次出现类似CW2A的转换,当突然需要改为窄字符时,就需要大改代码。何况,Win32API没有提供CW2A类,取而代之的是系统方法调用。还接着上一个例子,如果想在非MFCDLL中取得页面文件的名字并保存,代码变得十分可怕:
class CMyClass
{
 ...
 ACE_Mem_Map m_memmap;
 TCHAR m_strFileName[BUFLEN];
 ...
}
void CMyClass::MyFun2()
{
 const char * pcstrFileName = m_memmap.filename();
#ifdef _UNICODE 
 //转换窄字符为宽字符,没有MFC只能调用API
 size_t origsize = strlen(pcstrFileName) + 1;
 size_t convertedChars = 0;

 if (0==::MultiByteToWideChar(CP_THREAD_ACP,MB_PRECOMPOSED,pcstrFileName,origsize+1,m_strFileName,BUFLEN))
 {
  MessageBoxA(0,"代码页错误!","Error",MB_OK);
  return ;
 }
#else
 _tcscpy(m_strFileName,pcstrFileName);
#endif
 ...
}

为了避免这种麻烦,采用如下手段:
    首先,修改工程文件,除了Debug和Release外创建UnicodeDebug,UnicodeRelease编译方案,为他们设置"Use Unicode Character Set"字符集选项;输出文件为UnicodeRelease:ACEu.dll,ACEu.lib,UnicodeDebug:ACEud.dll,ACEud.lib,存放到固定的文件夹中。
    而后,重写config.h,基本如下:

#ifdef _UNICODE
#ifndef ACE_USES_WCHAR
#define ACE_USES_WCHAR
#endif
#endif

#ifndef ACE_HAS_MFC
#define ACE_HAS_MFC 1
#endif

#include "ace/config-win32.h"

    重新编译,将编译生成四组库,ACE/ACEd/ACEu/ACEud,可以同样修改QoS及其他工程。注意,Qos链接选项中的ACEdll要配合字符集选项设置。接着,创建$(ACEDIR)/ACEPreInclude.h,如下:

//本文件提供对ACE的智能引用,链接对应的库
#ifdef _UNICODE
#define ACE_USES_WCHAR
 #ifdef _DEBUG
  #pragma comment( lib, "aceud.lib")
 #else
  #pragma comment( lib, "aceu.lib")
 #endif
#else
 #ifdef _DEBUG
  #pragma comment( lib, "aced.lib")
 #else
  #pragma comment( lib, "ace.lib")
 #endif
#endif

    最后,在要使用ACE库的项目中,Stdafx.h加入:
    #include "ACEPreInclude.h"
    即可。编译器将自动根据字符集选项选择相应的库进行连接。写好后,上述范例代码变为:
class CMyClass
{
 ...
 ACE_Mem_Map m_memmap;
 ACE_TCHAR m_strFileName[BUFLEN];
 ...
}
void CMyClass::MyFun2()
{
 const ACE_TCHAR * pcstrFileName = m_memmap.filename();
 _tcscpy(m_strFileName,pcstrFileName);
}
简单多了哦!

阅读(3604) | 评论(0) | 转发(0) |
0

上一篇:ACE_日志的使用

下一篇:编译过程

给主人留下些什么吧!~~