我的以前的测试报告程序需要在倒完测试数据报告后,在文件摘要中加上一些类似版权说明的文字等等. 因此需要对文件摘要信息进行编辑. 我的记忆中以前好像只有office文档才可以又摘要信息, 现在看来基本上所有文件(windows2000以上的平台)都可以有摘要信息..
在网络上搜寻一番发现一些有用的网址 1.如何编辑文件的摘要 http://groups.google.com/group/microsoft.public.dotnet.framework/msg/99a5dd5c80c084b5?hl=zh-CN&lr=&ie=UTF-8&oe=UTF-8&rnum=4 2.微软知识库关于ole32.dll 中函数的说明 http://msdn2.microsoft.com/en-us/library/aa380328.aspx 3.新手对COM的认识及疑惑 http://www.cn-doc.com/_soft_visual_c_tech_doc/2005_08_18_23/20050818230216780.htm[原处]http://dev.csdn.net/user/putongren
下面是对本问题的总结:
刚开始,我以为是shell32里面的方法可以完成,因此引用了Microsoft Shell Controls And Automation, (shell32.dll),但经过分析后没有找到相关的方法. shell32是COM可以托管引用. 引用shell32可以查出文件的摘要信息,但无法编辑保存,下面是关于这个问题的答案. 如何用C#获得文件信息以及扩展信息
多次网上查询后,我发现这篇在微软讨论组的文章(居然用google搜出来,没有用msdn搜出),实际已经解决我的问题 setting file comment attribute programmatically
主要思路定义相关结构体(以接口方式定义)及输入参数的枚举值 引入ole32.dll 定义静态方法对应函数 通过静态方法返回接口, 此接口实际上就是一个指针(可以调用它的函数) 调用接口函数
开始我以为ole32.dll是一个COM,一直想引用,结果发现它只是一个函数库, 必须对它进行dllimport.具体代码可以看后面. 另外,我还发现以前微软的virsual studio 6 里面的Depends工具,依然在virsual studio 2005中提供了,目录在?:/Program Files/Microsoft Visual Studio 8/Common7/Tools 用这个工具可以查看一个函数库有哪些公开方法.
补充点知识,先
对于使用COM或者函数库的总结,来之(http://dev.csdn.net/user/putongren), 引用一部分
1. 想办法知道 com 组件的 clsid (一个编号,16字节长,全球唯一);如果不知道 clsid,则要知道名字(progid),如 excel.application。
2. 开始与 ole32.dll 打交道吧 ! 如果进程尚未装载它,那就 loadlibrary,然后再 getprocaddress ...
3. 不管三七二十一,先运行调用 coinitializeex,ole32.dll 内的一个函数,以便 ole32.dll 内部为线程记录相应的数据,并允许你调用 ole32.dll 内的其他函数。
4. 如果已知道 clsid,则此步跳过。否则调用 clsidfromprogid 根据名字计算出 clsid。
这个 ole32.dll 内的函数实际上是在注册表中进行查找,位置在: /hkey_classes_root/名字(progid),其下有一个 /clsid,里面就是clsid,其实自己去注册表查找也可以。 excel.application 对应的 clsid = {00024500-0000-0000-c000-000000000046}。
5. 调用 ole32.dll 内的 cogetclassobject 建立一个 clsid 的 object。也可以调用 cocreateinstance / cocreateinstanceex。
这个 object 可以想象成操作系统在内存中加载了一个动态链接库,或独立启动了一个进程,它不返回 object 的句柄,所以不要想通过一个句柄进行进一步访问——像 getwindow(hwnd, ...),也趁早放弃通过 getprocaddress 取得函数入口位置进行进一步访问的想法。到底怎么访问呢,往下看...
6. 调用 cogetclassobject 建立一个 clsid 的 object 的同时,根据一个接口编号参数 iid_i... (全世界统一固定编号)返回一个接口指针,一个接口可以想象成一个函数的集合。
根据 clsid 产生的 object 可能有若干个不同的函数集合,每一个函数集合都有一个接口编号与其对应,iunknown 这个编号对应的接口是每个 object 都有的。每个函数集的前三个函数都一样:queryinterface、addref、release。把其他接口编号作为参数调用 queryinterface 就可以找到其对应的函数集。
7. 关于 clsid 和 接口 iid
clsid 是一个 16 字节长的全球唯一编号,ole32.dll 根据它在注册表中寻找对应的dll文件(也可以是exe文件)。参考程序与进程的概念,clsid 的实例化是加载一个 dll,或运行一个 exe 进程。dll 或 exe 被 ole32.dll 加载后,在 ole32.dll 的地址空间为其建立若干函数入口地址表(当然根据 dll 或 exe 的自身定义),并为每一个函数入口表建立一个保存其开始地址的指针(这个指针是地址中的一个长字)。
iid -- 接口,也是一个 16 字节长的全球唯一编号,ole32.dll 根据它在 clsid 的实例的所有 函数入口地址表的 指针 中找到一个对应的,返回来。所以接口的实例就是那个返回来的指针。
c 程序员可以把接口认为是一个结构,其成员是若干函数;c++ 程序员可以把接口认为是一个类 class。
至于通过 iid 找到的 函数入口表 中的各函数如何调用,去看那个 dll 或 exe 的开发接口文档吧。在调用一个具体的函数时,当然可以把参数压入堆栈,然后 call 那个函数,不过这好像是汇编中的低级做法,比较麻烦,在 vc 中定义一个类指针来保存接口返回来的指针,然后访问类成员,用起来比较方便——参数压入堆栈的问题编译器替你解决了。
8. com
所以 com,概括起来说,就是用两级编号,通过 ole32.dll 调用某个 dll 或 exe 提供的函数的方式。
当然在这基础上还有 automation 等,定义了更多标准的接口编号及访问方式,完成更复杂的功能。
调用方法SetProperty
/*pls visit this article * * http://msdn2.microsoft.com/en-us/library/aa380328.aspx * To open an existing file, use the StgOpenStorageEx function instead. notice: Applications written for Windows 2000, Windows Server 2003 and Windows XP must use StgCreateStorageEx rather than StgCreateDocfile to take advantage of the enhanced Windows 2000 and Windows XP Structured Storage features. * */ using System; using System.Collections.Generic; using System.Text; using StructuredStorageWrapper; namespace WindowsApplication8 { class FileSummary { public static void SetProperty(string filename,string msg , SummaryPropId summaryType) { // first you need to either create or open a file and its // property set stream //申明接口(指针) IPropertySetStorage propSetStorage = null; //com 组件的 clsid 参见IPropertySetStorage定义 Guid IID_PropertySetStorage = new Guid("0000013A-0000-0000-C000-000000000046"); //Applications written for Windows 2000, Windows Server 2003 and Windows XP must use StgCreateStorageEx rather than StgCreateDocfile to take advantage of the enhanced Windows 2000 and Windows XP Structured Storage features uint hresult = ole32.StgOpenStorageEx( filename, (int)(STGM.SHARE_EXCLUSIVE | STGM.READWRITE), (int)STGFMT.FILE, 0, (IntPtr)0, (IntPtr)0, ref IID_PropertySetStorage, ref propSetStorage); //返回指针 // next you need to create or open the Summary Information property set Guid fmtid_SummaryProperties = new Guid("F29F85E0-4FF9-1068-AB91-08002B27B3D9"); IPropertyStorage propStorage = null; hresult = propSetStorage.Create( ref fmtid_SummaryProperties, (IntPtr)0, (int)PROPSETFLAG.DEFAULT, (int)(STGM.CREATE | STGM.READWRITE | STGM.SHARE_EXCLUSIVE), ref propStorage); // next, you assemble a property descriptor for the property you // want to write to, in our case the Comment property PropSpec propertySpecification = new PropSpec(); propertySpecification.ulKind = 1; propertySpecification.Name_Or_ID = new IntPtr((int)summaryType); //now, set the value you want in a property variant PropVariant propertyValue = new PropVariant(); propertyValue.FromObject(msg); // Simply pass the property spec and its new value to the WriteMultiple // method and you‘re almost done propStorage.WriteMultiple(1, ref propertySpecification, ref propertyValue, 2); // the only thing left to do is commit your changes. Now you‘re done! hresult = propStorage.Commit((int)STGC.DEFAULT); //下面的很关键,如何关闭一个非托管的指针,如果不关闭,则本程序不关闭,文件被锁定! System.Runtime.InteropServices.Marshal.ReleaseComObject(propSetStorage); propSetStorage = null; GC.Collect(); } } } 把ole32一些方法进行COM封装 using System; using System.Text; using System.Runtime.InteropServices; namespace StructuredStorageWrapper { public enum SummaryPropId : int { Title = 0x00000002, Subject = 0x00000003, Author = 0x00000004, Keywords = 0x00000005, Comments = 0x00000006, Template = 0x00000007, LastSavedBy = 0x00000008, RevisionNumber = 0x00000009, TotalEditingTime = 0x0000000A, LastPrinted = 0x0000000B, CreateDateTime = 0x0000000C, LastSaveDateTime = 0x0000000D, NumPages = 0x0000000E, NumWords = 0x0000000F, NumChars = 0x00000010, Thumbnail = 0x00000011, AppName = 0x00000012, Security = 0x00000013 } public enum STGC : int { DEFAULT = 0, OVERWRITE = 1, ONLYIFCURRENT = 2, DANGEROUSLYCOMMITMERELYTODISKCACHE = 4, CONSOLIDATE = 8 } public enum PROPSETFLAG : int { DEFAULT = 0, NONSIMPLE = 1, ANSI = 2, UNBUFFERED = 4, CASE_SENSITIVE = 8 } public enum STGM : int { READ = 0x00000000, WRITE = 0x00000001, READWRITE = 0x00000002, SHARE_DENY_NONE = 0x00000040, SHARE_DENY_READ = 0x00000030, SHARE_DENY_WRITE = 0x00000020, SHARE_EXCLUSIVE = 0x00000010, PRIORITY = 0x00040000, CREATE = 0x00001000, CONVERT = 0x00020000, FAILIFTHERE = 0x00000000, DIRECT = 0x00000000, TRANSACTED = 0x00010000, NOSCRATCH = 0x00100000, NOSNAPSHOT = 0x00200000, SIMPLE = 0x08000000, DIRECT_SWMR = 0x00400000, DELETEONRELEASE = 0x04000000 } public enum STGFMT : int { STORAGE = 0, FILE = 3, ANY = 4, DOCFILE = 5 } [StructLayout(LayoutKind.Explicit, Size = 8, CharSet = CharSet.Unicode)] public struct PropSpec { [FieldOffset(0)] public int ulKind; [FieldOffset(4)] public IntPtr Name_Or_ID; } [StructLayout(LayoutKind.Explicit, Size = 16)] public struct PropVariant { [FieldOffset(0)] public short variantType; [FieldOffset(8)] public IntPtr pointerValue; [FieldOffset(8)] public byte byteValue; [FieldOffset(8)] public long longValue; public void FromObject(object obj) { if (obj.GetType() == typeof(string)) { this.variantType = (short)VarEnum.VT_LPWSTR; this.pointerValue = Marshal.StringToHGlobalUni((string)obj); } } } [ComVisible(true), ComImport(), Guid("0000013A-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IPropertySetStorage { uint Create( [In, MarshalAs(UnmanagedType.Struct)] ref System.Guid rfmtid, [In] IntPtr pclsid, [In] int grfFlags, [In] int grfMode, ref IPropertyStorage propertyStorage); int Open( [In, MarshalAs(UnmanagedType.Struct)] ref System.Guid rfmtid, [In] int grfMode, [Out] IPropertyStorage propertyStorage); } [ComVisible(true), ComImport(), Guid("00000138-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IPropertyStorage { int ReadMultiple( uint numProperties, PropSpec[] propertySpecifications, PropVariant[] propertyValues); int WriteMultiple( uint numProperties, [MarshalAs(UnmanagedType.Struct)] ref PropSpec propertySpecification, ref PropVariant propertyValues, int propIDNameFirst); uint Commit( int commitFlags); } public enum HResults : uint { S_OK = 0, STG_E_FILEALREADYEXISTS = 0x80030050 } public class ole32 { [StructLayout(LayoutKind.Explicit, Size = 12, CharSet = CharSet.Unicode)] public struct STGOptions { [FieldOffset(0)] ushort usVersion; [FieldOffset(2)] ushort reserved; [FieldOffset(4)] uint uiSectorSize; [FieldOffset(8), MarshalAs(UnmanagedType.LPWStr)] string pwcsTemplateFile; } [DllImport("ole32.dll", CharSet = CharSet.Unicode)] public static extern uint StgCreateStorageEx( [MarshalAs(UnmanagedType.LPWStr)] string name, int accessMode, int storageFileFormat, int fileBuffering, IntPtr options, IntPtr reserved, ref System.Guid riid, [MarshalAs(UnmanagedType.Interface)] ref IPropertySetStorage propertySetStorage); [DllImport("ole32.dll", CharSet = CharSet.Unicode)] public static extern uint StgOpenStorageEx( [MarshalAs(UnmanagedType.LPWStr)] string name, int accessMode, int storageFileFormat, int fileBuffering, IntPtr options, IntPtr reserved, ref System.Guid riid, [MarshalAs(UnmanagedType.Interface)] ref IPropertySetStorage propertySetStorage); } }