.NET Forum / .NET Framework / Interop / September 2005
ClassCast-Exception for COM-interface, but only when run in own 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; } } } }
Free MagazinesGet 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 ...
|
|
|