Tech Off Thread

16 posts

WPF + WindowsFormsHost + .Net Forms + ( COM interfaces + Forms Invoke ) = E_NOINTERFACE Exception

Back to Forum: Tech Off
  • User profile image
    pompomJuice

    Hi there.

    I really need help from someone who knows the ins and outs of Microsoft development. I am "porting" a .Net Winforms app written for Windows Mobile into WPF. This winforms app is actually a navigaion application, similar to tom-tom or Garmen navigation that we have come to know and love.  I have in my posession the WinForms app, plus the COM library it requires to do its work. The COM library is compiled to x86 if someone was wondering, I have both x86 and ARMv4 version of it. BUT I do not have the source code of the COM object.

    Ok so what do we have? Exceptions for Africa as usual:

    Unable to cast COM object of type 'NETSMMobile.SM3DDisplayTransformationClass' to interface type 'NETSMMobile.I3DDisplayTransformationSetup'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{8D724E8D-E00A-4700-9722-009901156857}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

    Now, this looks like a normal coding error, but SM3DDisplayTransformationClass implements I3DDisplayTransformationSetup. I have checked and double checked. So how can this happen then. Well, somehow WinForms' Invoke causes the "cast" to fail. I say "cast" because there is no real cast going on there. Anywais lets say there is a "cast", implicid maybe, the cast works "outside" of the invoke, but not inside. Here is the code I used to test this:

    private void Instance_MapLoaded(object sender, NETSMMobile.esriSMRendererLoadStatus eStatus)
    {
       // Here I injected my little test line to cause an exception. The real exception actually happned in the else statement below.
       double d = MapObject.Instance.MapView.RotationAngle; //This line throws the exception but only if the method was called inside the Invoke below.  Look for MapView's explenation below.
       if (m_bDisposed)
          return;
       if (InvokeRequired)
          Invoke(new MapLoadedEventHandler(Instance_MapLoaded), new Object[] { sender, eStatus });
       else
       {
           //Where the exception actually fires, inside here somewhere.
       }
    }
    


    MapView implements IMapView that exposes the RotationAngle Property. The MapView implementation redirects the call to a COM interface object hosted inside MapView:

    class MapView : IMapView
    {
       //Our COM interface
       private I3DDisplayTransformationSetup m_objTransformation;
    
       public double RotationAngle
       {
          get{ return m_objTransformation.RotationAngle; }
       }
    }
    
    // Generated by Microsoft (R) .NET Framework Type Library to Assembly Converter 3.5.30729.1
    [TypeLibType( 2 )]
    [ClassInterface( 0 )]
    [Guid( "36FAAB60-7424-4DF6-8DB6-B9931DF3EB26" )]
    public class SM3DDisplayTransformationClass : SM3DDisplayTransformation, I3DDisplayTransformationSetup, IDisplayTransformation
    {
       [DispId( 2 )]
       public virtual double RotationAngle { get; set; }
    }
    
    [InterfaceType( 1 )]
    [Guid( "8D724E8D-E00A-4700-9722-009901156857" )]
    public interface I3DDisplayTransformationSetup
    {
       [DispId( 2 )]
       double RotationAngle { get; set; }
    }
    
    


    There, that should be enough to demonstrate the link to COM from that call that causes the exception. Now I have tried to guess things such as spamming [STAThread] and things like that but I am getting nowhere. I fear I have to little knowledge of COM and .Net->COM and to study these things might take 100 years, which I don’t have .

    To add, I have tested that I registered the COM object on my BOX. If I unregistered it the code would fail much earlier with proper error basically saying that the COM interface does not exist.

    I also tried referencing the COM object directly, same error.

    I also generated my own .NET assembly from the COM object using that tlbimp.exe, but  same results.

    One more thing, if you compile the original Forms project and deploy it to a Windows mobile device, it would work. So I fear the problem lies in the port, or differences between WinForms on Compact Framework and full .Net.

    Please help someone, anyone.

    Regards.

  • User profile image
    pompomJuice

    Oh yea, I forgot to mention: 

    m_objTransformation is actually pointing to an instance of a SM3DDisplayTransformationClass object. That is why the exception is complaining about casing from SM3DDisplayTransformationClass to I3DDisplayTransformationSetup. This is legal though as 
    SM3DDisplayTransformationClass implements I3DDisplayTransformationSetup, or the compiler would have moaned Wink SM3DDisplayTransformationClass by law must always be able to cast to one of its interfaces ,surely!

    Regards

  • User profile image
    Dexter

    pompomJuice said:
    Oh yea, I forgot to mention: 

    m_objTransformation is actually pointing to an instance of a SM3DDisplayTransformationClass object. That is why the exception is complaining about casing from SM3DDisplayTransformationClass to I3DDisplayTransformationSetup. This is legal though as 
    SM3DDisplayTransformationClass implements I3DDisplayTransformationSetup, or the compiler would have moaned Wink SM3DDisplayTransformationClass by law must always be able to cast to one of its interfaces ,surely!

    Regards
    So if I understand correctly this code from the Invoke method:

    double d = MapObject.Instance.MapView.RotationAngle; //This line throws the exception but only if the method was called inside the Invoke below.  Look for MapView's explenation below.
       if (m_bDisposed)
          return;

    involves the COM object somehow but it is executed on a different thread than the main thread (where I assume the COM object was created)?

    Then that's a possible cause of your problems. Accesing STA COM objects from a different thread involves marshaling/proxying. If that cannot be done for the requested interface the error manifests as if there was no interface at all.

    Try moving that code in the "else" block of your Instance_MapLoaded method and see if it works.

  • User profile image
    pompomJuice

    Dexter said:
    pompomJuice said:
    *snip*
    So if I understand correctly this code from the Invoke method:

    double d = MapObject.Instance.MapView.RotationAngle; //This line throws the exception but only if the method was called inside the Invoke below.  Look for MapView's explenation below.
       if (m_bDisposed)
          return;

    involves the COM object somehow but it is executed on a different thread than the main thread (where I assume the COM object was created)?

    Then that's a possible cause of your problems. Accesing STA COM objects from a different thread involves marshaling/proxying. If that cannot be done for the requested interface the error manifests as if there was no interface at all.

    Try moving that code in the "else" block of your Instance_MapLoaded method and see if it works.

    Indeed!

     

    The COM object was indeed created in that thread, which was actually a worker thread. That explains why I could access that interface before the invoke, but not after. I say this because MapObject.Instance was created outside of that worker thread, which would indicate that its contained COM interfaces were also created outside of the worker thread. But not in this case, that interface was created in the worker thread as you predicted!

     

    What is interesting is that I just went to test what happens on a Windows CE device, as the code runs there. I set up the same test and debugging the app while running in CE seems to have no problem with accessing COM objects between threads. So there I think we can learn something, that sometimes things are not so portable as you think when working with COM. The original implementer never got hit with the problem of marshalling between threads, so he clearly just fired away. 

     

    I might be able to fix this instance, but I get a sneaky suspicion the entire program is going to be riddled with this issue. I am not looking forward to this.

     You mention STA Com objects, can I instantiate them in a way that they are not single thread apartment objects? Which would mean I assume I can access them from multiple "apartments"? I am guessing here, I lack theory.

    Regards,

     

    PS- I will probably check out marshalling/proxy you mentioned, but I have never done that before so. We never get a break in this world of computers ;'(

     

  • User profile image
    Dexter

    pompomJuice said:
    Dexter said:
    *snip*

    Indeed!

     

    The COM object was indeed created in that thread, which was actually a worker thread. That explains why I could access that interface before the invoke, but not after. I say this because MapObject.Instance was created outside of that worker thread, which would indicate that its contained COM interfaces were also created outside of the worker thread. But not in this case, that interface was created in the worker thread as you predicted!

     

    What is interesting is that I just went to test what happens on a Windows CE device, as the code runs there. I set up the same test and debugging the app while running in CE seems to have no problem with accessing COM objects between threads. So there I think we can learn something, that sometimes things are not so portable as you think when working with COM. The original implementer never got hit with the problem of marshalling between threads, so he clearly just fired away. 

     

    I might be able to fix this instance, but I get a sneaky suspicion the entire program is going to be riddled with this issue. I am not looking forward to this.

     You mention STA Com objects, can I instantiate them in a way that they are not single thread apartment objects? Which would mean I assume I can access them from multiple "apartments"? I am guessing here, I lack theory.

    Regards,

     

    PS- I will probably check out marshalling/proxy you mentioned, but I have never done that before so. We never get a break in this world of computers ;'(

     

    On Windows CE it probably works because the "CE" COM does not implement synchronization, all COM objects are free threaded (the client code has the reponsability to synchronize access to a COM object).

    Now I'm not sure why that object doesn't have a proxy registered. One possibility is that it is actually a free threaded COM object and it doesn't have a proxy because that's not needed (I assume it's only used as an inproc server). However, if the object is created on the main UI thread it will be subject to apartment threading rules.

    You'll have to figure out what threading model the COM object supports (see its registration key in HKCR\CLSID\{guid}\InProcServer32). If it's indeed free threaded you could try to create the object on a different thread than the main UI thread.

  • User profile image
    figuerres

    pompomJuice said:
    Oh yea, I forgot to mention: 

    m_objTransformation is actually pointing to an instance of a SM3DDisplayTransformationClass object. That is why the exception is complaining about casing from SM3DDisplayTransformationClass to I3DDisplayTransformationSetup. This is legal though as 
    SM3DDisplayTransformationClass implements I3DDisplayTransformationSetup, or the compiler would have moaned Wink SM3DDisplayTransformationClass by law must always be able to cast to one of its interfaces ,surely!

    Regards
    there are more than a few differences between CE and desktop windows OS stuff so I would really look to see if that code can be found or if a native win32 version of the dll can be found.

    you might (depends on the rest of the code in it) find other problems if it was written for CE and never tested on a desktop before.

    Hope it goes well and that I am just adding a caution note.

    WIth .Net CF 1.xx we always say "Net CF is just a thin wapper around SystemNotImplimentedExeption"  NetCF 3.5 is WAY better than 1.x
    Smiley

  • User profile image
    pompomJuice

    Dexter said:
    pompomJuice said:
    *snip*
    On Windows CE it probably works because the "CE" COM does not implement synchronization, all COM objects are free threaded (the client code has the reponsability to synchronize access to a COM object).

    Now I'm not sure why that object doesn't have a proxy registered. One possibility is that it is actually a free threaded COM object and it doesn't have a proxy because that's not needed (I assume it's only used as an inproc server). However, if the object is created on the main UI thread it will be subject to apartment threading rules.

    You'll have to figure out what threading model the COM object supports (see its registration key in HKCR\CLSID\{guid}\InProcServer32). If it's indeed free threaded you could try to create the object on a different thread than the main UI thread.

    Thanks Dexter.

    Firstly, on a by note, anyone that uses Chrome with channel9, DONT it "aw snaps" when you copy and paste to it. I think I have lost about 5 posts like this, 3 of them the main post. I almost threw my laptop out of the window. I am back on exploder for now Wink How many times do I have to realize that you only use Microsoft with Microsoft? Anything else is folly. ( I come from Linux, forced to convert to Microsoft recently because of all the hype Microsoft creates around their stuff. MS is bullying Linux IMO and must stop it because my boss is  influenced by snazzy Microsoft posters, not actually working operating systems; In the end I sit with the mess)

    Ok so I hear you on the CE. Makes sense. I have done more reading on Marshalling and COM and came across this interesting article which you dont need to read, but maybe for someone else that knows as little as me. 

    http://www.ddj.com/windows/184416838

    I like that article. This makes me feel better because I was debugging till early morning hours as he states discovering multithreading issues. I felt like a chop.

    Ok so Dobbs showed me that Marshalling is just crazy and I opted to go with your initial advice to inspect those "else" code blocks more carefully. You said maybe I can take out those COM calls. Indeed!!! You were right. I removed the invokes completely and just tediously invoked every little UI change inside of that "else" code block. SUCCESS!!! Thanks.

    Ok but now further down the line I just hit another Exception. Sigh, COM object that has been separated from its underlying RCW cannot be used.

    I have not yet investigated it, first I go to the gym to pump out some frustrations then I come back for round two. So far all I have figured out is that RCW stands for Runtime Callable Wrapper. Quick random spamming of STAThread and MTAThread has show me that this error actually responds to those modes. the MTAThread give that same interface cast exception above, but STAThread now gives the RCW disappearing error.

    Any quick tips will be greatly appreciated. So far you have helped a lot.

    Thanks

    Werner

  • User profile image
    pompomJuice

    figuerres said:
    pompomJuice said:
    *snip*
    there are more than a few differences between CE and desktop windows OS stuff so I would really look to see if that code can be found or if a native win32 version of the dll can be found.

    you might (depends on the rest of the code in it) find other problems if it was written for CE and never tested on a desktop before.

    Hope it goes well and that I am just adding a caution note.

    WIth .Net CF 1.xx we always say "Net CF is just a thin wapper around SystemNotImplimentedExeption"  NetCF 3.5 is WAY better than 1.x
    Smiley

    I hear ya.

    See my boss don't care about that. Microsoft says it can be done, I get payed big bucks, so I must get it done. There is no please explain to boss man that things are always more complicated than a sticker on a box that says WPF hosts Forms apllications.

    Interestingly the app contains very little WindowsCE spesific parts. It seems that it was mostly developed as a WinForms app in .Net 2.0, deployed to a CF. Worked kinda well.

    To reiterate, I have the Winforms project that compiles and deployes to windows CE device. I have tested this on my phone and it worked. The code ported fairly easy to my WPF app, compiling whise. Runtime though I get HUGE issues. I thought .NET C# code was just intended so that if it compiles it will run on different platforms. But I know it is never that easy.

    LOL @ SystemNotImplimentedExeption. Luckaly for me the app was developed for CE thus porting it up to Full framework is much easier than the other way around which would basically be a SystemNotImplimentedExeption wrapper.

    W

  • User profile image
    Dexter

    pompomJuice said:
    Dexter said:
    *snip*

    Thanks Dexter.

    Firstly, on a by note, anyone that uses Chrome with channel9, DONT it "aw snaps" when you copy and paste to it. I think I have lost about 5 posts like this, 3 of them the main post. I almost threw my laptop out of the window. I am back on exploder for now Wink How many times do I have to realize that you only use Microsoft with Microsoft? Anything else is folly. ( I come from Linux, forced to convert to Microsoft recently because of all the hype Microsoft creates around their stuff. MS is bullying Linux IMO and must stop it because my boss is  influenced by snazzy Microsoft posters, not actually working operating systems; In the end I sit with the mess)

    Ok so I hear you on the CE. Makes sense. I have done more reading on Marshalling and COM and came across this interesting article which you dont need to read, but maybe for someone else that knows as little as me. 

    http://www.ddj.com/windows/184416838

    I like that article. This makes me feel better because I was debugging till early morning hours as he states discovering multithreading issues. I felt like a chop.

    Ok so Dobbs showed me that Marshalling is just crazy and I opted to go with your initial advice to inspect those "else" code blocks more carefully. You said maybe I can take out those COM calls. Indeed!!! You were right. I removed the invokes completely and just tediously invoked every little UI change inside of that "else" code block. SUCCESS!!! Thanks.

    Ok but now further down the line I just hit another Exception. Sigh, COM object that has been separated from its underlying RCW cannot be used.

    I have not yet investigated it, first I go to the gym to pump out some frustrations then I come back for round two. So far all I have figured out is that RCW stands for Runtime Callable Wrapper. Quick random spamming of STAThread and MTAThread has show me that this error actually responds to those modes. the MTAThread give that same interface cast exception above, but STAThread now gives the RCW disappearing error.

    Any quick tips will be greatly appreciated. So far you have helped a lot.

    Thanks

    Werner

    I should have been more explicit about proxy/marshaling. Marshaling is something you don't need to be concerned about in .NET  because the runtime will do that automatically. What the Dr. Dobb's article says only applies to the unmanaged world.

    Of course, that brings the question why this doesn't work if it's done automatically. That's because this marshaling requires that the component (or more exactly the interface) has a registered proxy. So your component is either not registered correctly or it doesn't have a proxy at all.

    As for the RCW error: I've never seen that before. Could you post a stack trace or at least the exact line that causes that?

    PS: you still need to figure out if your COM object is apartment threaded or free threaded Smiley

  • User profile image
    pompomJuice

    Dexter said:
    pompomJuice said:
    *snip*
    I should have been more explicit about proxy/marshaling. Marshaling is something you don't need to be concerned about in .NET  because the runtime will do that automatically. What the Dr. Dobb's article says only applies to the unmanaged world.

    Of course, that brings the question why this doesn't work if it's done automatically. That's because this marshaling requires that the component (or more exactly the interface) has a registered proxy. So your component is either not registered correctly or it doesn't have a proxy at all.

    As for the RCW error: I've never seen that before. Could you post a stack trace or at least the exact line that causes that?

    PS: you still need to figure out if your COM object is apartment threaded or free threaded Smiley

    Thanks for that information.

    I have mailed the original authors of those COM objects for those questions.

    Also thanks for pointing out my miss interpretation of Dr. Dobbs. I also wondered about that, since he never mentions .Net in the article itself, only the heading. I think the heading might be wrong.

    All other research I did also pointed out that everything should just work. Oh well, kind of fixed that problem by removing them out of the invokes.

    Now the other error: I have checked it out for about 5 minutes. It looks less involved. There error seems to happen all on its own in one method call. Here it is:

     
    public void SetClientRect(Rectangle rcClient) // exception
    {
       if (m_objClientEnvelope == null)
          m_objClientEnvelope = new SMEnvelopeClass(); // The COM object
      m_objClientEnvelope.XMin = rcClient.Left;// <---- EXCEPTION happens here on m_objClientEnvelope
      m_objClientEnvelope.XMax = rcClient.Right;
      m_objClientEnvelope.YMin = rcClient.Top;
      m_objClientEnvelope.YMax = rcClient.Bottom;
      ((IDisplayTransformation)m_objTransformation).ClientRect = m_objClientEnvelope; // exception
    }
    
    //The interface generated by that Assembly generator
    [Guid( "C3D7F3E5-28D7-407F-BBED-CF8F2222BE35" )]
    [TypeLibType( 2 )]
    [ClassInterface( 0 )]
    public class SMEnvelopeClass : SMEnvelope, IEnvelope
    {
       public SMEnvelopeClass();
      [DispId( 4 )]
      public virtual double XMax { get; set; }
      [DispId( 3 )]
      public virtual double XMin { get; set; }
      [DispId( 2 )]
      public virtual double YMax { get; set; }
      [DispId( 1 )]
      public virtual double YMin { get; set; }
    }
    


    Ok so again I think its a threading problem because that method instantiates m_objClientEnvelope the first time it is called. I injected a thread id call there and it is clear that if the creating thread accesses that member it works, if the thread ID changes it fails with that error. I think its a different kind of error for the same problem, those threads are not part of the same apartment and the marshalling between the apartments ( or thread and apartment? ) is not working? I am guessing here, my COM knowledge is a day old. Baby steps.

    I think with the previous error I got lucky. A worker thread instantiated the COM object and used it inside that worker thread. Because I eliminated the other thread created by invoke I sidestepped that problem.

    The new error occurs because two worker threads ( that are essential to the program ) are trying to access the same COM object. This time trickery might not work. Sigh.... I am out of ideas here.

    With regards to your comment "So your component is either not registered correctly or it doesn't have a proxy at all." Basically what I did was regsvr32 smmobile.dll and tlbimp smmobile.dll /out:NETSMMobile.dll and referenced that NETSMMobile.dll in my devStudio. I read about that proxy/stub file and I have no idea if that information was "imbedded" within the COM object itself. But I don’t have that proxy dll file that they speak of.

    Thanks for the help.

    Regards,

     

  • User profile image
    pompomJuice

    pompomJuice said:
    Dexter said:
    *snip*

    Thanks for that information.

    I have mailed the original authors of those COM objects for those questions.

    Also thanks for pointing out my miss interpretation of Dr. Dobbs. I also wondered about that, since he never mentions .Net in the article itself, only the heading. I think the heading might be wrong.

    All other research I did also pointed out that everything should just work. Oh well, kind of fixed that problem by removing them out of the invokes.

    Now the other error: I have checked it out for about 5 minutes. It looks less involved. There error seems to happen all on its own in one method call. Here it is:

     
    public void SetClientRect(Rectangle rcClient) // exception
    {
       if (m_objClientEnvelope == null)
          m_objClientEnvelope = new SMEnvelopeClass(); // The COM object
      m_objClientEnvelope.XMin = rcClient.Left;// <---- EXCEPTION happens here on m_objClientEnvelope
      m_objClientEnvelope.XMax = rcClient.Right;
      m_objClientEnvelope.YMin = rcClient.Top;
      m_objClientEnvelope.YMax = rcClient.Bottom;
      ((IDisplayTransformation)m_objTransformation).ClientRect = m_objClientEnvelope; // exception
    }
    
    //The interface generated by that Assembly generator
    [Guid( "C3D7F3E5-28D7-407F-BBED-CF8F2222BE35" )]
    [TypeLibType( 2 )]
    [ClassInterface( 0 )]
    public class SMEnvelopeClass : SMEnvelope, IEnvelope
    {
       public SMEnvelopeClass();
      [DispId( 4 )]
      public virtual double XMax { get; set; }
      [DispId( 3 )]
      public virtual double XMin { get; set; }
      [DispId( 2 )]
      public virtual double YMax { get; set; }
      [DispId( 1 )]
      public virtual double YMin { get; set; }
    }
    


    Ok so again I think its a threading problem because that method instantiates m_objClientEnvelope the first time it is called. I injected a thread id call there and it is clear that if the creating thread accesses that member it works, if the thread ID changes it fails with that error. I think its a different kind of error for the same problem, those threads are not part of the same apartment and the marshalling between the apartments ( or thread and apartment? ) is not working? I am guessing here, my COM knowledge is a day old. Baby steps.

    I think with the previous error I got lucky. A worker thread instantiated the COM object and used it inside that worker thread. Because I eliminated the other thread created by invoke I sidestepped that problem.

    The new error occurs because two worker threads ( that are essential to the program ) are trying to access the same COM object. This time trickery might not work. Sigh.... I am out of ideas here.

    With regards to your comment "So your component is either not registered correctly or it doesn't have a proxy at all." Basically what I did was regsvr32 smmobile.dll and tlbimp smmobile.dll /out:NETSMMobile.dll and referenced that NETSMMobile.dll in my devStudio. I read about that proxy/stub file and I have no idea if that information was "imbedded" within the COM object itself. But I don’t have that proxy dll file that they speak of.

    Thanks for the help.

    Regards,

     

    Update:

    Ok I have fixed the errors. It seems that I needed to pay closer attention when fiddeling with STAThread and MTAThread attributes.

    The errors go away setting the creation/access threads to MTA, but ensuring all threads accessing the COM objects are MTA is tedious, as any events (OnMouseclick for instance) triggered from STA (Forms UI) required me to make worker threads that are MTA to do the work using COM interfaces. These MTA worker threads make calls to Form UI again so again more wrappers for invoke were required.

    So the original problem described at the top was caused by the fact that Control.Invoke spawned a STAThread, which  caused that E_NOINTERFACE error. Therefor the mistake was made by me, or the programmer not making sure that all COM access originated from MTAThread. The original implementation onto Windows CE Masked this problem as windows CE does not seem to have a problem with this.

    The port is compete, no exceptions, but the hackinng required to make it work must have broken something. Normal controls work fine but the map drawing failes for some reason. At least I understand the COM now.

    Thanks

  • User profile image
    Deactivated User

    Comment removed at user's request.

  • User profile image
    pompomJuice

    dotnetisv said:
    pompomJuice said:
    *snip*
    Hi guys, 

    DISCLAIMER:
    for the record please accept my appology for rudely interupting to ask a pretty basic question the answer to which (of course)  may reveal my ignorance.

    If you are porting to WPF, as Dexter mentioned, mostly this kind of thing is handled by the CLR, why have you not optioned to just use COM Interop and WRAP your unmanaged code in a managed class? I may be wrong, however I think this might give you the added isolation you require.

    Interested in your replies.

    -James
    www.dotnetisv.com
    Actually, we are already doing this.

    As I loosly mentioned before, there are two ways to "use" legacy COM interfaces in .Net. This article describes them best, with PRO's and CON's. 

    http://msdn.microsoft.com/en-us/library/ms973800.aspx


    I edited my post above yours, explaining the solution to my original problem.

    Regards,

  • User profile image
    Dexter

    pompomJuice said:
    pompomJuice said:
    *snip*
    Update:

    Ok I have fixed the errors. It seems that I needed to pay closer attention when fiddeling with STAThread and MTAThread attributes.

    The errors go away setting the creation/access threads to MTA, but ensuring all threads accessing the COM objects are MTA is tedious, as any events (OnMouseclick for instance) triggered from STA (Forms UI) required me to make worker threads that are MTA to do the work using COM interfaces. These MTA worker threads make calls to Form UI again so again more wrappers for invoke were required.

    So the original problem described at the top was caused by the fact that Control.Invoke spawned a STAThread, which  caused that E_NOINTERFACE error. Therefor the mistake was made by me, or the programmer not making sure that all COM access originated from MTAThread. The original implementation onto Windows CE Masked this problem as windows CE does not seem to have a problem with this.

    The port is compete, no exceptions, but the hackinng required to make it work must have broken something. Normal controls work fine but the map drawing failes for some reason. At least I understand the COM now.

    Thanks
    A correction:

    Control.Invoke does not spawn a new thread. Control.Invoke does some "magic" that causes the "invoked" code to execute on the thread that created the control (tipically the main thread).

    And a note:

    I'm not sure what was your solution after all. Creating a new thread every time you need to call into the COM component? That's scary! Smiley

  • User profile image
    Deactivated User

    Comment removed at user's request.

  • User profile image
    pompomJuice

    Dexter said:
    pompomJuice said:
    *snip*
    A correction:

    Control.Invoke does not spawn a new thread. Control.Invoke does some "magic" that causes the "invoked" code to execute on the thread that created the control (tipically the main thread).

    And a note:

    I'm not sure what was your solution after all. Creating a new thread every time you need to call into the COM component? That's scary! Smiley
    Aah thanks for informing me about the invoke mechanics. Thinking about it now, I realize I reduced it in my mind to "it spawnes another thread that can touch the UI", since when I encountered invokes I did not fully grasp that it was using delegates. Now I do. Still learning here Wink

    That was my solution yes. Reason being that there are thousands of lines of code. I cannot go change the structure of the program now so I need to handle this problem on a case by case basis. And that is, creating a new MTA thread and then returning the resulting work somehow. It worked, no exceptions, but clearly I broke something. And it was scary to do it that way. I don't like hacking.

    Thanks for the help.


Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.