C++ Q&A 专栏:用 VolInfo 获取逻辑驱动器.....

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

C++ Q&A 专栏...

原著:Paul DiLascia

翻译:彭德奎

原文出处:MSDN Magazine Jan 2004(C++ Q&A)

原代码下载:CQA0401.exe (127KB)

1、如何用 VolInfo 获取逻辑驱动器?

2、如何用C#编程修改系统菜单?

我试图写一个能列出系统上的驱动器(C:,D:等)的MFC程序,我也需要知道该驱动器是一个硬盘或是光驱。有这样的一个类来获得此信息吗?

没有提供这种信息的MFC类,但Microsoft®

Windows®有一组卷管理函数能实现这个目的。其中包括获取逻辑驱动器以及卷挂接点(mount

points)信息的函数,高级的NTFS的特性不在此之列。对于你的要求,你只要处理逻辑驱动器信息即可。Figure 1显示了相关函数。

有四个基本的函数:GetLogicalDrives, GetLogicalDriveStrings, GetDriveType和

GetVolumeInformation。第五个是SetVolumeLabel,如果你愿意,可以用它设置卷标。这些函数都相当简单易懂,为了使用方便起见,我把它们封装到了一个友好的MFC类中:CVolumeMaster,(参见

Figure 2),它可以让你处理CString而不是TCHAR数组。我还写了一个

例子程序:VolInfo.exe,示范了如何使用这个MFC类。你能从本文开始的超链接处下载源代码。Figure

3是VolInfo例子程序在我自己的计算机上运行时显示的详细信息。

Figure 3 详细的驱动器信息

第一个函数,GetLogicalDrives,返回一个DWORD的位掩码(bitmask)值,以告知驱动盘符。“0”表示是驱动器A,“1”表示驱动器B,依次类推。大家知道,英语字母表有26个字母,而DWORD有32位,你们中数学在行的人将快速地推算出:一个DWORD能提供足够大的空间来容纳所有,或者是一些可能的驱动器字母的组合。幸好微软(Redmond)不在西北利亚(古代斯拉夫语的字母有33个字母),CVolumeMaster有一个静态的方法FormatBitMask,它位掩码信息格式化为ASCII----VolInfo程序应用它来显示一条象下面这样的信息:

10110 10001 11000 00000 00000 00000 00

这条信息表示在我的电脑里有驱动器:A、C、D、F、J、K、和L。哟!如果你的大脑皮层以二进制形式编程编多了,迟早会有GetLogicalDriveStrings这样的函数诞生,它返回一个代表所有驱动器字母的重要字符串。每一个驱动器字母拥有D:\(尾随一个‘\’)的形式,这里

D 表示驱动器盘符,每个字符串有一空(null)终结符,结尾处有两个null。既然知道了用 TCHARs 处理很拘束,那么就写一个顺手的CVolumeMaster封装器,用

CStringArray 来保存得到的驱动器字母。毕竟这是个C++专栏。你只要写如下的代码:

CVolumeMaster vm;

CStringArray arDrives;

int n = vm.GetLogicalDriveStrings(arDrives);

现在arDrives里是驱动器字母串,n是逻辑驱动器数目。明白了吧?

有哪些驱动器你是知道了,但如何知道各个驱动器是什么类型的呢?GetDriveType就是为此而设。GetDriveType返回一个代码,如

DRIVE_FIXED 代表硬盘,或者 DRIVE_CDROM

代表CD-ROM驱动器。CVolumeMaster有一静态函数,用于将代码格式化为人可识别的字符串;VolInfo用它作为输出。详细情况请参见源代码。

最后,如果你想进一步了解某个逻辑驱动器,比如它的卷标,它使用的文件系统或者驱动器是否支持命名流(named streams)和加密,调用

GetVolumeInformation 函数即可。这种瑞士军刀式的函数可以获得卷标、文件系统名称(如, NTFS

或是FAT)、卷序列号、文件系统标识、最大组件长度。

你会问“最大组件长度是什么鬼东西?” 那是指反斜线符之间路径名称部分长度的文件系统表示。换句话说,如果路径名称是c:\mumble\bletch\oops,那么“mumble”、“bletch”和“oops”就是组件,每个组件的长度是有一个限制的。使用VolInfo,你可以发现NTFS支持组件的最大长度为255,而CD-ROM通常只为127。这就解释了为什么当你保存你全部的MP3到CD,你经常会得到一条信息,告诉你一些文件名或其别的什么东西太长,询问是否截短它。

CVolumeMaster 有一个自己的 GetVolumeInformation 版本——它使用 CString 代替LPTSTR。

CString volname,filesys;

DWORD serno, maxcomplen, flags;

