我的以前的测试报告程序需要在倒完测试数据报告后,在文件摘要中加上一些类似版权说明的文字等等. 因此需要对文件摘要信息进行编辑. 我的记忆中以前好像只有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,
,
(IntPtr),
(IntPtr),
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),
(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 = ;
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(, ref propertySpecification, ref
propertyValue, ); // 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 = ,
OVERWRITE = ,
ONLYIFCURRENT = ,
DANGEROUSLYCOMMITMERELYTODISKCACHE = ,
CONSOLIDATE =
} public enum PROPSETFLAG : int
{
DEFAULT = ,
NONSIMPLE = ,
ANSI = ,
UNBUFFERED = ,
CASE_SENSITIVE =
} 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 = ,
FILE = ,
ANY = ,
DOCFILE =
} [StructLayout(LayoutKind.Explicit, Size = , CharSet = CharSet.Unicode)]
public struct PropSpec
{
[FieldOffset()]
public int ulKind;
[FieldOffset()]
public IntPtr Name_Or_ID;
} [StructLayout(LayoutKind.Explicit, Size = )]
public struct PropVariant
{
[FieldOffset()]
public short variantType;
[FieldOffset()]
public IntPtr pointerValue;
[FieldOffset()]
public byte byteValue;
[FieldOffset()]
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 = ,
STG_E_FILEALREADYEXISTS = 0x80030050
} public class ole32
{
[StructLayout(LayoutKind.Explicit, Size = ,
CharSet = CharSet.Unicode)]
public struct STGOptions
{
[FieldOffset()]
ushort usVersion;
[FieldOffset()]
ushort reserved;
[FieldOffset()]
uint uiSectorSize;
[FieldOffset(), 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);
} }

C# 如何编辑文件的摘要信息的更多相关文章

  1. vim编辑文件警告Swap file already exists ,如何删除vim编辑产生的.swp文件?查看隐藏文件命令

    vim编辑文件警告Swap file already exists,如何删除vim编辑产生的.swp文件?查看隐藏文件命令 Linux(centos7)下多个用户同时编辑一个文件,或编辑时非正常关闭, ...

  2. [python爬虫] Selenium定向爬取PubMed生物医学摘要信息

    本文主要是自己的在线代码笔记.在生物医学本体Ontology构建过程中,我使用Selenium定向爬取生物医学PubMed数据库的内容.        PubMed是一个免费的搜寻引擎,提供生物医学方 ...

  3. vi编辑文件保存后,提示“Can't open file for writing Press ENTER or type command to continue”

    在linux上使用vi命令修改或者编辑一个文件内容的时候,最后发现使用<Esc+:+wq!>无法保存退出,却出现,如下提示: E212: Can't open file for writi ...

  4. 如何通过 terminal 查看一个文件的 meta信息

    如何通过 terminal 查看一个文件的 meta 信息 Linux shell stat 查看文件 meta 信息 stat stat指令:文件/文件系统的详细信息显示: 使用格式:stat 文件 ...

  5. 如何直接在ftp里编辑文件

    首先要连接ftp,如何链接ftp,这个我已经分享过,如果还有不懂的,可以查看下经验分享,打开ftp,并连接你要修改的站点!   下载安装代码编辑器,比如EditPlus.sublime text等,随 ...

  6. python 编辑文件时路径问题解决方法:文件或者目录不存在、文件编辑后无法保存等(以编辑xml文件为例)

    1.获取工程所在根路径:根路径=os.path.dirname(os.path.abspath('__file__')) 2.将获取的根路径和相对路径组合:组合路径=os.path.join(根路径, ...

  7. vs切换当前编辑文件时自动定位目录树

    在编辑区,切换当前编辑文件时(单击.cpp或.h文件选项卡),"解决方案资源管理器"目录树会自动定位当前编辑的文件,并以灰色标识,当一个解决方案中的工程数目数目很多,每个工程下面又 ...

  8. 哇 真的是一个好插件!!!Sublime Text编辑文件后快速刷新浏览器

    http://9iphp.com/web/html/sublime-text-refresh-browser.html这篇博文咯 来源:[Tips]Sublime Text编辑文件后快速刷新浏览器 - ...

  9. VS设置程序集属性(文件的详细信息)

    适用范围 本文方法适用于:C#创建的控制台程序,WinForm,WPF等VS创建的.Net工程信息设置. 方法步骤 1.在 项目 上点击鼠标右键选择 属性 ,进入这个页面,点击 程序集信息(重点关注 ...

随机推荐

  1. 错误记录:html隐藏域的值存字符串时出错

    问题 webform在后台给前台传值.  <input type="hidden" value="<%=userType %>" id=&qu ...

  2. Linux基本命令(10)其他命令

    其他命令 命令 功能 命令 功能 echo 显示一字串 passwd 修改密码 clear 清除显示器 lpr 打印 lpq 查看在打印队列中等待的作业 lprm 取消打印队列中的作业 10.1 ec ...

  3. Groovy获取json和xml数据

    如果是xml就用这个 // to read a node from your Response def grUtils = new com.eviware.soapui.support.GroovyU ...

  4. 游戏BI,起步了。

    思索许久,终于决定自己的发展将会是游戏的BI. 即说即做,本文是我未来BI工作的开端. 传统的游戏BI,只是将运营的工作数据化,流量的变现指标化.和网站类似,无外乎用户导入,流失,保有,付费,回访等等 ...

  5. 11个实用经典的SQL小贴士

    学习工作之余,在没有要解决问题的压力之下,还是建议系统的看看书,对于一些认为没啥用的知识点,也建议去仔细的看看,练练手,说不定什么时候就用到了,到时也好有针对性的去查,不至于盲目的按照自己的思路,重复 ...

  6. 软件工程个人项目-Word frequency program by11061167龚少波

    (一)工程设计时间预计 1.代码编写:4小时 熟悉Visual studio 2012的使用 : 程序代码部分主要分为三个步骤: (1)主函数的构建,包括各种函数调用及输入输出部分: (2)对目标文件 ...

  7. 精选PSD素材下载周刊【Goodfav PSD 20130720】

    我们每周精选来自Goodfav PSD的免费PSD素材,有兴趣的朋友尤其是做设计工作的,不妨收藏或者下载. 这些现成的PSD素材能给我们一些相关的灵感,从而提高工作的效率. 1.File Upload ...

  8. 多台服务器最好加上相同的machineKey

      <machineKey validationKey="6E993A81CF4BDCA1C1031528F55DADBB8AF1772A" decryptionKey=&q ...

  9. 第三百四十一天 how can I 坚持

    不好,有点肚子疼,凉肚子了. 今天晚上回来看了个电影<聚焦>,貌似明白了一个道理,任何一份职业,只要认识到了它的价值,那就好好干. 计划又放在脑门后了,上班又闲扯了一天.老季公司招人,让我 ...

  10. <转>linux进程间通信<一>

    这篇文章真心不错,只是代码比较久,有些地方需求大家自行修改.先全文转载,以备复习只用.原文链接为:http://www.ibm.com/developerworks/cn/linux/l-ipc/pa ...