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 Properties = new Dictionary ();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.xls1大小:16.0 KB2项目类型:Microsoft Office Excel 97-2003 工作表3修改日期:2017/12/22 11:354创建日期:2017/12/22 10:385访问日期:2017/12/22 11:356属性:A7脱机状态:8脱机可用性:9假设的类型:文档10所有者:USER-20170822CM\Administrator11种类:文档12拍摄日期:13参与创作的艺术家:14唱片集:15年:16流派:17指挥者:18标记:19分级:未分级20作者:Administrator21标题: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:35146分部:147文档 ID:148页码范围:149幻灯片:150总编辑时间:151字数:152截止日期:153结束日期:154文件计数:155文件名:1.xls156文件版本: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.xls181依位置: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来索引的。那么,我们是不是就能不通过循环,而直接用下标来获取想要的属性呢?代码如下:
////// 获取指定文件指定下标的属性值 /// /// 文件路径 /// 属性下标 ///属性值 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,我们还注意到每个属性都有对应的一个“属性名”,那么,我们能不能通过属性名来获取属性值呢,这样会比使用下标保险多了吧。代码如下:
////// 获取指定文件指定属性名的值 /// /// 文件路径 /// 属性名 ///属性值 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(参看上面),那么次数将更多,于是,我又对代码做了一些优化:
////// 存储属性名与其下标(key值均为小写) /// private static Dictionary_propertyIndex = null; /// /// 获取指定文件指定属性名的值 /// /// 文件路径 /// 属性名 ///属性值 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; } ////// 初始化属性名的下标 /// private static void InitPropertyIndex() { DictionarypropertyIndex = new Dictionary (); //获取本代码所在的文件作为临时文件,用于获取属性列表 string tempFile = System.Reflection.Assembly.GetExecutingAssembly().FullName; Dictionary 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 | 不同版本的系统 | 不同语言的系统 |
所以,根据你的程序可能的运行环境,选择适合你的方法;
再思考:要能在不同语言不同版本的系统将通用,该怎么办?