إطلاق مقبض ملف OLE ISTorage في C#
سؤال
أحاول تضمين ملف 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."
لا يكون الأمر مناسبًا إذا كنت ترغب في إصدار آخر مرجع تم إنشاؤه ولكن احتفظ بالمراجع التي تم إنشاؤها من قبل.
لقد عملت بالنسبة لي بدون تسرب ذاكرة.