Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Interop / September 2005

Tip: Looking for answers? Try searching our database.

ClassCast-Exception for COM-interface, but only when run in own thread

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
taj - 26 Sep 2005 21:24 GMT
Hi there, i hope anybody can help or just give a hint...

using C#, i try to get an instance of a COM-class of the ShellAPI,
IShellIcon in this case pericular case, but is also reproduceable with
other Shell-interfaces :-) See the code...

object iconObj = null;
ShellAPI.IShellIcon shellIcon = null;
IntPtr MyPtr, MyPtr2;
ShellAPI.SHGetDesktopFolder(out MyPtr);
Marshal.QueryInterface(MyPtr, ref ShellAPI.IID_IShellIcon, out MyPtr2);
if(MyPtr2 != IntPtr.Zero)
  iconObj = Marshal.GetTypedObjectForIUnknown(MyPtr2,
  typeof(ShellAPI.IShellIcon));
if(shellIcon != null)
  System.Windows.Forms.MessageBox.Show("Got it!");

...nothing spectacular so far, i also thought this; the piece of code
works pretty well when run in the main thread of my
single-thread-apartement; however, as soon as transfered to its own
thread for performance resons, i'm able to catch an exception when
calling "GetTypedObjectForIUnknown" saying, "System.__ComObject" can't
be casted to "ShellAPI.IShellIcon".
The thread is currently started by "BeginInvoke"; i also already tried
the following:
- Starting a real thread ("new Thread(new ThreadStart"-blablabla)
- Starting a real thread, also in SingleThreadApartement
- Calling CoInitialize at the beginning of the thread-lifecycle (HRESULT
says, can't be done twice)
- Calling OleInitialize at the beginning of the thread

I'm really desperating about this, cause the ideas i still can try are
running out...

Any ideas?!? Help would be great,
regards,
taj
Robert Jordan - 26 Sep 2005 22:07 GMT
> Hi there, i hope anybody can help or just give a hint...
>
[quoted text clipped - 31 lines]
>
> Any ideas?!? Help would be great,

You probably have to run a message pump on the second thread:

Thread t = new Thread(new ThreadStart(Proc));
t.ApartmentState = ApartmentState.STA;
t.Start();

void Proc()
{
  Application.Run(new ShellForm());
}

// dummy form. may be hidden.
class ShellForm : Form
{
  protected override void OnLoad (EventArgs e)
  {
     base.OnLoad (e);
     // initialize and run your COM stuff
  }
}

Rob
taj - 26 Sep 2005 22:26 GMT
Hi Robert,

well, remembering my delphi-times i just thought that could be the
point, cause delphi uses such dummy-forms for sure... however, i'm sorry
it did not solve my problem. after reading your posted URL
http://msdn.microsoft.com/msdnmag/issues/04/06/BasicInstincts/
i switched back to an own thread in a single-threaded-apartment; cause
the marshalling fails, i asume of something like "the interface is
defined in another thread apartment, so the conversation fails"...
however, it's just a thought, and i use just a interface, which can't be
bound to an thread-context, or can it?!?
the interface definition:

public const  string IIDS_IShellIcon =
  "000214E5-0000-0000-C000-000000000046";
public static Guid   IID_IShellIcon  = new Guid(IIDS_IShellIcon);
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
  Guid(IIDS_IShellIcon)]
    public interface IShellIcon {
      [PreserveSig] uint GetIconOf(IntPtr pidl, GIL flags, out uint
        lpIconIndex);
    }

however, thanks Robert, any more ideas?

regards,
taj

Robert Jordan schrieb:

>> Hi there, i hope anybody can help or just give a hint...
>>
[quoted text clipped - 54 lines]
>
> Rob
Robert Jordan - 26 Sep 2005 22:55 GMT
Hi Taj,

> well, remembering my delphi-times i just thought that could be the
> point, cause delphi uses such dummy-forms for sure... however, i'm sorry
[quoted text clipped - 5 lines]
> however, it's just a thought, and i use just a interface, which can't be
> bound to an thread-context, or can it?!?

Interface declarations are apartment free.

> however, thanks Robert, any more ideas?

Try to call Application.OleRequired() on the 2nd thread.

Rob
taj - 27 Sep 2005 14:43 GMT
Robert Jordan schrieb:
> Interface declarations are apartment free.

