VB.NET exe that talks to out-of-process COM on activate event bombs when external script AppActivates it - vb.net

I wrote a VB.NET Windows Forms app that requests a string from an out-of-process COM object every time the activate event fires. My form has two tabs, so I need to programmatically flip to the correct tab every time my window gains focus. Works fine, until...
By chance, someone ran a vbscript (yes, script, not exe) that contains:
Set shell = CreateObject("WScript.Shell")
shell.AppActivate("Window Title That Matches My App")
This script consistently crashes my app. Usually so badly that the Exception dialog usually can't paint itself. I have to kill it from task manager. Sometimes the Exception is readable. (I also confirmed the exception by attaching to the running exe with Visual Studio). It's: "System.Runtime.InteropServices.COMException (0x8001010D): An outgoing call cannot be made since the application is dispatching an input-synchronous call."
What's really messing with my mind is that my app has multiple instance detection using a mutex, and if an existing instance is running, my own code (compiled) uses VB.NET's own AppActivate keyword, and this does NOT crash my app. It activates the running instance and exits the redundant instance as expected.
The problem seems solely to be triggered by cscript/wscript's AppActivate. I wrote a 3-liner .vbs to confirm this. It's repeatable.
Is there a way to trap or avoid this in my compiled app?

It's not clear to me WHY this approach actually fixes the problem, but it DOES work:
Add a timer to the form.
Move all the _Activated code to the timer's _Tick event.
Make the _Activated event start the timer.
Make the _Tick event stop the timer then perform the COM stuff.

Related

Windows Automaton - InvokePattern blocking execution until everything has completed

