分享
 
 
 

改变窗口中的光标形状

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

C++ Q&A...

在注册表中查找默认浏览器信息

改变窗口中的光标形状

避免资源ID冲突

原著:Paul DiLascia

翻译:曾国

原文出处:January 2001 issue of MSDN Magazine

原代码下载:CQA0101.exe(47KB)

如何在注册表中查找默认浏览器信息

如何改变窗口中的光标形状

如何避免资源ID冲突

如何在注册表中查找默认浏览器位置的定义?我需要知道EXE文件的路径和名称以便启动一个应用程序会话。我的目的很简单,就是打开默认的浏览器,这样用户能够象普通程序一样使用它,而不是在我设计的程序窗口内浏览因特网。

Rolf Wenger

据我所知,在Window中没有专门指定默认浏览器的注册表键值或设定值。即使专家也很难弄清楚整个注册表,更何况常人。我知道可能存在这样一个键值,

HKCU\System\Mumble\Bletch\Blah\Gak\DefaultBrowser

如果你知道这样的键值,请写信告诉我。不过,我知道一个简单的解决办法,那就是查找哪个程序和HTML文件相关联。在Window操作系统中HTML文件的后缀通常为.htm和.html,所以你要做的就是查找HKCR/.htm的键值。如果你查找了会找到下面的键值:

HKEY_CLASSES_ROOT\.htm="htmlfile"

再根据这个键值查找HKCR/htmlfilm的条目,你会找到下面的键值:

[HKEY_CLASSES_ROOT\htmlfile\shell\open\command]

@="\"C:\\PROGRA~1\\INTERN~1\\iexplore.exe\" -nohome"

这个键值表明Microsoft Internet Explorer (iexplore.exe)是用来打开.htm文件的程序。(-nohome开关标志告诉IE浏览器不要打开主页)如果默认的浏览器是Netscape,这个条目会是这样:

[HKEY_CLASSES_ROOT\htmlfile\shell\open\command]

@="\"C:\\PROGRA~1\\NETSCAPE\\netscape.exe\".

我的回答满意吗?

我想为对话框的一个按钮设置不同的光标,应该如何进行设置?

Rolf Wenger

有两种方式可以改变窗口中的光标:你可以在注册窗口类时声明一个全局光标(HCURSOR)作为WNDCLASS结构的一部分,或者通过处理WM_SETCURSOR消息来手工设置光标。标准的MFC程序采用第一种方式设置光标,它自动为主窗口注册一个箭头光标。你可以在主窗口或子窗口中通过处理WM_SETCURSOR消息来改写这个行为。

// 在按钮类中处理WM_SETCURSOR消息

BOOL CMyButton::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg)

{

::SetCursor(m_hMyCursor);

return TRUE;

}

不管什么时候,当用户将鼠标移动到按钮上并且鼠标没有被捕获时,Windows 会发送一条WM_SETCURSOR消息给按钮。它传递一个窗口句柄——即鼠标指针指向的窗口,

此时就是按钮本身;击中测试码——即在WM_NCHITTEST消息中使用的HTXXX码(见

Figure 1);和一个触发事件的消息ID,比如说它触发了WM_MOUSEMOVE事件。设置鼠标

光标的最佳机会就是在处理WM_SETCURSOR消息的时候。如果要这么做,你必须返回TRUE以阻止窗口默认的处理过程。

此时处理会如何进行呢?首先窗口默认的处理过程向父窗口(如果有的话)发送WM_SETCURSOR消息到父窗口。如果父窗口处理了WM_SETCURSOR消息(就是说它返回了TURE),Windows就不做什么了,

该消息就算处理完了。如果父窗口没有处理WM_SETCURSOR消息(返回FALSE),Windows就给子窗口一个处理这条消息的机会。假如子窗口也没有处理该消息(返回FALSE),Windows就使用全局光标,要是连全局光标也没有,则使用箭头光标。

