C# shell32.dll 的用法
1 首先要使用shell32 请在项目引用中添加shell32.dll 的引用 (备注:该引用是系统dll文件 在C:\Windows\System32 目录下 可以自行拷贝到项目中)
private void btnTest_Click(object sender, EventArgs e) //测试的按钮点击事件 { //测试,将excel中的student导入到sqlserver的DB_MES中,如果sql中的数据表不存在则创建 System.Windows.Forms.OpenFileDialog fd = new OpenFileDialog(); // new 一个打开文件对话框用于选择文件 if (fd.ShowDialog() == DialogResult.OK) { string filePath = fd.FileName; //实例化一个shell 对象 Shell32.Shell shell = new Shell32.ShellClass(); //获取文件所在父目录对象 Folder folder = shell.NameSpace(filePath.Substring(0, filePath.LastIndexOf('\\'))); //获取文件对应的FolderItem对象 FolderItem item = folder.ParseName(filePath.Substring(filePath.LastIndexOf('\\') + 1)); //字典存放属性名和属性值的键值关系对 Dictionary<string, string> Properties = new Dictionary<string, string>(); int i = 0; while (true) { //获取属性名称 string key = folder.GetDetailsOf(null, i); if (string.IsNullOrEmpty(key)) { //当无属性可取时,推出循环 break; } //获取属性值 string value = folder.GetDetailsOf(item, i); //保存属性 Properties.Add(key, value); this.richTextBox1.Text += i.ToString() + key + ":" + value + '\n'; // 窗体界面上创建的richTextBox 控件上显示所有的属性值 i++; } }
//------我的开的文件名是1.xls---------实际运行效果如下------根据运行结果可以查看我们需要的信息对应的索引
0名称:1.xls 1大小:16.0 KB 2项目类型:Microsoft Office Excel 97-2003 工作表 3修改日期:2017/12/22 11:35 4创建日期:2017/12/22 10:38 5访问日期:2017/12/22 11:35 6属性:A 7脱机状态: 8脱机可用性: 9假设的类型:文档 10所有者:USER-20170822CM\Administrator 11种类:文档 12拍摄日期: 13参与创作的艺术家: 14唱片集: 15年: 16流派: 17指挥者: 18标记: 19分级:未分级 20作者:Administrator 21标题: 22主题: 23类别: 24备注: 25版权: 26#: 27长度: 28比特率: 29保护: 30照相机型号: 31尺寸: 32照相机制造商: 33公司: 34文件说明: 35程序名称: 36持续时间: 37联机: 38重复: 39位置: 40可选参加者地址: 41可选的与会者: 42组织者地址: 43组织者名称: 44提醒时间: 45必选的与会者地址: 46必选的与会者: 47资源: 48会议状态: 49忙闲状态: 50总大小: 51帐户名: 52任务状态: 53计算机:USER-20170822CM (本机) 54纪念日: 55助理姓名: 56助理电话: 57生日: 58商务地址: 59公司所在市县: 60公司所在国家/地区: 61公司邮箱: 62公司所在地的邮政编码: 63公司所在省市自治区: 64公司所在街道地址: 65业务传真: 66公司主页: 67商务电话: 68回呼号码: 69车载电话: 70子女: 71公司主要电话: 72部门: 73电子邮件地址: 74电子邮件2: 75电子邮件3: 76电子邮件列表: 77电子邮件显示名称: 78文件为: 79名字: 80全名: 81性别: 82名: 83个人爱好: 84住宅地址: 85住宅所在市/县: 86住宅所在国家/地区: 87住宅邮箱: 88住宅所在地的邮政编码: 89住宅所在省市自治区: 90住宅所在街道地址: 91住宅传真: 92住宅电话: 93即时通讯地址: 94姓名缩写: 95职务: 96标签: 97姓氏: 98通讯地址: 99中间名: 100手机: 101昵称: 102办公位置: 103其他地址: 104其他市县: 105其他国家/地区: 106其他邮政信箱: 107其他邮政编码: 108其他省市自治区: 109其他街道地址: 110寻呼机: 111称谓: 112市县: 113国家/地区: 114邮箱: 115邮政编码: 116省市自治区: 117街道地址: 118主要电子邮件: 119主要电话: 120职业: 121配偶/伙伴: 122后缀: 123TTY/TTD 电话: 124Telex: 125网页: 126内容状态: 127内容类型: 128获取日期: 129存档日期: 130完成日期: 131设备类别: 132已连接: 133发现方法: 134友好名称: 135本地计算机: 136制造商: 137型号: 138已配对: 139分类: 140打印机状态: 141客户端 ID: 142参与者: 143创建内容的时间: 144最后一次打印的时间: 145最后一次保存的日期:2017/12/22 11:35 146分部: 147文档 ID: 148页码范围: 149幻灯片: 150总编辑时间: 151字数: 152截止日期: 153结束日期: 154文件计数: 155文件名:1.xls 156文件版本: 157标志颜色: 158标记状态: 159可用空间: 160位深度: 161水平分辩率: 162宽度: 163垂直分辩率: 164高度: 165重要性: 166是附件: 167已删除: 168加密状态: 169有标志: 170已完成: 171不完整: 172阅读状态: 173已共享:否 174编写者: 175日期: 176文件夹名称:桌面 177文件夹路径:C:\用户\Administrator\桌面 178文件夹:桌面 (C:\用户\Administrator) 179参加者: 180路径:C:\用户\Administrator\桌面\1.xls 181依位置: 182类型:Microsoft Office Excel 97-2003 工作表 183联系人: 184条目类型: 185语言: 186访问时间: 187描述: 188链接状态:未解析 189链接目标: 190URL: 191创建媒体日期: 192发布日期: 193编码人员: 194制作人: 195发布者: 196副标题: 197用户 Web URL: 198创作人: 199附件: 200密件抄送地址: 201密件抄送: 202抄送地址: 203抄送: 204会话 ID: 205接收日期: 206发送日期: 207发件人地址: 208发件人: 209带有附件: 210发信人地址: 211发信人: 212存储: 213收件人地址: 214操作标题: 215收件人: 216里程: 217唱片集艺术家: 218唱片集 ID: 219每分钟节拍数: 220作曲者: 221初始调性: 222编译的一部分: 223氛围: 224部分设置: 225时期: 226颜色: 227家长分级: 228父级分级原因: 229已用空间: 230EXIF 版本: 231事件: 232曝光补偿: 233曝光程序: 234曝光时间: 235光圈值: 236闪光灯模式: 237焦距: 23835mm 焦距: 239ISO 速度: 240镜头制造商: 241镜头型号: 242光源: 243最大光圈: 244测光模式: 245方向: 246人员: 247程序模式: 248饱和度: 249目标距离: 250白平衡: 251优先级: 252项目: 253频道号: 254剧集名称: 255关闭字幕: 256重新运行: 257SAP: 258广播日期: 259节目描述: 260记录时间: 261电台呼叫信号: 262电台名: 263摘要: 264片段: 265自动汇总: 266搜索级别: 267敏感度: 268共享设备: 269共享状态:专用 270产品名称: 271产品版本: 272支持链接: 273源: 274开始日期: 275帐单信息: 276完成: 277任务所有者: 278总计文件大小: 279合法商标: 280视频压缩: 281导演: 282数据速率: 283帧高度: 284帧速率: 285帧宽度: 286总比特率:
上面的方法返回所有属性值,在我的Win7 Pro 64bit 上,返回了287个属性!可以想象,信息是很丰富的,但是速度也是够慢的。
可以看到,上面代码用了一个循环,获取属性名和属性值时都是通过i来索引的。那么,我们是不是就能不通过循环,而直接用下标来获取想要的属性呢?代码如下:
/// <summary> /// 获取指定文件指定下标的属性值 /// </summary> /// <param name="filePath">文件路径</param> /// <param name="index">属性下标</param> /// <returns>属性值</returns> public static string GetPropertyByIndex(string filePath, int index) { if (!File.Exists(filePath)) { throw new FileNotFoundException("指定的文件不存在。", filePath); } //初始化Shell接口 Shell32.Shell shell = new Shell32.ShellClass(); //获取文件所在父目录对象 Folder folder = shell.NameSpace(Path.GetDirectoryName(filePath)); //获取文件对应的FolderItem对象 FolderItem item = folder.ParseName(Path.GetFileName(filePath)); string value = null; //获取属性名称 string key = folder.GetDetailsOf(null, index); if (false == string.IsNullOrEmpty(key)) { //获取属性值 value = folder.GetDetailsOf(item, index); } return value; }
在我的系统环境上,分辨率“尺寸”下标是31,那么我只需要GetPropertyByIndex(fd.fileName,31)就可以获取到分辨率信息了。但是特别需要注意,“尺寸”属性的下标,在不同的Windows版本(XP,Vista,Win7,Win2003等)不一定是一样的。
ok,我们还注意到每个属性都有对应的一个“属性名”,那么,我们能不能通过属性名来获取属性值呢,这样会比使用下标保险多了吧。代码如下:
/// <summary> /// 获取指定文件指定属性名的值 /// </summary> /// <param name="filePath">文件路径</param> /// <param name="propertyName">属性名</param> /// <returns>属性值</returns> public static string GetProperty(string filePath, string propertyName) { if (!File.Exists(filePath)) { throw new FileNotFoundException("指定的文件不存在。", filePath); } //初始化Shell接口 Shell32.Shell shell = new Shell32.ShellClass(); //获取文件所在父目录对象 Folder folder = shell.NameSpace(Path.GetDirectoryName(filePath)); //获取文件对应的FolderItem对象 FolderItem item = folder.ParseName(Path.GetFileName(filePath)); string value = null; int i = 0; while (true) { //获取属性名称 string key = folder.GetDetailsOf(null, i); if (string.IsNullOrEmpty(key)) { //当无属性可取时,退出循环 break; } if (true == string.Equals(key, propertyName, StringComparison.CurrentCultureIgnoreCase)) { //获取属性值 value = folder.GetDetailsOf(item, i); break; } i++; } return value; } GetProperty
这个方法是我一开始写的,通过在while里面加上属性名的判断,直到找到对应的属性名,则返回相应的属性值。
不过这个方法还是不够简洁,“尺寸”属性在31,意味着每一次都需要循环31次才能拿到我要的值,如果我要获取的属性名下标为287(参看上面),那么次数将更多,于是,我又对代码做了一些优化:
/// <summary> /// 存储属性名与其下标(key值均为小写) /// </summary> private static Dictionary<string, int> _propertyIndex = null; /// <summary> /// 获取指定文件指定属性名的值 /// </summary> /// <param name="filePath">文件路径</param> /// <param name="propertyName">属性名</param> /// <returns>属性值</returns> public static string GetPropertyEx(string filePath, string propertyName) { if (_propertyIndex == null) { InitPropertyIndex(); } //转换为小写 string propertyNameLow = propertyName.ToLower(); if (_propertyIndex.ContainsKey(propertyNameLow)) { int index = _propertyIndex[propertyNameLow]; return GetPropertyByIndex(filePath, index); } return null; } /// <summary> /// 初始化属性名的下标 /// </summary> private static void InitPropertyIndex() { Dictionary<string, int> propertyIndex = new Dictionary<string, int>(); //获取本代码所在的文件作为临时文件,用于获取属性列表 string tempFile = System.Reflection.Assembly.GetExecutingAssembly().FullName; Dictionary<string, string> allProperty = GetProperties(tempFile); if (allProperty != null) { int index = 0; foreach (var item in allProperty.Keys) { //属性名统一转换为小写,用于忽略大小写 _propertyIndex.Add(item.ToLower(), index); index++; } } _propertyIndex = propertyIndex; } GetPropertyEx
propertyIndex用于存储属性名与其下标,用Dictionary是因为_propertyIndex[key]的时间复杂度是O(1)。然后在GetPropertyEx方法中找到属性名对应的下标,直接返回该下标的属性值。InitPropertyIndex方法只会被调用一次。
好了,我们现在通过属性名来获取属性值,在不同系统之间应该不会有问题了吧?
不一定,原因你肯定也想到了,如果是在一个英文windows上,它的属性名里面不会有“尺寸”,对应的应该是“Resolution”之类的(我没有英文版系统,所以只是猜测),也不会有“名称”属性,而是“Name”;
总结一下,
方法名 |
适用 |
不适用 |
GetPropertyByIndex |
不同语言的系统 |
不同版本的系统 |
GetPropertyEx |
不同版本的系统 |
不同语言的系统 |
所以,根据你的程序可能的运行环境,选择适合你的方法;
再思考:要能在不同语言不同版本的系统将通用,该怎么办?