I have experienced this behavior in my automation application: when I "click" a button by calling the InvokePattern.Invoke() method everything stops until the handler of the click event inside the automated application finishes.
While this can make some thing simple (for example I don't have to write tons of code to wait for a dialog with progress bar to disappear, because I simply get the control back when everything is done), but I can't do anything else. It even blocks the access to the Automation API in another thread where it continues after the click handler is done.
This causes problems when click handler in the automated application opens a modal dialog, then I can't do anything, the access to the application through automation API is blocked until the dialog is manually closed.
Has anybody solved this somehow and can help me?
Thanks,
Karel
PS: Reference source is saying this:
Request that the control initiate its action.
/// Should return immediately without blocking.
/// There is no way to determine what happened, when it happend, or whether
/// anything happened at all
public void Invoke() { ... }
Edit: It works perfectly when automating Windows notepad application which is not a .NET application. And it does not work for a Notepad clone (C# WinForms aplication).

VB program free freezes but still responds?

I have a winforms program written in visual Studio, coded in visual basic.
The main part of the program is a while loop that sends out serial commands, writes data to sql, does basic arithmetic, and updates controls on my form. I have this while loop in a background workers do work sub so that the controls can be updated from the background worker sub, while it is still running. After awhile of running, my form will freeze up, while halfway through updating several text boxes. However, the background worker continues to run, the sql data still gets written, and most curiously clicking a button on the form does what it is supposed to, even though we can't see that the button was pressed.
I can't think of anything that would cause behavior like this, where visually the form is frozen, but all the controls still respond.
It freezes because the UI thread is busy with the loop. It seems that you didn't implement the background worker properly and it is still attached to the UI thread.
You can add DoEvents somewhere inside your loop, which gives the UI thread some time to handle UI events. But this is just a Band-Aid fix because I don't know your code. The ideal way is to fix your background worker and its relationship with the UI thread.

Issue with OpenFileDialog and threading

I just upgraded from VS 2005 to VS 2012. This is a new issue that I do not understand. I am using the default "Form1" class the VS automatically creates. I added a button to open a file open dialog and when I click the button I get this error:
Current thread must be set to single thread apartment (STA) mode before OLE calls can be >made. Ensure that your Main function has STAThreadAttribute marked on it. This exception >is only raised if a debugger is attached to the process.
I have added " to Public Class Form1:
<STAThread()> Public Class Form1
But I get this...
Attribute 'STAThreadAttribute' cannot be applied to 'Form1' because the attribute is not >valid on this declaration type.
I have searched but get some info telling me that I need to set the entry point (Form1 I believe) to be Single Thread Attribute but the above code does not work.
How?
The <STAThread()> attribute cannot be added to classes like your form. It only works when it is applied to the Main function, which is the entry point of your application.
But VB.NET hides this function from you because it is rare that one needs to mess with Main in a WinForms application. It is just needed to get the plumbing set up for your app, which the compiler can manage for you. This is controlled by the "Application Framework" checkbox in the project options. If this is checked, the compiler automatically generates the Main function and the required plumbing. You can disable this option, but it makes life quite a bit harder for the average WinForms developer because you'll have to write and maintain your own Main function.
The real question here is why this is a problem at all. The compiler-generated Main function for a WinForms application is always going to have the STAThread attribute applied to it. That is just how the WinForms framework is designed to run. If that is not happening, then there is something badly wrong with your project. I would recommend scrapping it and starting over letting Visual Studio create a new WinForms project from one of the built-in templates. Everything should Just Work™.
The other option, of course, is that you're trying to display the OpenFileDialog on a separate thread (other than your main UI thread). But from your description in the question (adding a button to the form to display the dialog), it doesn't sound like this is the case. Regardless, the solution is not to do that. For example, if you're using a BackgroundWorker to do work on a non-UI thread in order to keep the UI responsive, that's great, but you'll want to do all of the UI stuff like showing an OpenFileDialog on the main UI thread before invoking the BackgroundWorker. There is a way to set a particular thread's apartment state using the SetApartmentState function, but I really don't recommend showing an OpenFileDialog on a background thread.

Why does my form appear behind everything?

I have a program where I need to do some initial work before calling the form, so I disabled the Application Framework setting and wrote my own Main function with a call to Application.Run(myForm) when it's time to run the form.
Everything was working fine, no problems, but now I have need of some other service before opening the form. Rather than add all that code to this program, it has all been moved into its own executable. This second program can edit files that the first program will use, so I need the first program to wait so that it will read up those changes (should they be made). I suppose could just as easily use the Shell function, but for various reasons I'm creating my own Process object and calling it/waiting on it through that.
Anyway, I make this call to the second program some time before the Application.Run call. The first program waits its turn, and I can interact with the second program successfully, no trouble at all. But when it's done, the window for the first program is hidden behind any other windows that are on the screen. This doesn't happen in XP, only in Vista (and maybe 7, but I haven't confirmed yet). I've already tried manually forcing the form to appear in front, minimize then maximize, get focus, etc, but nothing brings it to the front unless the user manually clicks on it with the mouse.
What am I doing wrong? Why does this behavior occur? I know it has something to do with waiting for the executable to finish, because if I don't force the first program to wait everything is fine (other than it not waiting). I can circumvent the issue by calling the second program in the Load event of the form, but then I have to read the file a second time to catch the changes instead of reading it once, and it also looks bad because the form is being drawn really slowly while the second program is sitting there.
If anyone has any input, I'd appreciate it.
This isn't really an answer to why you're experiencing this behaviour, but a simple workaround would be to temporarily set the form's TopMost property to True in the load event. Then, depending on how intrusive you want that to be, you could either reset it under a short timer or wait for say the MouseEnter event to fire.
There are another topic in this site about that, but I not got the link. This problem seems be a bug into .NET framework. The API below (VB.NET example) works for me in windows XP and 8.1. Don't tested in other versions of Windows.
<Runtime.InteropServices.DllImport("user32")> _
Public Shared Function SetForegroundWindow(hwnd As IntPtr) As Integer
End Function
Private Sub Form_Load(sender As Object, e As EventArgs) Handles Me.Load
SetForegroundWindow(Handle)
End Sub

Start VB.NET GUI app using Sub Main or form startup object?

Is there any reason to start a GUI program (application for Windows) written in VB.NET in the Sub Main of a module rather than directly in a form?
EDIT: The program won't take any command line parameters and it will be executed as a GUI program always.
The primary reason for using Main() in VB .NET 1.x was for adding code that needed to run before any forms were loaded. For example, you might want to detect whether an instance of your Windows Forms app was already loaded. Or you might want to intercept any unhandled exception for the AppDomain:
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf MyExceptionFilter
But the next version of VB and Visual Studio 2005 introduced a new Application model that made Main() unnecessary in most scenarios. You can now intercept the My.Application.Startup event to add code that needs to run before any forms are loaded.
Note that the code for the Startup event handler is stored in the ApplicationEvents.vb file, which is hidden by default.
You can do it either way, but you should really only keep code in the form that is directly related to the operations and user interface elements on that form. Application startup code isn't related to UI, normally concerned with splash screens, checking network connectivity, verifying a single instance only, setting up user configuration settings, and so on.
After the above items (or the appropriate initialization code for your app) are complete, Sub Main can create an instance of the main form, then show it so the user can begin interacting with your application.
This separates startup code from your form code. Later, when you're maintaining the application, you'll be glad you separated the two.
Yes, and I have done it a few times.
One reason is, that if your app is COM EXE (speaking now from a VB6 point of view) then you want to be able to detect in what context the EXE is being called (being launched or being spoken to by some other app).
For example:
Sub Main()
If App.StartMode = vbSModeAutomation Then
...
Else
...
End If
End Sub
Another is if you want your app to be able to handle any command line parameters.
For example:
Sub Main()
If App.PrevInstance Then End
If InStr(Command, "/s") > 0 Then
Form1.Show
ElseIf InStr(Command, "/p") > 0 Then
LoadPicture ("c:\windows\Zapotec.bmp")
End If
End Sub
(from one of my attempts to make a screen saver)
No, if you always want to show that form.
Yes, if you sometimes want to use your app without GUI, just using command line.
Yes, if I want to display different forms depending on some parameter (in a file, on a remote server, etc.).