views:

263

answers:

5

Now unfortunately due to the fact that WinCE Usb Device Arrival / Removal exposes itself via WindowsMessages I have to ensure that a certain (non-UI) component is not created on a background thread. I would like to assert this via an exception but am lacking the code for the assertion.

This component creates a MessageWindow* and uses it to receive usb arrived/removed messages. The issue is if someone creates this component on a background thread (not necessarily; IsBackground = true) when the thread exits the window will be destroyed.

Any ideas?

*as an aside I still don't know why Form doesn't inherit from this class

Update

I think my version 1 wasn't very clear. So this is v2.

When you create a MessageWindow or a Form for that matter on a thread, when that thread exits the Window/Form is destroyed.

My component is creating a "hidden" message window to intercept some important events, ergo I do not wish for it to be destroyed. Therefore I must somehow ensure that the code that creates the form is running on the "Main UI" thread.

If possible i'd like to avoid passing down a reference to the "main" form to this component as it is (architecturally speaking) supposed to be miles away from the UI.

Update

Moving logging question to a separate Q.

A: 

In forms, you use the InvokeRequired property.

Tom Ritter
This is okay but IIRC it only compares the current thread id against the thread id that created the form. You can for example create a form on a background thread and have InvokeRequired return false even though when that thread exits the form will die. I guess I could make it work by requesting the creator of the component pass down their main form as an argument. Although aestically thats not a very nice API and I have no way of telling if the form they do send IS the main form.
Quibblesome
A: 

Why not create the non-UI component on a background thread and when you go to update any UI component just look to see if invokeRequired then get back on the main thread to actually do the update.

You should have nothing really tying up the main event thread, IMO.

James Black
I don't think you get me. My "non-UI" component needs to create a message window component to intercept windows messages. I want to guarantee that when I create this window the calling thread isn't going to exit (when a thread exits all windows created on that thread are destroyed).
Quibblesome
You can make a thread that never exits, just sleep, process messages, loop. I just feel uncomfortable having anything on the main thread as it is too easy to start affecting the device performance.
James Black
Fair point. I guess I can create a background thread. Create the window and then call Application.Run() (the empty version). This will get around the issue and give me a logging mechanism. Thanks. :)
Quibblesome
/me cries as he discovers that Application.Run() is not supported in CF and Application.Run(Form) doesn't work with MessageWindow cause it isn't typeof form.
Quibblesome
You may want to look at http://www.cnblogs.com/warrentang/archive/2008/06/15/1222749.html but use IE to view it, as the source code doesn't seem to show up in Firefox 3.0. :(
James Black
A: 

You can use it in this way:

void MyCallback()
{
if (form1.InvokeRequired) { // form1 is any existing gui control
    form1.Invoke(new Action<>(MyCallBack));
    return;
}
// your logic here
}
Stormenet
In CF InvokeRequired is just return !myStoredThreadId == Thread.CurrentThread.ManagedThreadId. This means that it doesn't actually tell you if you're on the main UI thread. Just that you are on the same thread that created the control. Passing the main form down to this lib would hurt a lot of architectural boundaries too. :(. Thanks for the suggestion though!
Quibblesome
+1  A: 

Ok, I understand that you don't want for your component to "know" about the main window -- makes sense.

How about this: How about if you make sure that you always instance your component on the main thread? You component will create it's listener window on the constructor's thread.

If you do that, then you just need to make sure that you call the constructor from the main thread. I'm making some assumptions about your code, but I'm guessing that you must have some class in your architecture that knows about both the UI and your component. Create your component there, using a callback, and the main form's InvokeRequired/Invoke methods.

JMarsch
Aye this is more or less what i'm going to have to do but it is supposed to be a component used by many teams and I don't like having non-explicit clauses such as this. It means that instead of just being able to use the object they'll have to read xyz beforehand and I can't always guarantee that.That said this is a highly practical solution and I guess i'll just have to live with it for now.
Quibblesome
I agree about trying to make it explicit. You can help to avert the problem a little bit by putting a note in the summary xml comments for the constructor and/or class. (at least your warning will pop up in intellisense that way).
JMarsch
Hey, I had another idea about your problem. Look for the next answer from JMarsch below.
JMarsch
A: 

Hey there: I had an idea about your problem. This is just a random thought, and I don't know for sure whether it will work (I have not tested, nor even compiled this -- it just hit me):

What if you get the window handle of the main window of your app, then build a Control around it (I'm assuming that you have a gdi-based app, like Winforms)?

this code might not compile, but it's close (it would go into your component -- note that it would make your component require a gdi windows/winform app, as opposed to a console or WPF app).

If you do try it, I'd love to hear whether it worked for you.


using System.Diagnostics;
using System.Windows.Forms;
void Init()
{
   // get handle to the main window
   intPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
   Control mainWindow = Control.FromHandle(mainWindowHandle);
   if(mainWindow.InvokeRequired)
      mainWindow.Invoke(SetupMessageWindow);
   else
      SetupMessageWindow();
}
void SetupMessageWindow()
{
  // do your thing...
}

JMarsch