OpenSpan Blogzone

Archive for the ‘4.1’ Category

Inside Out: Synchronous Click

Fri ,26/06/2009

In 4.1 we introduced a new set of methods: PerformSynchronousClick, PerformSynchronousDoubleClick and PerformSynchronousRightClick. These methods are only available in our windows adapter currently, but will be extended to other technology stacks in the future. These new methods complement our existing PerformClick, PerformDoubleClick and PerformRightClick. So why do we need synchronous versions of these methods? And why are the existing methods not synchronous? What does synchronous mean in this context anyway?

To understand these functions, let’s take a step back and talk about the Windows message loop. All Windows programs (at least those with a user interface) feature some variation of the following:

MSG msg;while( GetMessage( &msg, NULL, 0, 0 ) != 0){   TranslateMessage(&msg);    DispatchMessage(&msg); }

This simple code snippet is the prototypical Windows message loop. Essentially, GetMessage asks Windows for a message and waits. When Windows has a message (such as a keyboard or mouse message), GetMessage returns. The caller then passes the message to TranslateMessage (which turns keyboard messages into char messages), and then DispatchMessage (which passes the message to the actual destination window).

However, not all messages are returned by GetMessage. Only posted messages (input messsages and messages posted using PostMessage) are returned by GetMessage. So what happens to sent messages (messages sent using SendMessage or one of its variants)? These messages are dispatched synchronously to the destination window by GetMessage before it returns. The sequence looks like this:

  1. Enter GetMessage.
  2. Dispatch all sent messages to the destination windows.
  3. Get the next posted message and fill the msg structure.
  4. Exit GetMessage.

In truth, it gets a little more complicated than this, particularly when you’re dealing with another application’s message loop. Applications can and do add all sorts of additional processing to the message loop to do special input filtering, show modal dialogs, etc. Moreover, simulating even the simplest click can involve over twenty messages, some sent, some posted, depending on the styles of the destination window.

Unfortunately, directly sending messages to the destination window will not yield the correct behavior because if the application does any special message processing in the message loop we will have bypassed it entirely. Moreover, we never know when one of the twenty messages we send will result in a long running application process that we don’t want to wait for. For these reasons, prior to 4.1, we did not provide any synchronous click functions at all. We filled the message queue and let’er rip.

For most scenarios, this works fine. OpenSpan always waits for new controls to be created using our implicit WaitForCreate methods so there are very few instances where we need to wait for the actual result of a click. However, one of those situations involves our prototypical demo application: calculator. Using the standard click methods, the following automation will return 0 or 4 rather than the desired 8. With the information above can you understand why?

That’s right, each step of the automation doesn’t wait for the previous step to complete. Thus, the automation gets the text before calculator has updated the text field with the final result. The new synchronous click methods do wait for all of the click messages to be processed. Thus, the automation below will return 8 every time.

But what if clicking a button creates a modal dialog? Will OpenSpan wait for the modal dialog to be dismissed by the user before continuing? What if there is no user? What if I want to dismiss the dialog in my automation? Oh noesss!

Simmer down. The short answer is that we do not wait for the modal dialog to be dismissed. Modal dialogs are nothing more than a nested GetMessage loop inside the outer GetMessage loop. Usually the sequence looks something like this:

  1. GetMessage returns a mouse up message.
  2. DispatchMessage passes the mouse up message to a window.
  3. The window procedure creates the modal dialog and shows it.
  4. The window procedure calls GetMessage and enters a new message loop to process messages posted to the modal dialog.
  5. The user dismisses the modal dialog.
  6. The windows procedure exits the message loop and returns.
  7. DispatchMessage returns and the outer message loop resumes.

OpenSpan simply waits until step 4, when GetMessage is called again after the mouse up message and returns. Perceptive readers with knowledge of hooking may have guessed how we do this, but for everyone else, it’s sufficient to say that we ensure that each time an application calls GetMessage (or any of its variants such as PeekMessage) the next simulated message is always returned. Some other time, maybe I’ll talk about how we ensure that OpenSpan messages receive priority over other messages and are always processed in order. Until then, I’ll let everyone guess in the comments (everyone except OpenSpan employees, that would be cheating) and give the correct answer a free “Do It on the Desktop” t-shirt.

4.1 Feature Spotlight: Automation Stack

Wed ,17/06/2009

It’s that time again! As our 4.1 release nears completion, it’s time to start posting about our new features. One of the new features we’re testing currently is a new automation stack implementation. As our platform has evolved, we have begun to use our automation engine in new and more complicated environments. Previously, most of our environments were single-threaded automations of applications on the user desktop. Now, our platform is being used on the server to automate back-office requests, process messages and expose applications as web services. In this new environment, it quickly became clearly that our automation engine needed a proper stack to ensure that our automations remained thread safe.