yes, that's what i thought for sure...

> Try to call Application.OleRequired() on the 2nd thread.

good hint again, but no success either, sorry...

i'm really getting confused more and more.

any more ideas?!?

regards, taj
Robert Jordan - 27 Sep 2005 15:31 GMT
> Robert Jordan schrieb:
>
[quoted text clipped - 9 lines]
>
> any more ideas?!?

No, but I'll try to reproduce the problem myself.

Rob
taj - 27 Sep 2005 17:18 GMT
> No, but I'll try to reproduce the problem myself.
>
> Rob

okay, i now will try to build up an application from scratch, to see
whether something's wrong with my total application environment.

thank you very much, Robert!

regards,
taj
taj - 27 Sep 2005 17:39 GMT
>> No, but I'll try to reproduce the problem myself.
>>
[quoted text clipped - 7 lines]
> regards,
> taj

oh my god! i'm so an dumb-filled idiot! i just tried out this, and by
doing so, i found what the problem has to be: my interface definitions
reside in an helper class "ShellAPI", which contains function-stubs,
enumerations and everything related to Windows Shell API.
When moving the interface declaration out of this class (which, however
never has been instantiated cause its constructor is private),
everything works fine and as expected...
so, anyhow, i was right when saying "it seems the interface is related
to the main threads apartment" - it really was...

thank you very much, robert, finally you gave me the hint i looked for
while investigating this nasty brainbug.
its always hard to discover the own incompetence, but sometimes you
learn something by doing so :-)

thank you very much, Robert.
gracias,
taj
taj - 27 Sep 2005 18:35 GMT
> oh my god! i'm so an dumb-filled idiot! i just tried out this, and by
> doing so, i found what the problem has to be: my interface definitions
[quoted text clipped - 5 lines]
> so, anyhow, i was right when saying "it seems the interface is related
> to the main threads apartment" - it really was...

oh my god, mor and more i get into desperation...

in the stand-alone-application, this works well, in my application
(distributed over several DLLs), it works not...

may it be it has something to do with the point, that the code resides
in its own DLL, and the Shell interfaces and stubs reside in another?

regards,
taj
Robert Jordan - 27 Sep 2005 19:29 GMT
Hi Taj,

>> oh my god! i'm so an dumb-filled idiot! i just tried out this, and by
>> doing so, i found what the problem has to be: my interface definitions
[quoted text clipped - 13 lines]
> may it be it has something to do with the point, that the code resides
> in its own DLL, and the Shell interfaces and stubs reside in another?

I don't think this is a problem. Try to get rid of this
ugly Marshal.QueryInterface stuff:

- redeclare SHGetDesktopFolder() as

  SHGetDesktopFolder(out IShellIcon ppshf);

- deleted the Guid consts

- declare IShellFolder:

  [ComImport]
  [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  [Guid("000214E5-0000-0000-C000-000000000046")]
  public interface IShellIcon
  {
      void GetIconOf(IntPtr pidl, GIL flags, out uint lpIconIndex);
  }

The code you posted in your first mail looks much cleaner now:

IShellIcon icon;
ShellAPI.SHGetDesktopFolder(out icon);
if (icon != null) MessageBox.Show("Got it!");

Rob
taj - 27 Sep 2005 20:15 GMT
> I don't think this is a problem. Try to get rid of this
> ugly Marshal.QueryInterface stuff:
[quoted text clipped - 22 lines]
>
> Rob

Hi Robert,

okay, i really tracked down the problem to the Marshal-class-methods;
the problem is, that i don't get the IShellIcon by calling
SHGetDesktopFolder, but i recieve an IShellFolder-instance.

in the old world, i would have used QueryInterface to get the
IShellIcon-interface of it (if it is supported, for Desktops
IShellFolder it is for sure). How to do such an transformation in C#?!?

i simply used this:

  SHGetDesktopFolder(out shellFolder);
  shellIcon = shellFolder as IShellIcon;

which works well - but - and only! if i not used the following statement
in the main class:

  SHGetDesktopFolder(out shellFolder);

somehow the marshaller gets tracked of the usage of the shellFolder in
the main thread, so the second gets confused.
i may send the code if appropriate; however, i'm wondering why in the
second thread, the instantiation of the original object - IShellFolder -
still works, but the transformation to an IShellIcon object out of it
fails...

However, thousand thanks,
would be glad to here from you,
taj
Robert Jordan - 27 Sep 2005 20:44 GMT
Hi taj,

> okay, i really tracked down the problem to the Marshal-class-methods;
> the problem is, that i don't get the IShellIcon by calling
[quoted text clipped - 3 lines]
> IShellIcon-interface of it (if it is supported, for Desktops
> IShellFolder it is for sure). How to do such an transformation in C#?!?

QueryInterface gets automatically called when you cast
to one of the interfaces.

> i simply used this:
>
[quoted text clipped - 5 lines]
>
>   SHGetDesktopFolder(out shellFolder);

Is shellFolder shared between the threads? I mean: do you call
SHGetDesktopFolder(out shellFolder) in your main thread
and shellIcon = shellFolder as IShellIcon in the secondary
thread?

If the COM object behind the IShellIcon interface is not
free-threaded, then QueryInterface will fail.

Rob
taj - 27 Sep 2005 21:10 GMT
Hi Rob,

see my last post which is on the same level in this discussion thread :-)

