分类: C/C++
2008-03-12 19:44:19
下载源代码: (163KB) Mark Simpson 如果没读过 2003 四月的专栏文章,我在这里简单介绍一下 CWebVersion 类,它是我编写的一个用来比较程序版本号的类,版本号文件存储在 Web 上。我在 TraceWin 程序中就是使用这个类来通知用户何时有新版本下载的。 没错,你可以用 HTTP;但不必转换文件。在我原来的实现中确实应该使用 HTTP,因为 HTTP 比 FTP 使用的更加广泛。许多 Web 服务提供商出于安全的原因都不允许匿名的 FTP 访问,但对于文件传输来说,FTP 效率更高(这也是我用 FTP 的原因),HTTP 对于获取简单的文本文件不错。 CWebVersion 读取文本文件,文件中的版本数据用逗号分割成四部分:高/低位的主/次版本号。使用方法是这样的: if (CWebVersion::Online()) { CWebVersion webver(""); if (webver.ReadVersion("myversion.txt")) { // dwVersionMS and dwVersionLS now // hold the version numbers } } 静态成员函数 CWebVersion::Online 调用 ::InternetQueryOption, 用 INTERNET_OPTION_CONNECTED_STATE 作为参数,以便检查此电脑是否连接到 Internet。如果已经连接,那么 CWebVersion::ReadVersion 便从你的 Web 网站读取版本文件。接着你可以将读取到的版本号与应用程序中编译的版本号进行比较,这个版本号通常在 VERSIONINFO 或 DllGetVersion 资源中(详情参见:“如何获取某个动态链接库的版本信息”)。原来的 CWebVersion 使用 FTP 来获取文件;本文我改为使用 HTTP 来处理。使用 MFC 的 Wininet 类,在 Web 上通过 HTTP 读取文件很容易: // in CWebVersion::ReadVersion CInternetSession session(_T("MySession")); CHttpConnection* pConn = session.GetHttpConnection("",INTERNET_DEFAULT_HTTP_PORT); CHttpFile* pFile = pConn->OpenRequest(CHttpConnection::HTTP_VERB_GET, "TraceWinVer.txt"); pFile->SendRequest(); 上面的代码意图是想下载文件 /TraceWinVer.txt。 在调用了 SendRequest 之后,你可以调用 CHttpFile::QueryInfoStatusCode 来获取状态吗——例如,文件没找到的状态码是 404,200 表示成功(完整的状态码列表参见 wininet.h 头文件)——接着调用 CHttpFile::Read 将文件读入你的缓冲,这个工作由 CWebVersion::ReadVersion 完成,然后调用 scanf ,根据 “Mhi,Mlo,mhi,mlo” 格式解析文件内容,此处 Mhi,Mlo,mhi,mlo 分别代表主版本和次版本号的高位和低位字(WORDs)。CWebVersion 将这些信息保存在 CWebVersion::dwMajorVersion 和 CWebVersion::dwMinorVersion 中。完整的代码参见 Figure 1。 static LPCTSTR MyHeaders = _T("Accept: text/dat\r\n"); ... pHttpFile->AddRequestHeaders(MyHeaders);
HttpWebRequest* req = dynamic_cast 这里 dynamic_cast 必须使用 HTTP 专用的属性和方法 HttpWebRequest 和 HttpWebResponse。如果你使用 Visual Studio 2005 中所带的 C++/CLI,那么用(^)(tracking handles)代替指针,并且不必在处理托管串文字量是使用 S。在 .NET 中,如果要读取文件,先在响应流中创建一个 StreamReader,然后再读取它的内容,就像下面这样: StreamReader* strm = new StreamReader(resp->GetResponseStream(), encoding); String* content = strm->ReadToEnd(); strm->Close(); 我为 .NET 开发人员写了一个完整的 GetVersion 托管 C++ 程序,代码都在本文附带的源代码下载文件中。 如何在基于 MFC 的应用程序中添加声音效果(不仅仅是用 MessageBeep 函数发出的蜂鸣声)? Alexander Potapenko 将声音添加到基于 MFC 的应用程序并不难,但在我讲解如何做之前,得先提醒你,沉默是金,在软件中尤其如此。虽然很多地方都可以用声音(如生成失败、e-mail到达以及该购买杂物了),在大多数情况下,最好保持安静。 从设计上来说,软件里添加声音纯属没事找事,不知你访问过那种一打开主页就播放叮当声的网站没有?大多数人的第一反应是按“返回”按钮。如果你必须添加声音,请不要忘了在程序的“工具|选项”菜单中提供一个“静音”选项设置。 OK,现在言规正传,Windows 中有一个函数叫 PlaySound 可以做你想要做的事情。这个函数的定义在 mmsystem.h 头文件中,你必须与winmm.lib 链接。PlaySound 播放声音,它的参数之一是声音文件名或者资源名。下面是一个调用例子: PlaySound("woofwoof.wav",NULL,SND_NODEFAULT); 这里的专用标志 SND_NODEFAULT 告诉 Windows:如果找不到声音文件的话,不要播放默认的声音(MessageBeep)。其它标志参见 Figure 3。Windows 的函数众多,使用 PlaySound 的方式也多种多样,很多都没有文档可查。一些标志我一直也很迷惑。不过不要怕,我会解开这些迷。 PlaySound("AppExit",NULL, SND_APPLICATION|SND_NODEFAULT);
AppExit WAVE "res\\STExit.wav" 注意资源必须是一个 WAVE 文件——这个重要的细节到目前为止微软没有在文档中说明。资源编译器会将WAV文件嵌入EXE可执行文件,这样你就可以像下面这样用 SND_RESOURCE 标志播放它了: PlaySound("AppExit", AfxGetResourceHandle(), SND_RESOURCE); PlaySound 需要包含资源的模块句柄,这个模块句柄可以通过调用 AfxGetResourceHandle (在大多数应用程序中,它获得的结果与 AfxGetInstanceHandle 相同)来获得。在前面的代码段中,资源标示符是一个字符串(“AppExit”),但如果你指定了 SND_ALIAS_ID 标志,也可以用一个整数 ID。
SoundTest 定义声音的第一步是创建声音的 IDs。我用了一个枚举类型来处理五个声音:MYSND_HAPPY,MYSND_UNHAPPY 等等。注意不要使用 0 作为声音的 ID;因为 CSoundMgr::PlaySound(0) 停止播放当前的声音,作用相当于 ::PlaySound(NULL, NULL, 0)。定义了 IDs 之后,你便可以使用在 SoundMgr.h 文件中定义宏来建立声音映射表,就像下面这样: BEGIN_SOUND_MAP(MySounds) DEFINE_SOUND(MYSND_HAPPY, _T("ST_Happy"), _T("SoundTest Happy")) ... END_SOUND_MAP() 表的每一行都有三项:ID,逻辑名和 GUI界面名。ID 用于播放声音,逻辑名(例如:ST_Happy)给注册表键值以及默认的声音资源内部使用。GUI界面名是显示在用户界面上给用户看的,当用户使用控制面板中的声音配置小程序时,用户看到的就是 GUI界面名——例如,Figure 4 中显示的“SoundTest Happy”。表一旦定义好,下一步是创建一个 CSoundMgr 实例,用这个表的值来初始化这个实例: // 该程序的声音管理器 CSoundMgr SoundMgr(MySounds); 整个程序只需要一个 CSoundMgr 即可。最后,如果你想将默认的声音内嵌在程序中,那么你必须添加相应的 WAVE 资源,每个逻辑声音名对应一个 WAVE 文件。例如: ST_HAPPY WAVE "res\\STHappy.wav" ST_UNHAPPY WAVE "res\\STUnhappy.wav" 现在声音都定义好了,要播放声音只需用下面的方法即可: SoundMgr.PlaySound(MYSND_HAPPY) 此处程序播放声音 MYSND_HAPPY。大概过程是这样的恶:CSoundMgr 首先查找注册表键/值,CSoundMgr::PlaySound 播放 HKCU\AppEvents\Schemes\Apps\SoundTest\ST_Happy\.current 如果这个键/值不存在,你可以调用 CSoundMgr::IsRegistered 检查你的声音是否被注册过——如果没有,调用 CSoundMgr::Register 注册它们: if (!SoundMgr.IsRegistered()) SoundMgr.Register(); CSoundMgr::Register 创建所有需要的注册键,以便用户在控制面板中定制声音。此时它实际上不需要给注册键任何赋值,让它们为空,以便 CSoundMgr::PlaySound 使用默认的声音资源。如果你不想使用默认的声音,那就不要为它们创建资源或者用下面的方法屏蔽: CSoundMgr::m_bUseResourceSounds = FALSE; CSoundMgr 的实现很简单。大多数代码都是在处理注册表的存取,它也许是 Windows 编程中最繁琐的事情之一了(具体细节参见 Figure 6)。好在我把很多繁琐的工作都完成了。每个逻辑声音的子键位于 HKCU\AppEvents\Schemes\Apps\progname,此处 progname 是你的程序名,这个名字与程序“Settings”中使用的字符串名相同,也就是::AfxGetAppName 返回的值。 每个子键在 .current 中保存声音文件名。CSoundMgr 并不创建这个.current,它只建立空键,因为当用户改变声音时,控制面板中的声音配置程序创建 .current。每个逻辑声音键的缺省值是声音人类可读的 GUI 名,但据我所知 Windows 忽略这个值;它在一个别的键中查找 GUI 名:HKCU\AppEvents\EventNames。详情请见代码说明。 |
作者简介 Paul DiLascia 是一名自由作家,软件咨询顾问以及大型 Web/UI 的设计师。他是《Writing Reusable Windows Code in C++》书(Addison-Wesley, 1992)的作者。业余时间他开发 PixeLib,这是一个 MFC 类库,从 Paul 的网站 http:// 可以获得这个类库。 . |
本文出自 的 期刊,可通过当地报摊获得,或者最好是 |