Release OLE IStorage file descriptor in C #

I am trying to embed a PDF file into a Word document using the OLE technique described here: http://blogs.msdn.com/brian_jones/archive/2009/07/21/embedding-any-file-type-like-pdf-in-an -open-xml-file.aspx

I tried to implement the C ++ code presented in C #, so that the whole project is in one place and almost there, except for one checkpoint. When I try to pass the generated binary data of an OLE object to a Word document, I get an IOException.

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

There is a file descriptor that opens the .bin file ("oleOutputFileName" below), and I don't know how to get rid of it. I do not know a huge amount about COM - I cover it here - and I do not know where the file descriptor is located or how to release it.

This is what my C # code looks like. What am I missing?

    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);
    }

UPDATE 1: Clarified which file I meant by the “.bin file”.
UPDATE 2: I do not use the “use” of blocks because the things I want to get rid of are not disposable. (And, frankly, I'm not sure what I need to free to remove the file descriptor, COM is a foreign language for me.)

+3
source share
4 answers

, . (, - , COM, .)

, , :

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

refcount :

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, a IDataObject. as QueryInterface . GetObjectForIUnknown RCW, , GC . , using, IDisposable .

, STGMEDIUM IUnknown, . ReleaseStgMedium, , .

, . . MSDN , API-, , , , , .

+1

, , , , , .

:

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

, , , .

, , , :

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

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);
                }
            }
        }
    }

, , . finally, " Runtime Callable Wrapper (RCW), 0."

Not suitable if you want to free the last created link, but keep the previously created links.

It worked for me without memory leaks.

0
source

Source: https://habr.com/ru/post/1742214/


All Articles