我正在尝试使用此处描述的OLE技术将PDF文件嵌入Word文档中:http://blogs.msdn.com/brian_jones/archive/2009/07/21/embedding-any-file-type-like-pdf-pdf-in-an-an-open-open-xml-file.aspx

我尝试实现C#中提供的C ++代码,以使整个项目都位于一个地方,几乎在那里,除了一个路障。当我尝试将生成的Ole Object二进制数据馈送到Word文档中时,我会得到IOException。

IOException:该过程无法访问文件'c: whyere whying.pdf.bin',因为另一个过程正在使用它。

有一个文件句柄打开.bin文件(下面的“ OleoutputfileName”),我不知道如何摆脱它。我不知道关于com的大量数量 - 我在这里挥动它 - 我不知道文件句柄在哪里或如何释放它。

这是我的C#-ISED代码的样子。我想念什么?

    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:我不使用“使用”块,因为我想摆脱的东西不可支配。 (说实话,我不确定我需要发布什么来删除文件句柄,对我来说是外语。)

有帮助吗?

解决方案 2

我找到了答案,这很简单。 (可能太简单了 - 感觉像是一个黑客,但由于我对com编程的了解鲜为人知,所以我将继续使用它。)

存储对象上有多个参考,因此请继续前进直到它们全部消失:

var storagePointer = Marshal.GetIUnknownForObject(storage);
int refCount;
do
{
    refCount = Marshal.Release(storagePointer);
} while (refCount > 0);

其他提示

我看到您的代码中至少有四个潜在的重新数泄漏:

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对象的指针。除非将参考点的.NET类型符合RCW包装器,否则不会由GC收集COM对象,并将在最终确定器中正确释放其参考计数。

IntPtr 不是这样的类型 var 也是 IntPtr (从返回类型 Marshal.GetObjectForIUnknown 打电话),这使三个。

你应该打电话 Marshal.Release 一切 IntPtr 变量。

我不确定 OLE32.IStorage. 。这个也许需要 Marshal.Release 或者 Marshal.ReleaseComPointer.

更新: 我只是注意到我至少错过了一个裁判数。这 var 不是 IntPtr, , 这是一个 IDataObject. 。这 as 演员会隐含 QueryInterface 并添加另一个裁判计数。虽然 GetObjectForIUnknown 返回一个RCW,该rcw被延迟到GC启动为止。您可能想在 using 块激活 IDisposable 在上面。

同时, STGMEDIUM 结构也有一个 IUnknown 指针您没有发布。你应该打电话 ReleaseStgMedium 正确处理整个结构,包括该指针。

我太累了,无法继续查看代码。我明天回来,尝试找到其他可能的裁判计数泄漏。同时,您检查了您正在调用的所有接口,结构和API的MSDN文档,并尝试找出您可能错过的任何其他裁判计数。

我知道这个问题很旧,但是由于这给我带来了麻烦,我觉得我需要分享对我有用的东西。

起初,我尝试使用伯纳德·达恩顿(Bernard Darnton)的答案:

var storagePointer = Marshal.GetIUnknownForObject(storage);
int refCount;
do
{
    refCount = Marshal.Release(storagePointer);
} while (refCount > 0);

但是,即使该解决方案起初有效,最终还是引起了一些抵押问题。

因此,在Franci Penov回答之后,我在代码中添加了以下内容:

            OLE32.ReleaseStgMedium(ref stgm);
            Marshal.Release(unknownForDataObj);
            Marshal.Release(unknownFromOle);
            Marshal.ReleaseComObject(storage);

我已经为发布com对象写了这篇文章:

public static void ReleaseComObjects(params object[] objects)
    {
        if (objects == null)
        {
            return;
        }

        foreach (var obj in objects)
        {
            if (obj != null)
            {
                try
                {
                    Marshal.FinalReleaseComObject(obj);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }
        }
    }

您将要在最后的语句中发布的对象传递,并“通过将其参考计数设置为0来释放所有引用到运行时可呼叫包装器(RCW)。”

如果您想发布最后创建的参考,但请保留之前创建的引用,则不合适。

它对我有用,没有内存泄漏。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top