博文

[置顶] 类静态成员变量的导出问题(2009-06-03 16:01:00)

摘要:编程许久,在使用DLL导出类的时候,用的都是AFX_EXT_CLASS,直到近日,才终于发现这个家伙有点缺陷。     让我们来看这样一个问题:新建一个MFC Extension DLL ,并在其中新建一个类testA。   // testA.h class AFX_EXT_CLASS testA 
{
public:
   testA();
   virtual ~testA();
public:
   static const double PI;

};              在头文件中声明了类静态变量PI,并在testA.cpp中进行了初始化。  const double testA::PI = 3.14; 编译通过,生成lib,dll.   试验发现:     如果客户是一般的exe程序,使用PI正常,编译通过。     如果客户是另一个DLL,使用PI的时候编译通过不了,报如下错误: error   LINK2001:   unresolved   external   symbol   ... PI ...      在网上找了好久,没有找到解决方案,只好用全局变量PI来代替,或者使用全局函数来返回PI的值,即  double GetPI()
{
 return testA::PI;
}           有一天终于发现了解决办法:即采用普通的__declspec(dllexport)和__declspec(dllimport),而不采用AFX_EXT_CLASS   // testA.h #ifdef SIMPLEDLL_EXPO......

阅读全文(8590) | 评论:0

GDI对象个数异常(2012-10-27 15:00:00)

摘要: 通过任务管理器发现,自己在重绘控件时,导致大量GDI对象增加,从而拖慢程序,并且崩溃。
原因分析发现是有多处调用GetDC后,而没有调用ReleaseDC导致的。
如果写成CClientDC dc(this),那么MFC自己封装了析构函数。所以要切记调用API的一些问题。 ......

阅读全文(2076) | 评论:0

从DLL 中生成LIB文件(2012-09-10 14:16:00)

摘要: 把sqlite3.dll 和sqlite3.def 文件放在同一个文件夹中,然后启动“Visual Studio命令提示” 将工作目录切换到文件夹中: lib /def:sqlite3.def /MACHINE:x86 生成sqlite3.lib 文件。   ......

阅读全文(1491) | 评论:0

CFileDialog多选的最大文件数(2011-08-01 16:34:00)

