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 |
不同版本的系統 |
不同語言的系統 |
所以,根據你的程序可能的運行環境,選擇適合你的方法;
再思考:要能在不同語言不同版本的系統將通用,該怎么辦?
