前一段时间我写了篇文章"关于以前写的VBA在新版本64位Inventor中无法使用的问题",然后就对Structure Storage API似乎有一发不可收拾的兴趣。
这一次我打算使用Structure Storage API来做个有效、方便的工具来删除Inventor文件中的VBA。Inventor文件是基于Structure Storage的,或者说IStorage/IStream。这类文件有些人翻译成复合文件。这一类型的文件都支持structure storage API,因此我们就可以用structure storage API来修改文件(内部包含的属性、信息)。IStorage是一种COM接口,能用C++调用,如果你对如何用C++调用IStorage接口来删除VBA程序感兴趣,我建议你看看Brian的博客:
我今天要讲的是C#调用IStorage接口。为了调用IStorage接口,我们要先给这个COM接口做一个.NET封装(wrapper),下面我会贴出C#的封装源代码,以及我的程序中主要的实现代码供大家参考:
IStorage 的C#封装:
[DllImport("ole32.dll")]
static extern int
StgIsStorageFile([MarshalAs(UnmanagedType.LPWStr)]
string pwcsName);
[DllImport("ole32.dll")]
static extern int
StgCreateDocfile([MarshalAs(UnmanagedType.LPWStr)]
string pwcsName, STGM grfMode, uint reserved, out IStorage
ppstgOpen);
[DllImport("ole32.dll")]
static extern int StgOpenStorage(
[MarshalAs(UnmanagedType.LPWStr)] string pwcsName
, IStorage pstgPriority
, STGM grfMode
, IntPtr snbExclude
, uint reserved
, out IStorage ppstgOpen);
private ArrayList fileList = new ArrayList();
public SSProcessor(ArrayList _fileList)
{
SetFileList(_fileList);
}
private void SetFileList(ArrayList _fileList)
{
fileList = _fileList;
}
public void CullFiles()
{
int count = 0;
foreach (FileSystemInfo file in fileList)
{
try
{
StgIsStorageFile(file.FullName);
count++;
}
catch
{
fileList.RemoveAt(count);
//add code to log non-storagefile items
}
}
}
public enum STGM : uint
{
STGM_READ = 0x00000000,
STGM_WRITE = 0x00000001,
STGM_READWRITE = 0x00000002,
STGM_SHARE_DENY_NONE = 0x00000040,
STGM_SHARE_DENY_READ = 0x00000030,
STGM_SHARE_DENY_WRITE = 0x00000020,
STGM_SHARE_EXCLUSIVE = 0x00000010,
STGM_PRIORITY = 0x00040000,
STGM_CREATE = 0x00001000,
STGM_CONVERT = 0x00020000,
STGM_FAILIFTHERE = 0x00000000,
STGM_DIRECT = 0x00000000,
STGM_TRANSACTED = 0x00010000,
STGM_NOSCRATCH = 0x00100000,
STGM_NOSNAPSHOT = 0x00200000,
STGM_SIMPLE = 0x08000000,
STGM_DIRECT_SWMR = 0x00400000,
STGM_DELETEONRELEASE = 0x04000000,
}
public enum STGC : uint
{
STGC_DEFAULT = 0,
STGC_OVERWRITE = 1,
STGC_ONLYIFCURRENT = 2,
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4,
STGC_CONSOLIDATE = 8
}
public enum STATFLAG : uint
{
STATFLAG_DEFAULT = 0,
STATFLAG_NONAME = 1
}
[ComImport]
[Guid("0000000d-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
public interface IEnumSTATSTG
{
// The user needs to allocate an STATSTG array whose size is celt.
[PreserveSig]
uint
Next(
uint celt,
//[MarshalAs(UnmanagedType.LPArray), Out]
out ComTypes.STATSTG rgelt,
out uint pceltFetched
);
void Skip(uint celt);
void Reset();
[return: MarshalAs(UnmanagedType.Interface)]
IEnumSTATSTG Clone();
}
[ComImport]
[Guid("0000000b-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
public interface IStorage
{
void CreateStream(
/* [string][in] */ string pwcsName,
/* [in] */ STGM grfMode,
/* [in] */ uint reserved1,
/* [in] */ uint reserved2,
/* [out] */ out IStream ppstm);
void OpenStream(
/* [string][in] */ string pwcsName,
/* [unique][in] */ IntPtr reserved1,
/* [in] */ STGM grfMode,
/* [in] */ uint reserved2,
/* [out] */ out IStream ppstm);
void CreateStorage(
/* [string][in] */ string pwcsName,
/* [in] */ STGM grfMode,
/* [in] */ uint reserved1,
/* [in] */ uint reserved2,
/* [out] */ out IStorage ppstg);
void OpenStorage(
/* [string][unique][in] */ string pwcsName,
/* [unique][in] */ IStorage pstgPriority,
/* [in] */ STGM grfMode,
/* [unique][in] */ IntPtr snbExclude,
/* [in] */ uint reserved,
/* [out] */ out IStorage ppstg);
void CopyTo(
/* [in] */ uint ciidExclude,
///* [size_is][unique][in] */ Guid rgiidExclude,
//Guid[] rgiidExclude,
IntPtr rgiidExclude,
///* [unique][in] */ IntPtr snbExclude,
//string[] snbExclude,
//IntPtr[] snbExclude,
IntPtr snbExclude,
/* [unique][in] */ IStorage pstgDest);
void MoveElementTo(
/* [string][in] */ string pwcsName,
/* [unique][in] */ IStorage pstgDest,
/* [string][in] */ string pwcsNewName,
/* [in] */ uint grfFlags);
void Commit(
/* [in] */ STGC grfCommitFlags);
void Revert();
void EnumElements(
/* [in] */ uint reserved1,
/* [size_is][unique][in] */ IntPtr reserved2,
/* [in] */ uint reserved3,
/* [out] */ out IEnumSTATSTG ppenum);
void DestroyElement(
/* [string][in] */ string pwcsName);
void RenameElement(
/* [string][in] */ string pwcsOldName,
/* [string][in] */ string pwcsNewName);
void SetElementTimes(
/* [string][unique][in] */ string pwcsName,
/* [unique][in] */ ComTypes.FILETIME pctime,
/* [unique][in] */ ComTypes.FILETIME patime,
/* [unique][in] */ ComTypes.FILETIME pmtime);
void SetClass(
/* [in] */ Guid clsid);
void SetStateBits(
/* [in] */ uint grfStateBits,
/* [in] */ uint grfMask);
void Stat(
/* [out] */ out ComTypes.STATSTG pstatstg,
/* [in] */ STATFLAG grfStatFlag);
}
调用IStorage来删除VBA代码的函数(节点RSeStorage/RSeEmbeddings/apc) :
public void ProcessFiles()
{
foreach (FileSystemInfo file in fileList)
{
// Open the file as a storage.
IStorage stg;
IStorage stg_new;
int result = StgOpenStorage(file.FullName, null, STGM.STGM_DIRECT | STGM.STGM_READWRITE | STGM.STGM_SHARE_EXCLUSIVE, IntPtr.Zero, 0, out stg);
// Find the storage with the path "RSeStorage/RSeEmbeddings/apc"
stg.OpenStorage("RSeStorage", null, STGM.STGM_DIRECT | STGM.STGM_READWRITE | STGM.STGM_SHARE_EXCLUSIVE, IntPtr.Zero, 0, out stg_new);
if (stg_new == null)
{
System.Windows.Forms.MessageBox.Show("can't find RSeStorage node");
return;
}
stg_new.OpenStorage("RSeEmbeddings", null, STGM.STGM_DIRECT | STGM.STGM_READWRITE | STGM.STGM_SHARE_EXCLUSIVE, IntPtr.Zero, 0, out stg);
if (stg == null)
{
System.Windows.Forms.MessageBox.Show("can't find RSeEmbeddings node");
return;
}
ComTypes.STATSTG s1 = new ComTypes.STATSTG();
IEnumSTATSTG SSenum;
stg.EnumElements(0, IntPtr.Zero, 0, out SSenum);
uint NumReturned = 0;
do
{
SSenum.Next(1, out s1, out NumReturned);
if (NumReturned > 0)
{
if (s1.pwcsName == "apc")
{
// Delete the storage
stg.DestroyElement("apc");
stg.Commit(STGC.STGC_DEFAULT);
break;
}
}
} while (NumReturned > 0);
}
System.Windows.Forms.MessageBox.Show("VBA project has been deleted!");
}
调用上面的函数的代码:
ArrayList fileList;
// Open the storage and read from storage
if (fileList.Count > 0)
{
SSProcessor ssProc = new SSProcessor(fileList);
ssProc.ProcessFiles();
}
实现选择文件功能:
try
{
FolderBrowserDialog folderDlg = new FolderBrowserDialog();
folderDlg.Description = "选择文件夹";
DialogResult res = folderDlg.ShowDialog(this);
if (res == DialogResult.OK)
{
fileList.Clear();
this.listBox1.Items.Clear();
//Get the Directory Info using Directory Name
DirectoryInfo dirInfor = new DirectoryInfo(folderDlg.SelectedPath);
FileInfo[] partFiles = dirInfor.GetFiles("*.ipt");
foreach (FileInfo fInfo in partFiles)
{
this.listBox1.Items.Add(fInfo.FullName);
fileList.Add(fInfo);
}
FileInfo[] assFiles = dirInfor.GetFiles("*.iam");
foreach (FileInfo fInfo in assFiles)
{
this.listBox1.Items.Add(fInfo.FullName);
fileList.Add(fInfo);
}
FileInfo[] drFiles = dirInfor.GetFiles("*.idw");
foreach (FileInfo fInfo in drFiles)
{
this.listBox1.Items.Add(fInfo.FullName);
fileList.Add(fInfo);
}
}
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
简单的执行界面图:
你也可以到我的资源中心(http://barbarahan.download.csdn.net/)去下载完整的源码deleteVBA_C#_source和执行程序deleteVBA_C#_exe。(我刚才上传了exe资源,但是CSDN没有显示,也许等你看的时候就出来了。)