摘要:         近期因为建库,需要一次性提交很多文件,经常发现list最终显示不完整,只有一部分。想想应该是与CFileDialog支持多选的最大文件数有关。             我们知道,使用CFileDialog类并设置OFN_ALLOWMULTISELECT标志时,OPENFILENAME结构体的lpstrFile成员是一个指向用户申请分配的缓冲区,里面接受所选的路径和文件名列表,这个列表的每一项由一个NULL隔开,最末以两个NULL结束。nMaxFile成员指明了缓冲区的大小,如果所选择的文件名的加起来的总长度超过了此值,则DoModal函数返回IDCANCEL,如果用户试图打开超过缓冲区大小的文件集的话,CommDlgExtendedError()将返回FNERR_BUFFERTOOSMALL,这时候,lpstrFile的前两个byte将会包含实际需要的缓冲区的字节数。         再来看看MSDN上的CFileDialog中变量的解释吧:     nMaxFile: 说明 lpstrFile 缓冲指针的大小,以 TCHARs 为单位。对于 ANSI 版本,它指的是字节数。对于 Unicode 版本,它指的是字符数,该缓冲必须足够大才能存储文件的路径字符串,包括结尾空字符。如果 该缓存太小以至于无法包含文件信息,那么 GetOpenFileName 和 GetSaveFileName 函数将返回 FALSE。该缓冲至少要求容纳 256 个字符。       如此,只要我们设置足够大的缓冲空间就可以了,看看自己的代码,都是简单设置为40000,不具有通用性,因为每个文件的路径名可能长度都不同,看网友比较好的方法是这样的:(假设每个文件名支持的最大的路径名是255)可以一眼看出最多支持选择2000个文件;    CFileDialog dlg(TRUE, N......

阅读全文(4707) | 评论:0

类静态成员变量的导出问题(续)(2010-12-31 15:17:00)

摘要: 近日,在Standard Dll导出静态变量,又出现了一些疑惑,不过还是解决了。    显然不用AFX_EXT_CLASS了。根据本博客上篇文章《类静态成员变量的导出问题》的解决办法,均使用__declspec(dllexport)导出,然后在使用的地方导入。但是还是发现有些繁琐的细节需要注意。    举例来说。A类中含有静态成员变量,在ADll中也是导出的。在BDll中的B类cpp中需要使用A类对象,显然在cpp中直接包含了A的头文件。B类自身也是需要导出的。    由于BDll的所有类都是要导出的,所以为了方便,直接在整个环境(一般在Setting里面)的预编译标志里面加入了导出标志。这样一来,错误就发生了。只要包含了A的头文件就会报链接错误,说找不到那个静态成员变量。实际上,只需要在包含A的头文件之前,再次定义(#define)一个导入标志即可。这里不要使用#undef,因为#undef是会取消宏定义。而再次重新定义,即使用#define,其作用域是从定义的起始地方直到程序末尾(或者#undef取消),如果碰到下一个#define会直接覆盖。虽然我们在这个B的cpp里面,临时定义了一下导入标志,但是在到了一个新的编译单元的时候,因为有全局环境的宏定义,这样问题就解决了。
另外推荐阅读一篇文章,作者写的不错: =============================================================== =========   在DLL编写时,使用__declspec(dllexport)的作用,它就是为了省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类。但是,MSDN文档里面,对于__declspec(dllimport)的说明让人感觉有点奇怪,先来看看MSDN里面是怎么说的:   “不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定......

阅读全文(6012) | 评论:0

AfxGetResourceHandle失败原因(2010-08-15 10:04:00)

摘要:我们都知道在MFC扩展库中创建对话框资源并显示出来,必须用到AfxGetResourceHandle与AfxSetResourceHandle两个函数(具体原因不再详述)。但是前不久在项目中竟然发现这样设置以后,对话框弹出仍然报错。调试发现,竟然是AfxGetReourceHandle失败,让人顿生奇怪。为什么会失败呢?   经过多方查证,终于定位原因是我新建的MFC扩展库是Unicode类型从而导致失败的。不过为什么会这样,只能留到以后去查证了。另外,如果你发现使用CString的时候出现了一大堆编译错误,多半也是这个原因,小心小心。   ......

阅读全文(7341) | 评论:3

激活另一个进程的窗口(转载)(2010-04-05 22:10:00)

摘要:问题: 我要在我的系统(假如当前窗口是A)里面用vba   的方法打开word的窗口(B窗口),但是打开后发现Word窗口不是活动的,A窗口依然是活动窗口,但是发现B窗口已经在任务栏上打开了,有时候即使能显示B窗口,但是你只要按键盘,发现活动窗口立即转到了A窗口上,让我无可奈何。   
      于是我通过FindWindow的方法来获取当前打开的B窗口的句柄(能够正确获取到),但是后面无论用SetActiveWindow、SetFocus、ShowWindow等方法来设置把B窗口设置为活动窗口,但是没有一个能行的,请大家教教我。   解答: 光找到WORD的主窗口不行,还要处理其子窗口。可以这样做:   
    
  CWnd   *pMainWnd,*pSubWnd;   
  pMainWnd=CWnd::FindWindow(_T("WORD窗口类型"),"WORD窗口名");   
  if   (pMainWnd)   
  {             pSubWnd=pMainWnd->GetLastActivePopup();   
      pMainWnd->ShowWindow(SW_SHOW);   
      pSubWnd->SetForegroundWindow();   
   }   ......

阅读全文(4366) | 评论:0

关于在OnTimer中连续弹出对话框的讨论(2009-02-05 22:46:00)

摘要: CxxxDlg::OnTimer(UINT nIDEvent)
{
  static int i = 0,j;
  j = i++;
  if (i==2) KillTimer(nIDEvent);
  MessageBox("!");
  i++;
  CString str;
  str.Format("%d,%d ",j,i);
  ::OutputDebugString(str);
}           以上代码在执行的时候,会弹出2个messagebox。很多人都有1个疑问,那就是当第一个消息框弹出的时候,应该相当于是模态对话框,怎么还会第二次进入OnTimer,并且再次弹出messagebox?     首先,让我们来了解一下计时器的原理。计时器可以响应事件,而用户界面也可以响应。但是程序却只有一个线程。CPU不得不分配一个时间片给应用程序专门处理定时器事件,应用程序不能在外面操作定时器。因为定时器处理是由CPU统一处理。      让我们再来看看常见模态对话框的domodal到底干了些什么。MSDN解释如下: The   function   displays   the   dialog   box   ,   disables   the   owner   window,   and   starts   its   own   message   loop   to   retrieve   and   dispatch   ......

阅读全文(3911) | 评论:0

消息反射中的难点掌握(2009-01-14 20:15:00)

摘要:在上一篇中已经提到: 在老版本的MFC中,对一些消息采用了虚函数机制,例如:WM_DRAWITEM,这样子控件就有机会控制自己的动作,代码的可重用性有了一定的提高,但是这还没有达到大部分人的要求,所以在高版本的MFC中,提出了一种更方便的机制:消息反射。通过消息反射机制,子控件窗口便能够自行处理与自身相关的一些消息,增强了封装性,同时也提高了子控件窗口类的可重用性。   通过使用消息反射宏,在调用ReflectChildNotify时,会对不同的消息有不同的处理。   例如,对于WM_CTLCOLOR,如果在父窗口中已经实现,又在子控件中实现,那么只会响应父窗口的。   而对于WM_NOTIFY,则恰恰相反,子窗口会首先处理消息,切记切记。    ......

阅读全文(2033) | 评论:0

分析与理解MFC消息反射机制(2009-01-14 17:49:00)

摘要: [转载] 分析与理解MFC消息反射机制收藏 前言:

我曾写过一篇文章对通知消息WM_NOTIFY进行分析,消息反射是MFC中对通知消息的处理方式,两者之间关系十分紧密,因此,我写了这篇文章,希望能够描绘出通知消息的完整印象。

消息反射的基础知识

1、消息反射解释:
  父窗口将控制子窗口发给它的通知消息,首先反射回子窗口进行处理(即给控制子窗口一个机会,让控制子窗口处理此消息),这样通知消息就有机会能被子窗口自身进行处理。

2、MFC中引入消息反射的原因:
  在Windows的消息处理中,控制子窗口的发给其父窗口的通知消息只能由其父窗口进行处理,这使得控制子窗口的自身能动性大大降低(你想,它连改变自己的背景色,处理一个自身滚动问题都要其父窗口来完成),为了解决这个问题,在MFC中引入了反射消息“Reflect Message”的概念,进行消息反射,可以使得控制子窗口能够自行处理与自身相关的一些消息,增强了封装性,从而提高了控制子窗口的可重用性。

消息反射的处理流程(不考虑OLE控制)

一、消息反射处理流程图:
  1、父窗口收到控制子窗口发来的通知消息后,调用它的虚函数CWnd::OnNotify. CWnd::OnNotify()
...{
    // 主体部分:
    if (ReflectLastMsg(hWndCtrl, pResult))    // 此时,hWndCtrl,为发送窗口,即子窗口的窗口句柄
        return TRUE;                        &nbs......

阅读全文(3803) | 评论:1