سؤال

أحاول تضمين ملف PDF في مستند Word باستخدام تقنية OLE الموضحة هنا:http://blogs.msdn.com/brian_jones/archive/2009/07/21/embedding-any-file-type-like-pdf-in-an-open-xml-file.aspx

لقد حاولت تنفيذ رمز C ++ المقدم في C# بحيث يكون المشروع بأكمله في مكان واحد وأنا هناك تقريبًا باستثناء حاجز طريق واحد. عندما أحاول إطعام البيانات الثنائية الكائنات التي تم إنشاؤها في مستند Word ، أحصل على iOexception.

ioException: لا يمكن للعملية الوصول إلى الملف "C: anylay anhing.pdf.bin 'لأنه يتم استخدامه بواسطة عملية أخرى.

يوجد مقبض ملف افتح ملف .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: أنا لا أستخدم كتل "استخدام" لأن الأشياء التي أرغب في التخلص منها غير قابلة للتصرف. (ولكي أكون صادقًا تمامًا ، لست متأكدًا مما أحتاج إلى إصداره لإزالة مقبض الملف ، كونه لغة أجنبية بالنسبة لي.)

هل كانت مفيدة؟

المحلول 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 بواسطة GC ما لم يكن نوع .NET الذي يحمل النقاط المرجعية إلى غلاف RCW وسيقوم بإصدار عدد المرجع بشكل صحيح في Finalizer.

IntPtr ليس مثل هذا النوع و var أيضا IntPtr (من نوع العودة من Marshal.GetObjectForIUnknown دعوة) ، بحيث يجعل ثلاثة.

يجب أن تتصل Marshal.Release على كل ما تبذلونه منك IntPtr المتغيرات.

لست متأكدًا OLE32.IStorage. قد يحتاج هذا أيضًا Marshal.Release أو Marshal.ReleaseComPointer.

تحديث: لقد لاحظت للتو أنني فاتني عدد المرجع على الأقل. ال var ليس IntPtr, ، انه IDataObject. ال as سوف يقوم الممثلون بعمل ضمني QueryInterface وإضافة عدد آخر المرجع. رغم GetObjectForIUnknown إرجاع RCW ، يتم تأخير هذا واحد حتى يبدأ GC. قد ترغب في القيام بذلك في using كتلة لتفعيل IDisposable عليه.

وفي الوقت نفسه ، و STGMEDIUM الهيكل أيضا واحد IUnknown المؤشر الذي لا تطلقه. يجب أن تتصل ReleaseStgMedium للتخلص بشكل صحيح من البنية بأكملها ، بما في ذلك المؤشر.

أنا متعب جدًا لمواصلة البحث من خلال الكود الآن. سأعود غدًا وأحاول العثور على تسريبات أخرى محتملة للمرجع. في هذه الأثناء ، يمكنك التحقق من مستندات MSDN لجميع الواجهات والهياكل وواجهات برمجة التطبيقات التي تتصل بها وتحاول معرفة أي تعدادات مرجعية أخرى قد تكون قد فاتتك.

أعلم أن السؤال قديم ، لكن هذا تسبب لي بعض المتاعب التي أشعر أنني بحاجة لمشاركة ما نجح بالنسبة لي.

في البداية ، حاولت استخدام إجابة برنارد دارنتون الخاصة:

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

لقد كتبت هذا لإطلاق كائنات كوم:

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

تقوم بتمرير الكائنات التي تريد إصدارها على سبيل المثال في عبارة أخيرًا و "تصدر جميع الإشارات إلى غلاف وقت التشغيل القابل للاتصال (RCW) عن طريق تعيين عدد المرجع إلى 0."

لا يكون الأمر مناسبًا إذا كنت ترغب في إصدار آخر مرجع تم إنشاؤه ولكن احتفظ بالمراجع التي تم إنشاؤها من قبل.

لقد عملت بالنسبة لي بدون تسرب ذاكرة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top