views:

293

answers:

6

I am developing a C# application to automate the running of a legacy VBScript(vbs) file which calls several VB6 .exe files. The .exe files have message box pop-ups that I need to 'respond' to in order to allow the VBScript process to run unattended. The response would need to be the Enter key. I do not have the source for the .exe files and I do not know exactly what they do. I would greatly appreciate any help with this...

+2  A: 

You might find AutoIt helpful.

AutoIt v3 is a freeware BASIC-like scripting language designed for automating the Windows GUI and general scripting. It uses a combination of simulated keystrokes, mouse movement and window/control manipulation in order to automate tasks in a way not possible or reliable with other languages (e.g. VBScript and SendKeys).

You can develop something using only the AutoIt programming language, or you can drive it from your own applications. My team's using this, with good success.

Michael Petrotta
+1. Interesting tool.
David Stratton
Looks like it could work, but unfortunately, I cannot introduce any third party applications at this client without going through the enterprise IT approval process...
kensy
+2  A: 

You can use the wsh SendKeys() function. However, because you need to ensure the message box is activated, you'll also need to call AppActivate() immediately before the SendKeys call.

Even this is buggy, but I've written several scripts that do just this, and as long as you can predict when the message boxes will come up, you can send the [Enter] key to respond to it.

David Stratton
+1  A: 

You can do this in C# without needing some external utility. The trick is to search for the message box dialog and click its OK button. Doing it more than once requires a Timer that keeps searching for such a dialog and clicking it away. Add a new class to your project and paste this code:

using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class MessageBoxClicker : IDisposable {
  private Timer mTimer;

  public MessageBoxClicker() {
    mTimer = new Timer();
    mTimer.Interval = 50;
    mTimer.Enabled = true;
    mTimer.Tick += new EventHandler(findDialog);
  }

  private void findDialog(object sender, EventArgs e) {
    // Enumerate windows to find the message box
    EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow);
    EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero);
    GC.KeepAlive(callback);
  }

  private bool checkWindow(IntPtr hWnd, IntPtr lp) {
    // Checks if <hWnd> is a dialog
    StringBuilder sb = new StringBuilder(260);
    GetClassName(hWnd, sb, sb.Capacity);
    if (sb.ToString() != "#32770") return true;
    // Got it, send the BN_CLICKED message for the OK button
    SendMessage(hWnd, WM_COMMAND, (IntPtr)IDC_OK, IntPtr.Zero);
    // Done
    return false;
  }

  public void Dispose() {
    mTimer.Enabled = false;
  }

  // P/Invoke declarations
  private const int WM_COMMAND = 0x111;
  private const int IDC_OK = 2;
  private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp);
  [DllImport("user32.dll")]
  private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp);
  [DllImport("kernel32.dll")]
  private static extern int GetCurrentThreadId();
  [DllImport("user32.dll")]
  private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen);
  [DllImport("user32.dll")]
  private static extern IntPtr GetDlgItem(IntPtr hWnd, int item);
  [DllImport("user32.dll")]
  private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

Sample usage:

private void button1_Click(object sender, EventArgs e) {
  using (new MessageBoxClicker()) {
    MessageBox.Show("gonzo");
  }
}
Hans Passant
This is the sort of thing I was trying to do but I could not get this to work when the messagebox was generated by another process.
kensy
Right, it won't work for other EXEs. Unless you can get the main thread ID of that EXE, Process.Threads and ProcessThread.Id
Hans Passant
+1  A: 

Might want to look at using SetWinEventHook PInvoke to detect when dialogs are being created. You can specify the hook to be global or for a specific process. You can set the WINEVENT_OUTOFCONTEXT flag to ensure that your code isn't actually running in the process you're hooking. And the event you're looking for should be EVENT_SYSTEM_DIALOGSTART.

Once you've got the hwnd of the dialog (from the event hook), you can use SendMesssage with WM_COMMAND or WM_SYSCOMMAND to get rid of it.

jaws
A: 

After spending the last 2 days trying to get this working, I finally gave up and decided on another approach. I am interrogating the data that is being sent to the external process and screening it for the conditions that are causing the message box pop-ups. Thanks to everyone who replied with answers!

kensy
That's probably a better and more robust approach.
Michael Petrotta
A: 

Using sendkey method, Pass keyboard key value and continue execution.

Shivaprakash