DissonnectedContext MDA عند استدعاء وظائف WMI في تطبيق واحد متخلف

StackOverflow https://stackoverflow.com/questions/3921661

سؤال

أكتب تطبيقًا في C#، .NET 3.0 في VS2005 مع ميزة مراقبة الإدراج/الطرد لمختلف محركات الأقراص القابلة للإزالة (أقراص فلاش USB ، الأقراص المضغوطة ، إلخ). لم أكن أرغب في استخدام WMI ، لأنه قد يكون غامضًا في بعض الأحيان (على سبيل المثال ، يمكن أن يولد أحداث إدراج متعددة لمحرك أقراص USB واحد) ، لذلك قمت ببساطة بتجاوز wndproc من الشكل الرئيسي للقبض على رسالة WM_Devicechange ، كما هو مقترح هنا. بالأمس ، واجهت مشكلة عندما اتضح أنه سيتعين علي استخدام WMI على أي حال لاسترداد بعض تفاصيل القرص الغامضة مثل الرقم التسلسلي. اتضح أن استدعاء روتينات WMI من داخل WNDProc يلقي MDA غير المتوصيل.

بعد بعض الحفر انتهت بحل محرج لذلك. رمز على النحو التالي:

    // the function for calling WMI 
    private void GetDrives()
    {
        ManagementClass diskDriveClass = new ManagementClass("Win32_DiskDrive");
        // THIS is the line I get DisconnectedContext MDA on when it happens:
        ManagementObjectCollection diskDriveList = diskDriveClass.GetInstances();
        foreach (ManagementObject dsk in diskDriveList)
        {
            // ...
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        // here it works perfectly fine
        GetDrives();
    }


    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        if (m.Msg == WM_DEVICECHANGE)
        {
            // here it throws DisconnectedContext MDA 
            // (or RPC_E_WRONG_THREAD if MDA disabled)
            // GetDrives();
            // so the workaround:
            DelegateGetDrives gdi = new DelegateGetDrives(GetDrives);
            IAsyncResult result = gdi.BeginInvoke(null, "");
            gdi.EndInvoke(result);
        }
    }
    // for the workaround only
    public delegate void DelegateGetDrives();

وهو ما يعني بشكل أساسي تشغيل الإجراء المتعلق بـ WMI على مؤشر ترابط منفصل - ولكن بعد ذلك ، في انتظار إكماله.

الآن ، السؤال هو: لماذا هل يعمل ، و لماذا هل يجب أن يكون بهذه الطريقة؟ (أو ، هل هو؟)

لا أفهم حقيقة الحصول على مفصل MDA أو RPC_E_WRONG_THREAD في المقام الأول. كيف الجري GetDrives() الإجراء من زر انقر فوق معالج الأحداث يختلف عن الاتصال به من wndproc؟ ألا تحدث في نفس الموضوع الرئيسي لتطبيقي؟ راجع للشغل ، تطبيقي متمرس بالكامل ، فلماذا فجأة خطأ يشير إلى بعض "موضوع خاطئ"؟ هل يستخدم استخدام WMI متعددة الترابطات والعلاج الخاص للوظائف من النظام. الإدارة؟

في غضون ذلك ، وجدت سؤالًا آخر يتعلق بهذا MDA ، إنه كذلك هنا. حسنًا ، يمكنني أن أعتبر أن استدعاء WMI يعني إنشاء مؤشر ترابط منفصل لمكون COM الأساسي-لكنه لا يزال لا يحدث لي لماذا لا توجد حاجة إلى عدم السحرية عند الاتصال به بعد الضغط على زر ويتم حاجة إلى سحر عند الاتصال من wndproc.

أنا في حيرة من أمري بشأن ذلك وسأقدر بعض التوضيح في هذا الشأن. لا يوجد سوى عدد قليل من الأشياء الأسوأ من وجود حل وعدم معرفة سبب عمله:/

هتاف ، ألكساندر

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

المحلول

