gpt4 book ai didi

c# - 在 C# 中释放 OLE IStorage 文件句柄

转载 作者:行者123 更新时间:2023-11-30 18:09:06 27 4
gpt4 key购买 nike

我正在尝试使用此处描述的 OLE 技术将 PDF 文件嵌入到 Word 文档中: https://learn.microsoft.com/en-us/archive/blogs/brian_jones/embedding-any-file-type-like-pdf-in-an-open-xml-file

我已经尝试实现 C# 中提供的 C++ 代码,以便整个项目都集中在一个地方,并且几乎就在那里,除了一个障碍。当我尝试将生成的 OLE 对象二进制数据提供给 Word 文档时,我得到了一个 IOException。

IOException: The process cannot access the file 'C:\Wherever\Whatever.pdf.bin' because it is being used by another process.

有一个打开 .bin 文件的文件句柄(下面的“oleOutputFileName”),我不知道如何摆脱它。我对 COM 知之甚少 - 我在这里即兴发挥 - 我不知道文件句柄在哪里或如何释放它。

这是我的 C# 化代码的样子。我错过了什么?

    public void ExportOleFile(string oleOutputFileName, string emfOutputFileName)
{
OLE32.IStorage storage;
var result = OLE32.StgCreateStorageEx(
oleOutputFileName,
OLE32.STGM.STGM_READWRITE | OLE32.STGM.STGM_SHARE_EXCLUSIVE | OLE32.STGM.STGM_CREATE | OLE32.STGM.STGM_TRANSACTED,
OLE32.STGFMT.STGFMT_DOCFILE,
0,
IntPtr.Zero,
IntPtr.Zero,
ref OLE32.IID_IStorage,
out storage
);

var CLSID_NULL = Guid.Empty;

OLE32.IOleObject pOle;
result = OLE32.OleCreateFromFile(
ref CLSID_NULL,
_inputFileName,
ref OLE32.IID_IOleObject,
OLE32.OLERENDER.OLERENDER_NONE,
IntPtr.Zero,
null,
storage,
out pOle
);

result = OLE32.OleRun(pOle);

IntPtr unknownFromOle = Marshal.GetIUnknownForObject(pOle);
IntPtr unknownForDataObj;
Marshal.QueryInterface(unknownFromOle, ref OLE32.IID_IDataObject, out unknownForDataObj);
var pdo = Marshal.GetObjectForIUnknown(unknownForDataObj) as IDataObject;

var fetc = new FORMATETC();
fetc.cfFormat = (short)OLE32.CLIPFORMAT.CF_ENHMETAFILE;
fetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
fetc.lindex = -1;
fetc.ptd = IntPtr.Zero;
fetc.tymed = TYMED.TYMED_ENHMF;

var stgm = new STGMEDIUM();
stgm.unionmember = IntPtr.Zero;
stgm.tymed = TYMED.TYMED_ENHMF;
pdo.GetData(ref fetc, out stgm);

var hemf = GDI32.CopyEnhMetaFile(stgm.unionmember, emfOutputFileName);
storage.Commit((int)OLE32.STGC.STGC_DEFAULT);

pOle.Close(0);
GDI32.DeleteEnhMetaFile(stgm.unionmember);
GDI32.DeleteEnhMetaFile(hemf);
}

更新 1:阐明了“.bin 文件”指的是哪个文件。
更新 2:我没有使用“使用” block ,因为我想要摆脱的东西不是一次性的。 (老实说,我不确定我需要释放什么来删除文件句柄,COM 对我来说是一门外语。)

最佳答案

我在您的代码中看到至少四个潜在的引用计数泄漏:

OLE32.IStorage storage; // ref counted from OLE32.StgCreateStorageEx(
IntPtr unknownFromOle = Marshal.GetIUnknownForObject(pOle); // ref counted
IntPtr unknownForDataObj; // re counted from Marshal.QueryInterface(unknownFromOle
var pdo = Marshal.GetObjectForIUnknown(unknownForDataObj) as IDataObject; // ref counted

请注意,所有这些都是指向 COM 对象的指针。 COM 对象不会被 GC 收集,除非保存引用的 .Net 类型指向 RCW 包装器并将在其终结器中正确释放其引用计数。

IntPtr不是这种类型,你的 var也是IntPtr (来自 Marshal.GetObjectForIUnknown 调用的返回类型),所以这就是三个。

你应该调用 Marshal.Release 在你所有的IntPtr变量。

我不确定 OLE32.IStorage .这个可能需要 Marshal.Release Marshal.ReleaseComPointer .

更新:我刚刚注意到我至少漏掉了一个引用计数。 var不是 IntPtr , 这是一个 IDataObject . as cast 将执行隐式 QueryInterface并添加另一个引用计数。虽然GetObjectForIUnknown返回一个 RCW,这个被延迟到 GC 启动。您可能想在 using 中执行此操作 block 激活 IDisposable在上面。

与此同时,STGMEDIUM struct 也有一个 IUnknown你没有释放的指针。你应该调用 ReleaseStgMedium 正确处理整个结构,包括该指针。

我现在太累了,无法继续查看代码。我明天会回来并尝试找到其他可能的引用计数泄漏。同时,您检查 MSDN 文档以了解您正在调用的所有接口(interface)、结构和 API,并尝试找出您可能遗漏的任何其他引用计数。

关于c# - 在 C# 中释放 OLE IStorage 文件句柄,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2688054/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com