Frage

Ich versuche einbetten PDF-Datei in ein Word-Dokument mit der OLE-Technik, die hier beschrieben: http://blogs.msdn.com/brian_jones/archive/2009/07/21/embedding-any-file-type-like-pdf-in-an-open-xml-file.aspx

Ich habe versucht, den C ++ Code in C # zur Verfügung gestellt zu implementieren, so dass das gesamte Projekt an einem Ort ist und ist fast da mit Ausnahme einer Straßensperre. Wenn ich versuche, die generierten OLE-Objekt Binärdaten in das Word-Dokument zu füttern erhalte ich eine IOException.

  

IOException: Der Prozess kann nicht auf die Datei zugreifen ‚C: \ Wo \ Whatever.pdf.bin‘., Weil sie von einem anderen Prozess verwendet wird

Es ist eine Datei-Handelte die .bin-Datei öffnen ( „oleOutputFileName“ weiter unten) und ich weiß nicht, wie man es loswerden. Ich weiß nicht, eine ganze Menge über COM - Ich bin hier alata -. Und ich weiß nicht, wo die Datei-Handle ist oder wie sie lösen

Hier ist, was meine C # -ised Code wie folgt aussieht. Was bin ich?

    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: Geklärte, welche Datei ich durch "die .bin-Datei" gemeint
. UPDATE 2: Ich bin nicht mit „mit“ Blöcken, weil die Dinge, die ich will, um loszuwerden, ist nicht Einweg. (Und um ehrlich zu sein bin ich nicht sicher, was ich loslassen müssen, um die Datei-Handle zu entfernen, COM, eine Fremdsprache zu mir zu sein.)

War es hilfreich?

Lösung 2

fand ich die Antwort und es ist ziemlich einfach. (Wahrscheinlich zu einfach -. Es fühlt sich an wie ein Hack, aber da ich weiß so wenig über COM-Programmierung Ich werde einfach mit ihm gehen)

Das Speicherobjekt hatte mehrere Verweise auf sie, so einfach weiter gehen, bis sie alle weg sind:

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

Andere Tipps

Ich sehe mindestens vier potentielle refcount Lecks in Ihrem Code:

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

Beachten Sie, dass alle diese Zeiger sind auf COM-Objekte. COM-Objekte werden nicht von GC, es sei denn der .NET-Typ gesammelt, die die Referenz auf ein RCW-Wrapper hält und richtig lösen seine Referenzzähler in seiner Finalizerthread.

IntPtr ist nicht diese Art und Ihre var auch ist IntPtr (aus dem Rückgabetyp des Marshal.GetObjectForIUnknown Anrufs), so dass drei macht.

Sie sollten anrufen Marshal.Release auf alle Ihre IntPtr Variablen.

Ich bin nicht sicher über OLE32.IStorage. Dies könnte man braucht entweder Marshal.Release oder Marshal.ReleaseComPointer .

Update: Ich habe gerade bemerkt, dass ich zumindest einen Verweiszähler verpasst. Die var ist kein IntPtr, es ist ein IDataObject. Die as Besetzung wird eine implizite QueryInterface tun und einen anderen Verweiszähler hinzu. Obwohl GetObjectForIUnknown ein RCW zurückgibt, wird dieses in bis in die GC-Kicks verzögert. Sie könnten dies in using Block tun möchten die IDisposable auf sie zu aktivieren.

Inzwischen hat die STGMEDIUM Struktur hat auch einen IUnknown Zeiger Sie nicht veröffentlichen. Sie sollten rufen Sie ReleaseStgMedium ordnungsgemäß von der entsorgen ganz Struktur, einschließlich diesen Zeiger.

Ich bin zu müde, gerade jetzt der Code weiterhin die Suche durch. Ich werde morgen zurückkommen und versuchen, andere mögliche ref Zählung Lecks zu finden. Inzwischen Sie die MSDN-Dokumentation überprüfen für alle Schnittstellen, Strukturen und APIs, die Sie anrufen und versuchen, herauszufinden, alle anderen ref zählt Sie vielleicht verpasst haben.

Ich weiß, die Frage ist alt, aber da dies mir einige Probleme verursacht hat, ich glaube, ich brauche zu teilen, was für mich gearbeitet hat.

Zuerst habe ich versucht Bernard Darnton eigene Antwort zu verwenden:

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

Doch obwohl die Lösung zuerst gearbeitet, es endete verursacht einige Sicherheiten Probleme.

Also, folgende Franci Penov Antwort, fügte ich hinzu, was mit dem Code folgt:

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

Ich habe dies geschrieben für die Freigabe von COM-Objekten:

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

Sie übergeben die Objekte, die Sie wollen, zum Beispiel lösen in einer finally-Anweisung und es „gibt alle Verweise auf eine Runtime Callable Wrapper (RCW) von den Referenzzähler auf 0 setzen.“

Es ist nicht geeignet, wenn Sie die zuletzt erstellte Referenz freigeben wollen aber behalten Referenzen erstellt vor.

Es ist für mich ohne Speicherlecks gearbeitet.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top