vm.GetVolumeInformation("C:\", volname, serno, maxcomplen, flags, filesys);

与此同时,我坚持使用 CString

的原因并不是因为它更容易,它也很安全。在注重安全以及恶意病毒肆虐的今天,即使阿诺德.施瓦辛格也知道什么是缓冲区溢出。使用 CString

是一条较好的避免途径。

对于标志,它们 Figure 4 中定义。

WinBase.h 和 WinNT.h 展示了 GetVolumeInformation可以返回的标志。再一次说明, CVolumeMaster有一个函数可以将这些标志格式化为一种人可识别的字符串——

VolInfo 例子程序用到了这种格式化,也可以用它来调试你自己程序。

知道你是一位C#专家(同时也是一位C++专家),我有一个问题。我怎样才能修改系统菜单?在C++里,我可以使用 GetSystemMenu

函数,但在C#中,我不知道该如何完成?

Philippe Morvan

嘿,Philippe,至少在我有10年经验之前我不能称自己为C#专家,而且C#出现并不长。然而,我知道你的问题的答案:使用 GetSystemMenu。对,就如你在C++中一样。怎样做呢?自然是用 托管。

有时我感觉就像坏掉的唱片,因为如此多的C#问题,我都用相同的答案:托管。那是因为我得到的大部分问题都是 GUI 问题,并且

Windows 窗体目前只暴露基本的窗口子集。一旦你想做一些复杂的东西,你还必须返回到Win32®。幸运的是,Microsoft

.NET Framework 托管服务使得它更容易。

如果有一种方法能用 Windows 窗体获得系统菜单,那么窗体类中就应该有一个类似SystemMenu的东西。啊哈,事实上没有这样的属性。

控件一般都用 Control.ContextMenu 得到上下文菜单,窗体用 Form.Menu 获得主菜单,但没有 SystemMenu 或是其它的属性

用 Menu 来直接存取系统菜单。这就是你要使用托管的原因。我写了一个小程序,SysMenu,来示范如何使用托管。Figure 5列出了代码。Figure 6为结果。

Figure 6 修改后的系统菜单

为使用GetSystemMenu API函数,首先声明托管方式, 用 DllImpor。. 对于SysMenu, 你实际上需要两个函数:

GetSystemMenu 和 AppendMenu.

using System.Runtime.InteropServices;

public class Form1 : Form

{

[DllImport("user32.dll")]

private static extern IntPtr GetSystemMenu(IntPtr hwnd, int bRevert);

[DllImport("user32.dll")]

private static extern bool AppendMenu(IntPtr hMenu,

MenuFlags uFlags, uint uIDNewItem, String lpNewItem);

}

你应该经常使用 IntPtr 来代替 HWNDs、HMENUs和其它类型的窗口句柄。对于 LPCTSTRs,

将参数声明为String类型。托管服务会在传给 Windows 之前将System::String自动转换为 LPCTSTR类型。对于

MenuFlags, 那是你必须自己定义的枚举:

public enum MenuFlags {

MF_INSERT = 0x00000000,

MF_CHANGE = 0x00000080,

ooo // etc

}

你不一定非要用枚举,但用枚举更安全。MF_XXX 值来自 WinUser.h。最后, 你需要一个新的命令

ID。在SysMenu中,IDC_MYCOMMAND值为 100. 如果你使用的值小于0xF000, 你要保证不和SC_MINIMIZE,

SC_MAXIMIZE 或其它内建的系统命令冲突。同时也必须确保不和你自己的主菜单命令冲突。有了这些定义之后,

你便可以开始添加菜单项。所有需要做的只是在你的窗体构造函数中添加很少的代码。首先是获得系统菜单:

// Get system menu

IntPtr hSysMenu = GetSystemMenu(this.Handle, 0);

随后是加入你的命令:

// Add separator and new command

AppendMenu(hSysMenu,MenuFlags.MF_SEPARATOR,0,null);

AppendMenu(hSysMenu,MenuFlags.MF_BYCOMMAND, IDC_MYCOMMAND, "Do you like interop?");

现在当用户在窗口标题栏点击系统菜单,你的新菜单将显示,如 Figure

6所示。只是为了好玩,我给了它一个复选标记。但用户调用你的命令时会发生什么呢?目前,什么也不会发生。为处理这个命令,你必须重写窗体的虚拟

WndProc 方法:

const int WM_SYSCOMMAND = 0x0112;

protected override void WndProc(ref Message msg)

{

if (msg.Msg==WM_SYSCOMMAND) {

if (msg.WParam.ToInt32() == IDC_MYCOMMAND) {

// handle it!

return;

}

}

base.WndProc(ref msg);

}

无论你做什么, 如果消息不是你的,不要忘记调用基类的 WndProc 方法。否则你的程序将会挨一记重拳后回家。

好了,今天就到这儿。和通常一样,你可以从MSDN®

Magazine Web 站点上下载所有程序资源。

编程快乐!

使用 cppqa@microsoft.com 发送你的问题和评论给 Paul

作者简介:

Paul DiLascia 是一个自由作家,顾问和 Web/UI 方面资深的设计师。他是 Windows++: Writing Reusable

Windows Code in C++ (Addison-Wesley, 1992)一书的作者。你可以在

http://www.dilascia.com 网站和 Paul

联系上。

本文出自

MSDN Magazine

January 2004 期刊,可通过当地

报摊获得,或者最好是 订阅

本文由 VCKBASE MTT 翻译

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
 
© 2005- 王朝網路 版權所有 導航