هناك مناقشة طويلة إلى حد ما لشقق كوم وضخ الرسائل هنا. لكن النقطة الرئيسية في الاهتمام هي استخدام مضخة الرسائل لضمان تنظيم المكالمات في STA بشكل صحيح. نظرًا لأن موضوع واجهة المستخدم هو STA المعني ، فسيحتاج الرسائل إلى ضخ لضمان أن كل شيء يعمل بشكل صحيح.

يمكن بالفعل إرسال رسالة WM_DeviceChange إلى النافذة عدة مرات. لذلك في الحالة التي تتصل فيها GetDrives مباشرة ، ينتهي بك الأمر بفعالية مع المكالمات العودية. ضع نقطة استراحة على مكالمة GetDrives ثم قم بتوصيل جهاز لإطلاق الحدث.

في المرة الأولى التي تضغط فيها على نقطة الاستراحة ، كل شيء في حالة جيدة. الآن اضغط على F5 للمتابعة وستضرب نقطة الاستراحة مرة ثانية. هذه المرة مكدس المكالمات مثل:

في نوم ، انتظر ، أو انضم إلى] deletemewindowsforms.exe! deletemewindowsforms.form1.wndproc (ref system.windows.forms.message m) line 46 c# system.windows.forms.dll! .onmessage (ref system.windows.forms.message M) + 0x13 بايت
System.windows.forms.dll! system.windows.forms.control.controlnativeWindow.wndproc (ref system.windows.forms.message m) + 0x31 bytes
System.windows.forms.dll! system.windows.forms.nativeWindow.debuggableCallback (System.Intptr hwnd ، int msg ، system.intptr wparam ، system.intptr lparam) + 0x64 bytes [الأصلي للانتقال المدارة
تمكنت من الانتقال الأصلي
mscorlib.dll! System.Threading.Waithandle.InternalWaitone (System.Runtime.InterOpservices.SafeHandle Waitableafehandle ، millisecondstimeout long ، bool hasthreadaffifaff ، exitcontext) + 0x2b bytes mscorlib.dll! ) + 0x2d بايت
mscorlib.dll! system.threading.waithandle.waitone () + 0x10 bytes system.management.dll! system.management.mtahelper.createinmta (System.Type type) + 0x17b bytes
System.Management.dll! system.management.management.createwbempath (مسار السلسلة) + 0x18 بايت System.Management.dll! System.Management.ManagementClass.ManagementClass
deletemewindowsforms.exe! deletemewindowsforms.form1.getDrives () السطر 23 + 0x1B بايت C#

بشكل فعال يتم ضخ رسائل النافذة لضمان حشد مكالمات COM بشكل صحيح ، ولكن هذا له تأثير جانبي على استدعاء wndproc الخاص بك والاستعماء مرة أخرى (حيث توجد رسائل WM_DeviceChange معلقة) بينما لا يزال في مكالمة GetDrives السابقة. عند استخدام BeginInvoke ، يمكنك إزالة هذه المكالمة العودية.

مرة أخرى ، ضع نقطة توقف على مكالمة GetDrives واضغط على F5 بعد أول مرة. في المرة القادمة ، انتظر ثانية أو ثانيين ثم اضغط F5 مرة أخرى. في بعض الأحيان سيفشل ، في بعض الأحيان لن تفعل ذلك وستضرب نقطة التوقف مرة أخرى. هذه المرة ، ستتضمن Callstack ثلاث مكالمات للحصول على GetDrives ، مع آخر مرة تسبب فيها تعداد مجموعة Diskdrivelist. لأنه مرة أخرى ، يتم ضخ الرسائل لضمان تنظيم المكالمات.

من الصعب تحديد سبب بالضبط MDA يتم تشغيله ، ولكن بالنظر إلى المكالمات العودية ، من المعقول افتراض أن سياق COM قد يتم هدمه قبل الأوان و/أو يتم جمع كائن قبل إصدار كائن COM الأساسي.

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