If you’re not familiar with OpenSpan, one of your first questions might be how the heck don’t you already have a stack? To explain that, I need to go under the hood for a little bit. Looking at the automation your first instinct would be to assume that OpenSpan is a visual programming language that generates source code. For example, the automation below could be expressed in a C# class as:

private string varString;private void Setup(){   btnSubmit.Click += delegate   {      varString = txtPhone.Text;      webBrowser.Navigate(txtUrl.Text);   };}


Pretty straightforward, right? How about this automation? In this case we’ve made one of the links asynchronous indicating that the next step should be performed on a new thread.

private string varString;private void Setup(){   btnSubmit.Click += delegate   {      varString = txtPhone.Text;      NavigateDelegate navDel = delegate(string url)      {         webBrowser.Navigate(url);      };      navDel.BeginInvoke(txtUrl.Text, null, null);   };}


Now it’s starting to get more complicated. Every time we need to execute an asynchronous link we would need to generate a delegate type. I’m using anonymous methods which makes it easier, but it’s still pretty difficult. How about this automation? We have two execution threads from different events converging on a single block. Easy to express in OpenSpan, but we would need to use another method or delegate in C#.

private string varString;private void Setup(){   btnSubmit.Click += delegate { NextStep(); };   webForm.Submitting += delegate(object sender, CancelEventArgs e)   {      NextStep();   };}

private void NextStep(){   varString = txtPhone.Text;   NavigateDelegate navDel = delegate(string url)   {      webBrowser.Navigate(url);   };   navDel.BeginInvoke(txtUrl.Text, null, null);}


Even more complicated, and I haven’t even added entry points to the automation. So what can we conclude from this exercise? As you might have guessed, OpenSpan does not generate source code. Instead, we model execution flow using a serialized object graph. This gives us greater flexibility to express logical concepts such as branches, joins, etc. without the limitations of traditional syntax. However, because we do not generate code we are unable to rely on the existing thread stack to manage our local variables for us. Thus, prior to 4.1, we simply didn’t have a stack at all.

So, how does the new stack work? For the most part, it works like the normal thread stack we all know and love. When an automation is entered, all of the local variables and components are allocated and pushed onto the automation stack. So far so good, right? But what about those pesky asynchronous links? In OpenSpan it’s perfectly legal to have two links on different threads in the same automation updating variables.


So what should we do here? In a traditional thread stack model, each asynchronous link gets it’s own stack. OpenSpan, however, makes creating new threads absurdly easy. Thus, it’s easy to create a number of parallel threads to accomplish tasks such as updating three or four application simultaneously. Should we allocate new variables and components every time we execute a new asynchronous link? Should we initialize them to their original defaults or should we initialize them to their current values?

As you probably guessed, the correct answer is none of the above. The new automation stack is not a thread stack. Local components and variables are allocated on a per automation basis not on a per thread basis. Thus, each of those asynchronous links in the example above are actually updating the same variable. This approach preserves the ease of threading OpenSpan traditionally provides, while adding the additional safety of stack-based variables.

Now that I’ve explained how the new automation stack works, I’ll cover some of the common questions I’ve been asked about this feature.

What if I want my variables to be available to all automations?
No problem. OpenSpan automations now have a global and a local tray. Components in the global tray behave just like they did in previous versions of OpenSpan. They can be updated from any automation at any time. Components in the local tray can only be used on that automation and are not exposed to other automations.

When are local and global components created?
Global components are created when the project is started and can be used in any automation. Local components are only created when an automation is run. Local components can only be used in the automation that contains them.

When are local and global components destroyed?
Global components are destroyed when the project is stopped. Local components are destroyed when all the threads in the automation have completed.

When should I use a local component?
Local components are appropriate when the component in question does not need to be accessed from other automations or when an automation can be run simultaneously on multiple threads. Local components are recreated every time an automation is run and thus will not maintain state across multiple runs. For instance, you could not create a local variable to keep track of the number of times an automation is run. Every time the automation runs the counter variable would be recreated and initialized to zero.

What happens when I upgrade my old automations?
Variables and components from old automations will automatically be placed in the global tray during the upgrade process. Thus, your old automations will continue to function as they did before. To take advantage of the automation stack, you can easily make any global components local by right-click and selected the “Make Local” menu item.

Pharmacy Without Prescription:
Buy clomid online
Buy zovirax online
Buy cipro online
Buy nexium online
Buy diflucan online
Buy lasix online
Buy neurontin online
Buy synthroid online
Buy flagyl online
Buy nolvadex online