1/22/2005

限制应用程序的实例数目

某些应用程序处理紧缺资源,例如可擦写光驱、串口或者大量内存,通常不希望这种应用程序的多个实例同时运行。

实际上你没有办法限制用户只能启动一次。你可以做到的是在应用程序启动之后查找是否用户启动了另一个实例。如果没有找到现存的实例,应用程序以正常方式启动。否则,通常的处理是退出。

要查找现存的实例,可以查找进程列表,比较每一个进程的文件名称和本应用程序的文件名称,如果找到了和文件名匹配的进程,那么认为应用程序已经启动了(参考平台SDK的Spy示例和Visual C++的TList示例)。这种方法的缺点是需要额外的工作(例如枚举找到的进程的窗口)来传递命令行参数。关于如何枚举一个进程或者线程的窗口,可以参考GetWindowThreadProcessId。也可以用约定的暗号来识别,例如创建一个对象,具有很难重复的属性,然后查找系统中是否已经有了同样属性的对象。通常创建互斥体(MUTEX)或者具有指定窗口类的窗口(不适用于对话框,因为对话框的窗口类是#32770)。一些其他的方法包括检查窗口过程处理特定消息的返回值、检查窗口的约定属性等等。建议用互斥体对创建对象的代码加锁,以避免同时创建两个对象。

有的程序在退出之前需要把命令行参数传递给现存的实例,例如一些文档处理程序和浏览器。官方的方法是在注册表中对应的文件类型中注册DDL命令:HKCR/filetype/shell/open/ddeexec = Open("%1")。 遗憾的是,MFC只在CFrameWnd类里面实现了对DDE的封装。如果你的程序不基于文档视图框架,那么实现DDE支持是很麻烦的事情。幸好你不必这么做,比较简单的方法是发送一个消息——例如WM_COPYDATA——到找到的现存实例窗口,传递命令行参数作为参数。

有的程序,例如IE或者资源管理器,Office等,使用多顶层窗口来替代应用程序的多个实例。尽管MFC7的应用程序向导提供了多顶层窗口的选项,但是这样的实现是不完美的——所有顶层窗口仍然在同一个线程内,使得每个顶层窗口没有单独的状态。举例来说,如果一个浏览器窗口显示了一个消息框,那么其他顶层窗口也会挂起。使用腾讯浏览器的人可能对这种情况很是熟悉。

MFC示例MTMDI演示了如何在MDI程序中在单独的线程中创建窗口,以及相应修改消息和命令传递过程。虽然这样应用程序改动最少,但是这打破了DOC/View架构,而且顶层窗口还是在一个线程中。
用对话框作为顶层窗口是最简单的,在单独的线程中创建对话框也很容易,但是在对话框上创建工具栏要编写一些额外的代码来模仿框架的行为,而且没有文档视图的支持,需要自己编写打开文档和网址的代码。还有一些其它的副作用,例如需要在对话框上实现工具栏、状态栏菜单的自动更新,不能通过自定义窗口类的办法查找现有的应用程序实例等等。

编写多顶层窗口文档处理程序时,在现存实例运行时,在资源管理器打开新的文件的实现是,新的实例发送命令参数到现存实例,创建一个新的顶层窗口之后退出。有很多种方法获得这个参数。MFC程序当然也是C++程序,所以标准C的argc和argv主函数入口参数仍然有效。但是MFC提供了命令行处理的封装类CCommandLineInfo,支持了标准的命令行开关的分析。但是更有效地处理命令行参数的办法还是采用MSJ1999年10月中的C++Q&A专栏的CCommandLineInfoEx类。它提供了自定义命令行开关分析的方法。

编程控制活动桌面,用ActiveX控件来增强桌面的功能

活动桌面处理和一个例子http://www.vckbase.com/vckbase/vckbase10/vc/nonctrls/atlcomocx_02/1002001.htm)讲述了使用IActiveDesktop接口可以做到的事情。活动桌面允许在桌面上显示HTML网页,这也意味着我们可以在桌面上的项目中以在网页中使用ActiveX控件来对网页进行扩展的方式来提供丰富的内容。但是不建议在桌面上使用不安全的控件,例如Windows Media Player 6.0。虽然用一段影片作为桌面背景是一件很酷的事情,但是你不得不忍受在每次切换任务或者刷新桌面的时候都弹出安全性提示对话框。你可以在编写自己的控件时利用
http://msdn.microsoft.com/library/CHS/vccore/html/_atl_step_7.3a_.putting_the_control_on_a_web_page.asp中的技术来避免这个烦人的对话框。

12/31/2004

How To Detect If an Application Has Stopped Responding

简介:本文描述了如何使用C++、VB、Windows API和.Net类库判断一个进程是否停止了响应。

没有一个明确的“停止响应”的定义,例如对于Internet Explorer或者Word 2000这样的多顶层窗口应用程序,可能存在部分顶层窗口失去响应的情况,这时很难定义应用程序是否停止了响应。但是一般来说,很多应用程序只有一个标志性窗口(或者叫主窗口)。如果主窗口在一段时间内不响应用户操作的时候,对于用户来说应用程序是停止响应的(例如在Internet Explorer等待远程FTP服务器返回登录结果时)。尽管这经常属于其他应用程序应该妥善考虑的范畴,但是如果自己的应用程序依赖于这样的程序而没有源代码级控制权,那么应该提供一个机会允许用户中断对外部应用程序的等待或者干脆终止外部应用程序。

Windows2000及Windows XP中提供了一个API IsHungAppWindow,但是微软声明此函数可能在后续的Windows版本中删除这个函数。不用遗憾,在Windows 32中有一个特殊的消息WM_NULL,在其文档中说明它可以和SendMessageTimeout一起用于判断某个窗口是否停止了响应。一般来说,我们需要获得进程的标志性窗口(或者叫主窗口)来执行这个判断。2002年7月的MSDN杂志Q&A专栏文章Get the Main Window, Get EXE Name 说明了如何做到这一点。很遗憾,附录中的文章How To Detect If an Application Has Stopped Responding中通过访问浏览器对象的hwnd属性绕过了这个过程,或许是因为在VB中调用EnumWindow比较繁琐;参见附录中的Enumerating Windows Using API(VB)。

另外,.Net Framework中提供了一个Responding属性,可以用于检测进程是否停止了响应。附录中的两篇基于.Net的文章就是检查了这个属性。看起来这似乎是通过检查进程的MainWindowHandle指明的窗口是否立刻响应来解决的。通过这种方法判断的缺点是无法设置等待超时的时间。

参考资料
IsHungAppWindow(MSDN)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/ishungappwindow.asp
WM_NULL(MSDN)
http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowmessages/wm_null.asp
Process.Responding Property (MSDN)
http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemDiagnosticsProcessClassRespondingTopic.asp
如何“干净地”终止 Win32 中的应用程序
http://support.microsoft.com/kb/178893/zh-cn
How To Detect If an Application Has Stopped Responding
http://support.microsoft.com/kb/231844
304991 Detect if an Application Has Stopped Responding by Using C#
http://support.microsoft.com/kb/304991
304990 Detect if an Application has Stopped Responding by Using Visual Basic .NET
http://support.microsoft.com/kb/304990
Get the Main Window, Get EXE Name
 http://msdn.microsoft.com/msdnmag/issues/02/07/CQA/
Enumerating Windows Using API(VB)
http://www.vbaccelerator.com/home/VB/Code/Libraries/Windows/Enumerating_Windows/article.asp

12/15/2004

在richedit控件中插入动态GIF

这是CSDN的VC论坛上的一个老FAQ了。我在写自定义在RichEdit中插入对象的图标(http://www.blogcn.com/user3/jiangsheng/blog/1319738.html)这片文章的时候就是想用这个技术做动画GIF的,但是怎么判断一个内嵌在RichEdit的对象是GIF这个问题一直没有解决。好在QQ附带的一个控件支持动画GIF,可以插入这个对象来解决问题。

首先需要一个定时器来定时更新GIF。

public: System::Void OnLoad(System::Object^  sender, System::EventArgs^  e)
   {
   this->typingRichTextBox->RichTextShortcutsEnabled=false;
   this->timer1->Start();
   }

private: System::Void OnFormClosing(System::Object^  sender, System::Windows::Forms::FormClosingEventArgs^  e)
   {
    this->timer1->Stop();
    this->frameClosing=true;
   }

然后在定时器的处理函数里面通知GIF控件更新显示。

private: System::Void OnTimer1Elapsed(System::Object^  sender, System::Timers::ElapsedEventArgs^  e)
   {
    if(this->frameClosing==false)
    UnmanagedGifTriggerFrameChange(this->contentRichTextBox->Handle.ToInt32());
   }

最后的工作就是插入GIF了

private: System::Void smileToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e)
   {
    insertemotion(sender,e,"c:\\Program Files\\Tencent\\QQ\\Face\\20.gif");
   }

private: System::Void insertemotion(System::Object^  sender, System::EventArgs^  e,String ^ gifPath)
   {
   System::Windows::Forms::RichTextBox^  ptypingRichTextBox=this->typingRichTextBox;
   stdcli::language::pin_ptr< wchar_t> wch = PtrToStringChars(gifPath);
   UnmanagedInsertGif(ptypingRichTextBox->Handle.ToInt32(),wch );
   }

为了偷懒起见关键的代码还是用Native C++来写,所以这些函数名全部以Unmanaged开头。

//unmanagedwin32.h
//混合托管和非托管编程的话,编译的时候不能使用/clr:safe和/clr:pure开关,
#pragma once
#pragma unmanaged//用这个开关来切换托管和非托管代码
extern void UnmanagedScrollToButton(int hwndRichEdit);
extern void UnmanagedInsertGif(int hwndRichEdit,wchar_t * pFilePath);
extern void UnmanagedGifTriggerFrameChange(int hwndRichEdit);
#pragma managed

//unmanagedwin32.cc

//使用了Windows 平台SDK
//必须要使用clr:oldSyntax来避免SDK头文件和C++/CLI语法的冲突
//而且在Visual C++ 2005 Express中要添加/d1PrivateNativeTypes 开关以避免混合LNK2022错误。
#define UNICODE
#define _UNICODE
#define  _WIN32_DCOM
#include "windows.h"
#include "Richedit.h"
#include "Richole.h"
#pragma comment( lib, "User32.lib" )
#include "UnmanagedWin32.h"
//纯用C编写自动化操作会死人的,幸好可以自动导入
#import "c:\\Program files\\tencent\\qq\\ImageOle.dll" named_guids
//移动光标到末尾然后调用System::Windows::Forms::RichTextBox的ScrollToCaret方法,需要拖动滚动条才可以看到最后一行之前的文字。老办法,API伺候。
void UnmanagedScrollToButton(int hwndRichEdit)
{
 HWND h=(HWND)hwndRichEdit;
 int line = SendMessage(h, EM_GETFIRSTVISIBLELINE, 0, 0);
 int linecount = SendMessage(h, EM_GETLINECOUNT, 0, 0);
 SendMessage(h, EM_LINESCROLL, 0, (linecount - line - 2));
}

下面的函数UnmanagedInsertGif(int hwndRichEdit,wchar_t * pFilePath)是插入一个ImageOle::GifAnimator对象,UnmanagedGifTriggerFrameChange(int hwndRichEdit)是枚举richedit中已经插入的对象,如果是ImageOle::GifAnimator对象,那么调用其TriggerFrameChange方法。

这两个函数里面的方法也可以用于插入其他类型控件,以及和插入的对象通讯。

使用windows 2000,QQ2004SP1,Visual C++ 2005 Express, Platform SDK (Windows 2003)编译测试。

11/07/2004

偶的CSDN收藏夹(大部分都是古董……)

无界面的HTML分析器·分析网页中的表格/ http://community.csdn.net/Expert/topicview.asp?id=351580
如何载入非标准大小的图标并显示/ http://community.csdn.net/Expert/topicview.asp?id=638695
在基于浏览器控件的程序中,当鼠标停留在一个超链上时,怎样得到该链指向的地址/ http://community.csdn.net/Expert/topicview.asp?id=653919
如何等待创建的子进程结束?/ http://community.csdn.net/Expert/topicview.asp?id=761987
CFileDialog如何打开多个文件/ http://community.csdn.net/Expert/topicview.asp?id=781609
保存指定的URL的网页到单一mht格式的文件/ http://community.csdn.net/Expert/topicview.asp?id=786376
DataGrid控件问题如何修改布尔型变量的显示/ http://community.csdn.net/Expert/topicview.asp?id=776613
如何创建BHO(浏览器辅助对象)/ http://community.csdn.net/Expert/topicview.asp?id=689265
如何使用IE的文件下载对话框下载指定URL的文件/ http://community.csdn.net/Expert/topicview.asp?id=773143
如何自动填写普通表单和浏览器中的表单/ http://community.csdn.net/Expert/topicview.asp?id=564348
如何得到用ShellExecuteEx()运行的程序的进程和线程ID/ http://community.csdn.net/Expert/topicview.asp?id=795946
如何调用WinHTTP函数自动提交HTML表单http://community.csdn.net/Expert/topicview.asp?id=796045
如何访问浏览器控件中的框架的文档/ http://community.csdn.net/Expert/topicview.asp?id=800217
如何访问浏览器控件中显示的XML文档的XML数据/ http://community.csdn.net/Expert/topicview.asp?id=783799
编程创建只读共享目录/ http://community.csdn.net/Expert/topicview.asp?id=772381
查询图像上与给定位图匹配的部分/ http://community.csdn.net/Expert/topicview.asp?id=806545
用ADO枚举数据库里面的系统对象/ http://community.csdn.net/Expert/topicview.asp?id=839972
调用系统的数据连接向导/ http://community.csdn.net/Expert/topicview.asp?id=858179
访问和修改IE的历史记录/ http://community.csdn.net/Expert/topicview.asp?id=844329
获得机器的详细硬件信息/ http://community.csdn.net/Expert/topicview.asp?id=867765
CoCreateInstance不识别CLSID_DOMDocument(只适用于XML3.0版本)/ http://community.csdn.net/Expert/topicview.asp?id=869616
编程禁止局域网内的计算机连接到Internet/ http://community.csdn.net/Expert/topicview.asp?id=706560 http://community.csdn.net/Expert/topicview.asp?id=866712
Win2000下自动关机 / http://community.csdn.net/Expert/topicview.asp?id=883036
从获得的IHtmlDocument2的接口中进一步获得flash的地址 http://dev.csdn.net/article/14/14752.shtm http://community.csdn.net/Expert/topicview.asp?id=934618
EXE文件运行时如何删除它自身 http://community.csdn.net/Expert/topicview.asp?id=737798
你第一次是怎么知道csdn的 http://community.csdn.net/Expert/topicview.asp?id=647904
如何在任务管理器中隐藏自己程序的进程 http://community.csdn.net/Expert/topicview.asp?id=1107094
如何禁用power键?/ http://community.csdn.net/Expert/topicview.asp?id=1139439
在NT/2000中怎么禁用Ctrl+Alt+Delete?(不能用gina,键盘驱动)/ http://dev.csdn.net/article/15/15645.shtm http://community.csdn.net/Expert/topicview.asp?id=1113085
如何禁用/启用网卡 / http://community.csdn.net/Expert/topicview.asp?id=1121940
xml数据文件如何在vc中用schema进行校验 / http://community.csdn.net/Expert/topicview.asp?id=2146670
为MFC 和 ATL控件创建签署的CAB文件 / http://community.csdn.net/Expert/topicview.asp?id=2282814
DirectShow: 对asf文件进行加密,能不能写一个source filter进行动态解密? http://community.csdn.net/Expert/topicview.asp?id=1600048
DirectShow 视频采集例子中如何用代码调节亮度 / http://community.csdn.net/Expert/topicview.asp?id=2333385
DLL初始化时能否使用线程/ http://community.csdn.net/Expert/topicview.asp?id=3286367
如何编写ActiveX控件将Word嵌入到网页/ http://community.csdn.net/Expert/topicview.asp?id=3389913
VB问题集锦及编程技巧/ http://community.csdn.net/Expert/topicview.asp?id=3408603