这些意味着什么?这意味着在需要动态设置光标时,你要决定是在子窗口还是在父窗口处理WM_SETCURSOR消息。两个选择都可行,这取决于实际情况。一般来说,最好让对象决定自己的属性,这就是说最好在子窗口处理消息。本例中子窗口是指按钮。但这需要从CButton类继承一个新的按钮类,让它有自己的消息映射和其

它一些必要的属性,如果你是乐于使用 Class

Wizard 的人(有没有人用它啊?),这意味着需要多敲几下键盘或多点几次鼠标。如果你已经具备了自己的按钮类,那我明确的告诉你在这个子类中处理WM_SETCURSOR消息。要是你没有自己的按钮类而且你

又是个懒人,那就在对话框里处理WM_SETCURSOR消息也行。不过千万不要告诉别的面向对象专家,是我要你这么干的!

Figure 2 按钮上设置的光标

我写了一个简单的基于对话框的应用程序,NoCursor,来举例说明这两种方法。如果你把鼠标移动到一个按钮上(OK或Cancel),光标变成蓝色的指示手指(见

Figure 2);该功能通过处理对话框类的OnSetCursor函数实现(见

Figure 3)。另外,当你把鼠标移动到带下划线的超级链接上时,光标变成另一种不同的指示手指。该功能是在子类CStaticLink里实现的(见

Figure 4)。CStaticLink是在我的专栏里经常出现的一个多用途超链接类(见

Figure 5)。CStaticLink::OnSetCursor函数中的大部分代码是处理如何从winhlp32.exe获得适合的手形光标资源,就光标设置而言这些代码无关

紧要,故省略。如果你对这些细节很感兴趣,可以象往常一样从本文顶部的链接下载全部代码。

Figure 4 超级链接上设置的光标

我获得一个含有对话框的库(当然也就包括一些资源ID)。当我在主程序中使用这个库时,库中的资源ID和主程序的资源ID发生了冲突。结果是,要显示库中的对话框时却弹出了主程序中的对话框。要怎样做才能避免冲突?难道要手工对库中的资源ID进行设置吗?

Hans Zwahlen

唉,这个问题没有特别令人满意的答案,只有一些曲线救国的办法。该问题的实质在于Windows中的每个资源必须从属于某个模块(EXE或DLL),而在每一个模块中,特定资源不能有相同的名称或ID。就DLL(动态链接库)而言不存在这样的问题,因为DLL本身就有和主程序不同的HINSTANCE句柄。但对静态链接库来说,所有资源在同一个EXE文件内共存,就像一个大家庭的成员共同生活在一起一样。当然,像家庭成员可能发生冲突一样,资源

也存在发生冲突的可能性。

MFC是如何解决这个问题的呢?它采用800磅重的大猩猩的方法,“我是800磅重的大猩猩,身强体壮,你们打不赢我,最好乖乖听我的。”据我所知MFC没有任何内置的对话框,但有一些通用的菜单项ID,像ID_FILE_OPEN。这些ID也被用作菜单项提示字符串的资源ID。所有MFC的ID值都比0xE000大。最好不要使用大于该值的ID,否则产生冲突就是你的问题。

这种方法对编写操作系统的人来说可能是适合的,但如果你只是一个努力赚钱养家糊口的人,当顾客抱怨你的ID和他的程序有冲突的时候,最好不要“嘴硬”,

你必须要想方设法解决这个问题,要不然怎么办?

我曾经用过的一个办法是:把库中所有资源ID规定为一个已初始化基本值的偏移量,在发生冲突时程序员能够改变这个基本值。可以在一个特殊的头文件中规定这个值,然后程序员必须同时在主程序文件和资源文件中包含它。

// 在主应用程序rc文件

#include "libres.h" // ID 标识符

#include "libres.rc" // 资源

这里libres.rc是包含库中所有对话框、光标和其他资源的资源文件。头文件libres.h以基本值偏移量的方式规定了libres.res中使用的资源ID。

// 在libres.h文件中

#ifndef LIBBASEID

#define LIBBASEID 2000 // 或其他值