No, as you can see cause i posted the full (console) application, both
threads call SHGetDesktopFolder for their own, and as i can see from
Marshal.GetIUnknownFromObject both threads get their own Pointer to it.

The second thread uses no pointers/references to objects from the main
thread; lets say it starts as a new, fresh application.

as i can see so far, call to SHGetDesktopFolder in the second thread
works well in both cases, that means its regardless whether the first
thread relaesed the interface in the meantime or not. however, and that
is the strange thing, casting to IShellIcon in the second one only works
well when the first thread released its interface pointer in the meantime.

i didn't try calling methods into IShellFolder directly in the second
thread, perhaps i should give a try; maybe they also will fail.

However, thanks again Robert,
regards,
taj

> Is shellFolder shared between the threads? I mean: do you call
> SHGetDesktopFolder(out shellFolder) in your main thread
[quoted text clipped - 5 lines]
>
> Rob
taj - 27 Sep 2005 20:56 GMT
Hi Robert, hi all,

i spent more investigation into this... i found out, that when releasing
the IShellFolder interface in the main thread before it is used in the
second one, the second one is able to use it...
if you uncomment out the line "Marshal.ReleaseComObject", the thread
(which starts after the main thread finished) is able to do the
transformation into IShellIcon; if not, it fails.

it's still magic that the original SHGetDesktopFolder seems to work,
just the casting into IShellIcon fails; i don't think this is interface
specific behaviour, cause i already saw and used SHGetDesktopFolder in
multi-threaded environments very well.

i post the (really reduced) code which may be used to reproduce the
problem, as mentioned uncomment the Marshalling-code to see it is very
well able to work somehow...

Robert, i will be gone for business travel till friday, i would be glad
if you find the time to give it one more minute; perhaps you find the
time and we will be able to continue the discussion of this topic then.

Regards & thousands thanks,
taj

// Console application that reproduces the topic
using System;
using System.Threading;
using System.Runtime.InteropServices;

namespace ConsoleApplication1 {
 class Class1 {
  [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
   Guid("000214E5-0000-0000-C000-000000000046")]
   public interface IShellIcon {
  }

  [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
   Guid("000214E6-0000-0000-C000-000000000046")]
   public interface IShellFolder {
  }

  [STAThread]
  static void Main(string[] args) {
   IShellFolder shellFolder = null;
   SHGetDesktopFolder(out shellFolder);

   Thread mythread = new Thread(new ThreadStart(MyThreadWorker));
   mythread.ApartmentState = ApartmentState.STA;
   ApartmentState aps = mythread.ApartmentState;
   mythread.Start();

   //Marshal.ReleaseComObject(shellFolder);
   mythread.Join();
  }

  [DllImport("shell32.dll")]
  public static extern Int32 SHGetDesktopFolder(out IShellFolder ppshf);

  public static void MyThreadWorker() {
   try {
    IShellIcon shellIcon = null;
    IShellFolder shellFolder = null;

    SHGetDesktopFolder(out shellFolder);
    shellIcon = shellFolder as IShellIcon;

    if(shellIcon != null)
     System.Windows.Forms.MessageBox.Show("Got it!");
   } catch(Exception ex) {
    string test = ex.Message;
   }
  }
 }
}

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.