none
COM call from WndProc causes runtime error 8001010D RRS feed

  • Question

  • Hello all,
    Following scenario: I'm developing a .net 1.1 application in c# to burn CDs/DVDs via IMAPIv2.
    For some unimportant reason I'm stuck to .net 1.1 which makes the implementation pretty tricky anyways. But right now I'm having a pretty basic COM-architecture related problem. I've to react on events when CDs/DVDs are inserted or removed from the machine and then (on inserting) get media details (writable? rewritable? capacity? free sectors etc). For this purpose I use WndProc like shown in the following snippets:
     
    btw: is there any way to get these message without a window?

    Code Snippet

    using System;
    using System.Windows.Forms;
     
    namespace BurningTests
    {
     public delegate void WndProcDelegate(ref Message m);
     
     public class WndProcProvider : NativeWindow
     {
      public WndProcDelegate OnWndProc;
     
      public WndProcProvider(Form parent)
      {
       parent.HandleCreated += new EventHandler(this.OnHandleCreated);
       parent.HandleDestroyed += new EventHandler(this.OnHandleDestroyed);
      }
     
      internal void OnHandleCreated(object sender, EventArgs e)
      {
       AssignHandle(((Form)sender).Handle);
      }
     
      internal void OnHandleDestroyed(object sender, EventArgs e)
      {
       ReleaseHandle();
      }
     
      protected override void WndProc(ref Message m)
      {
       if(OnWndProc != null)
        OnWndProc(ref m);
     
       base.WndProc (ref m);
      }
     }
    }

     

    Code Snippet

    using System;
    using System.Data;
    using System.Globalization;
    using System.Text.RegularExpressions;
    using System.Threading;
    using System.Windows.Forms;
    using BurningTests;
    using IMAPI2.Interop;
     
    namespace XXX.IMAPI2BurningServices
    {
     public class BsBurner
     {
      private const int WM_DEVICECHANGE = 0x0219;
      private const int DBT_DEVICEARRIVAL = 0x8000;
      private const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
     
      public BsBurnerUpdateDelegate BurnerUpdate;
     
      // [...]
     
      private Form helperForm;
      private WndProcProvider wndProcProvider;
     
      private static Regex regexHResultExc;
     
      static BsBurner()
      {
       regexHResultExc = new Regex(".*HRESULT:.?0x([0-9A-F]*).*");
      }
     
      public BsBurner(Form helperForm)
      {
       this.helperForm = helperForm;
     
       if (helperForm != null)
       {
        wndProcProvider = new WndProcProvider(helperForm);
        wndProcProvider.OnWndProc += new WndProcDelegate(OnWndProc);
       }
      }
     
      ~BsBurner()
      {
       // [...]
      }
     
      private void OnWndProc(ref Message m)
      {
       if (Convert.ToInt32(m.Msg) == WM_DEVICECHANGE)
       {
        switch (m.WParam.ToInt32())
        {
         case (DBT_DEVICEARRIVAL):
          this.RefreshMediaInfo();
          break;
         case (DBT_DEVICEREMOVECOMPLETE):
          this.RefreshMediaInfo();
          break;
        }
       }
      }
     
      public void RefreshMediaInfo()
      {
       // here comes a hell lot of code with several COM calls to IMAPI
       // it fails on everyone with runtime error 8001010D when called
       // from WndProc
      }
     }
    }

     

    From runtime error 8001010D I conclude, that the WndProc approach of NativeWindow is realised through COM. So the occurrence of this runtime error is / would be logical. One ugly way to solve this would be to start a .net timer calling the RefreshMediaInfo method; but I definately prefer not to do this.
    I wonder if there is a way to get these events without using WndProc and in best case without using COM (whether using it directly or indirectly like in my example).
    And i wonder if it was possible to make COM calls while a synchronized COM call is not finished with some special locking or synchronization techniques (or even with clever workarounds).

     

     

    Tuesday, March 4, 2008 4:48 PM

Answers

  • Just in case someone stumbles over this problem:
    A quick and seemingly sufficient solution to this problem is to use a dummy lock object, do a lock in the WndProc like this:

    private void OnWndProc(ref Message m)
    {
      lock(lockObject)
      {
        // if(condition) { // start thread }
      }     
    }

    and do a lock before each COM call which may be done before the synchron COM call of WndProc is finished. In theory there is a little gap where you could do a COM call before the synchron COM call of WndProc is finished. But this approach worked/works fine for me and proved in a little stress test.

    Regards,  Jacob
    Wednesday, March 5, 2008 2:17 PM