#endif

#define IDR_FOO (LIBBASEID+1)

#define IDR_BAR (LIBBASEID+2)

// 等等

只要改变LIBBASEID的值,使用这个库的程序员可以方便地重新映射所有资源ID,例如:

// 在主程序rc文件

#define LIBBASEID 3000

#include "libres.h"

#include "libres.rc"

现在所有的ID从3000开始而不是2000,在实际当中这个办法很有效,但它有一个麻烦的问题,那就是它需要用户重新编译库以改变ID。如果重新编译库是不可行的(也许你不想提供源代码),还有一些其

它的办法。要是程序员改变了LIBBASEID,只有当他直接使用IDR_FOO或其他资源ID时,才需要重新编译库。也就是说,在库中直接引用了这些资源ID:

BOOL SomeLibFn(...)

{

DialogBox(..., IDR_FOO, ...);

}

可以不直接引用这些资源ID,方式之一就是把它们作为调用函数的参数传递。

BOOL SomeLibFn(..., UINT nDlgID)

{

DialogBox(..., nDlgID, ...);

}

现在你不用担心库中的资源ID会发生冲突,因为主程序一定支持它们。改变LIBBASEID值的用户不用也不能重新编译库,因为所有ID是作为函数值传入的。当然这个方法要求编写主程序的程序员必须编辑库中的rc文件以设置ID,还是有点麻烦。更好的办法是以全局静态变量而不是固定的ID值作为偏移量的基本值。

// 在头文件中

extern UINT LibBaseId;

#define IDR_FOO (LibBaseId+1);

#define IDR_BAR (LibBaseId+2);

// 等等

// 在库中主模块

UINT LibBaseId = 2000;

假如现在发生冲突,应用程序可以在启动时改变LibBaseId值(也许是通过一个函数),然后重新编译主程序即可。唉,但这对RC文件不起作用。我曾经说过RC文件的语法并不是纯C/C++语法,而只是其一个子集,所以RC文件不知道extern和UINT是什么。要使资源编译器理解这些符号,你需要使用另外的头文件,或者采用更简便的方法,使用宏RC_INVOKED,这样这些符号就可以放在同一个头文件当中。

// 在libres.h头文件中

#ifdef RC_INVOKED

#ifndef LibBaseId

#define LibBaseId 2000

#endif

#else

extern UINT LibBaseId;

#endif

#define IDR_FOO (LibBaseId+1);

#define IDR_BAR (LibBaseId+2);

// 等等

一般来说,包含在RC文件中的头文件不能包含除#define以外的C代码。

采用这种方法,使用你的库的程序员必须做两件事来重新映射库中的资源ID。第一步,在把libres.h包含到主RC文件之前,他们必须重新#define LibBaseId一个新值。第二步,他们必须在主应用程序的启动代码中(比如在CWinApp::InitInstance函数中)将LibBaseId初始化为同样的值。

最后,有另外一种更简单的解决资源ID冲突的办法:使用字符串而不是数值来映射资源ID。

// 在libres.h头文件中

#define IDR_FOO "MyApp_IDR_FOO"

#define IDR_BAR "MyApp_IDR_BAR"

//等等

使用字符串映射资源ID的效率并不高,因为不仅它们占用空间多,而且查找字符串映射的资源比查找整数映射的资源慢。如果你库中有几百个用于字符串或其他使用频率高的资源ID,我建议你使用整数映射ID并且采用前面提出的一种解决方法。但假如你库中的资源就是几个对话框,那使用字符串映射资源ID是再好不过了。没有人会注意多用了5毫秒显示对话框,并且资源ID冲突的可能性几乎为零。

作者简介

Paul DiLascia,《Windows++: Writing Reusable Windows Code in

C++》(Addison-Wesley出版公司1992出版)的作者,是一名自由作家和顾问。你可以发邮件到askpd@pobox.com或登录http://www.dilascia.com/和他取得联系。

本文由 VCKBASE MTT 翻译

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有