Here’s an interesting one I ran into today with Asynchronous Delegates and PInvoke calls:
In West Wind Web Monitor I have a splash screen that pops up when the app starts. It runs on a separate thread so that it can pop immediately before the main form of the application loads. The form also contains a button that uses the Win32 ShellExecute API to load a URL for registration. It all worked well until yesterday.
Btw, the code to make the ShellExecute call looks like this:
[DllImport("Shell32.dll")]
private static extern int ShellExecute(int hwnd, string lpOperation,
string lpFile, string lpParameters,
string lpDirectory, int nShowCmd);
public static int GoUrl(string Url)
{
string TPath = Path.GetTempPath();
int Result = ShellExecute(0,"OPEN",Url, "",TPath,1);
return Result;
}
Anyway, a while back I decided instead of creating a brand new thread to just use an Async Delegate and use BeginInvoke to call the splash routine from it thinking that it would be more efficient (probably not since the Delegate probably has to fire a new thread too). Today getting ready for deployment and shipping out of the app I casually noticed that the button code that goes to the URL was not working. Turns out that the ShellExecute API call ends up failing with a value of 5 – Access Denied.
After some back and forth I realized that the same code was working fine in the main part of the application – the Splash screen also doubles as the About form. When I brought up the form from the Help menu it all worked fine and dandy.
So. it turns out that using the Delegate causes this problem to occur. If I start up my own thread the code runs just fine. Async Delegates use the .Net Thread pool and apparently these threads don’t have the same rights as the mainline application. Just to be sure I checked the current user credentials and it looks like my credentials. So the permissions issue here is not user level but .Net permissions level.
///*** This causes ShellExecute to fail with error 5
MethodInvoker delSplash = new MethodInvoker(Startup.RunSplash);
delSplash.BeginInvoke(null,null);
///*** This doesn’t
ThreadStart delSplash = new ThreadStart(Startup.RunSplash);
Thread NewThread = new Thread(delSplash);
NewThread.ApartmentState = ApartmentState.STA;