Dorian has just uploaded a video demonstrating his Screen Capture and Email script. Nice video, check it out:
May 9, 2013
April 17, 2013
Paste Into Object Without Using Keystrokes
Someone asked today how it might be possible to paste what is in the clipboard into an object using Macro Scheduler without having to use keystrokes.
It can be done by sending the WM_PASTE message using the SendMessage API function.
Here’s an example:
//Sending WM_PASTE to an object causes a clipboard paste, like sending CTRL-V //constants - here we define the WM_PASTE constant Let>WM_PASTE=770 //we need the handle of the object we want to send WM_PASTE to - // - as an example let's find the handle of Notepad's edit window // - for this example make sure Notepad is running with empty document // - so that the window title us "Untitled - Notepad". Modify if needed GetWindowHandle>Untitled - Notepad,hWndParent FindObject>hWndParent,Edit,,1,hWnd,X1,Y1,X2,Y2,result //now send WM_PASTE to it using SendMessage API LibFunc>User32,SendMessageA,result,hWnd,WM_PASTE,0,0
The only benefit I can see with this is that it doesn’t require keyboard focus. In theory neither should ObjectSendKeys and a CTRL-V require keyboard focus. The latter is simpler so I’d tend towards using that. And there may be some applications that work at a higher level and actually expect to see CTRL-V or similar.
Never-the-less it’s a nice example of sending a Windows message and it may be useful for some. So here it is.
March 27, 2013
Getting Data From Excel Without Office Installed
In this post from 2008 I demonstrated how to get data from Excel worksheets using Macro Scheduler’s database functions.
This works fine if Office is already installed on the PC. But what if you want to get data from an Excel sheet and the PC you are running on doesn’t have Office installed?
Well you need the Office Data Connectivity Components which you can download from microsoft.com here:
http://www.microsoft.com/en-us/download/details.aspx?id=23734
This will install the OLEDB/ODBC drivers which DBConnect can then use to make a connection to a workbook. You can then use DBQuery to retrieve data from a sheet as shown previously.
January 24, 2013
How to tell if the current session is a Remote Desktop session
Here’s a small piece of code which will tell you whether the current session is a Remote Desktop/Terminal Services session or not:
Let>SM_REMOTESESSION=4096 LibFunc>User32,GetSystemMetrics,isRemote,SM_REMOTESESSION If>isRemote>0 MessageModal>Current Session is RDP/Terminal Services Session Else MessageModal>Current Session is Local Session Endif
December 7, 2012
Custom Event Triggers
Did you know you can make just about any kind of schedule or trigger using Custom Event Triggers?
You’ll find the custom trigger option under Macro Properties in the Trigger tab.
Parsnipnose3000 has just posted a tip on Custom Event Triggers to the forums, showing how you can have a macro fire based on an image appearing on the screen. Check it out.
October 29, 2012
My Most Used RegEx
It occurred to me the other day while working on a script for a customer that I use this regular expression frequently:
(?<=TOKEN1).*?(?=TOKEN2)
It is very useful when parsing information out of web pages, or when finding elements in web pages.
What it does is pull out all the text between TOKEN1 and TOKEN2. Those could be other pieces of text, or html characters, or whatever.
As an example, recently I wrote a script which loops through all rows in an HTML table, and pulls out an order number, then looks this order number up in an Excel sheet. The order number appeared in a table cell along with other information. It was the first item inside an <i> (italics) tag and was followed by a space and then a hyphen. So I used this to pull it out of the row:
RegEx>(?<=<i>).*?(?= -),this_row,0,matches,nm,0
See how it looks for everything between the ‘<i>’ and ‘ -‘ (space then hyphen).
The next thing my code needed to do was find the ID of the single input field in the same row. This input was used to enter the order quantity, obtained from the Excel sheet. The ID is not something we know up front but it’s the only input field in the row. So I did this:
RegEx>(?<=id=").*?(?="),theInput,0,matches,nm,0
In other words, pull the text between id=” and “, which gives us the input’s ID value. We can then use that later to identify and fill the input field.
Regular Expressions are daunting at first. But eventually you find a small number of patterns help in many situations. This is one that I often find useful.
What’s your oft-used regular expression?
October 3, 2012
Sending/Retrieving Emails via Gmail
Since version 13.2 Macro Scheduler‘s email functions now support SSL. Google’s Gmail and many other email services now insist on SSL secured connections.
To use SSL you first need to install the OpenSSL library files.
Here’s an example of sending an email via Gmail:
Let>SMTP_AUTH=1 Let>[email protected] Let>SMTP_PASSWORD=your_password Let>SMTP_PORT=465 Let>SMTP_SSL=1 SMTPSendMail>[email protected],smtp.gmail.com,[email protected],your name,test,hello world,
And to retrieve emails from Gmail via POP3 (make sure you have enabled this in your gmail settings):
Let>POP3_PORT=995 Let>POP3_SSL=1 RetrievePOP3>pop.gmail.com,[email protected],your_password,c:\emails\in\
September 4, 2012
Custom Dialogs: How to make a keypress alter a button click outcome
Today I was asked how the outcome of a button click on a custom dialog could be altered if the ALT or CTRL key was pressed down at the time of the click.
This is possible by trapping the dialog’s OnKeyDown and OnKeyUp handlers. Using these we can detect if ALT or CTRL is pressed/released and set a flag accordingly. Then in the button’s OnClick handler we can check the value of this flag and decide what we should do.
Here’s some example code. Run it and click the button without holding down any keys. You’ll see a message saying that ALT was not pressed. Now click the button while holding down the ALT key and you’ll see the “you clicked the button while ALT was pressed” message.
Dialog>Dialog1 object Dialog1: TForm Left = 467 Top = 241 HelpContext = 5000 BorderIcons = [biSystemMenu] Caption = 'CustomDialog' ClientHeight = 212 ClientWidth = 431 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] KeyPreview = True OldCreateOrder = True ShowHint = True OnTaskBar = False PixelsPerInch = 96 TextHeight = 13 object MSButton1: tMSButton Left = 147 Top = 47 Width = 75 Height = 25 Caption = 'MSButton1' TabOrder = 0 DoBrowse = False BrowseStyle = fbOpen end end EndDialog>Dialog1 AddDialogHandler>Dialog1,MSButton1,OnClick,doClick AddDialogHandler>Dialog1,,OnKeyDown,doKeyDown AddDialogHandler>Dialog1,,OnKeyUp,doKeyUp Let>CTRL_DOWN=FALSE Let>ALT_DOWN=FALSE Show>Dialog1,r SRT>doClick //button was clicked, which key is down If>ALT_DOWN=TRUE MessageModal>You clicked the button while ALT was pressed Else MessageModal>You clicked the button without pressing ALT Endif END>doClick SRT>doKeyDown //is CTRL key pressed If>DoKeyDown_Key=17 Let>CTRL_DOWN=TRUE Endif //is ALT key pressed If>DoKeyDown_Key=18 Let>ALT_DOWN=TRUE Endif END>doKeyDown SRT>doKeyUp //was ctrl released If>doKeyUp_Key=17 Let>CTRL_DOWN=FALSE Endif //was ALT released If>doKeyUp_Key=18 Let>ALT_DOWN=FALSE Endif END>doKeyUp
You might be wondering how I know that the CTRL key is number 17 and the ALT key is 18. To find out the value of a key press the simplest way is just to pop a breakpoint at the start of the doKeyDown subroutine, then run the script in the debugger, press a key and look in the watch list to see what value is produced. Alternatively there’s a table here. Note that ALT is known as VK_MENU and CTRL is VK_CONTROL.
July 26, 2012
Retrieving all Items and Indexes from a Listbox
Some people have reported issue with the GetListItem command failing to work with some applications. While we investigate the issues we’ve created a small workaround. This is a .Net tool called ListInspector. It’s a command line tool which you can pass a listbox handle to and it will dump out a list of all the items and their indexes.
Here’s an example of use:
//get handle of putty and find handle of listbox GetWindowHandle>PuTTY Configuration,pHwnd FindObject>pHWnd,ListBox,,1,hWnd,L,T,R,B,res //run ListInspector to get a list of items Let>RP_WINDOWMODE=0 Let>RP_WAIT=1 Run>cmd.exe /C "%SCRIPT_DIR%\ListInspector.exe" %hWnd% > %TEMP_DIR%\items.txt ReadFile>%TEMP_DIR%\items.txt,items //find the index of the item we want Let>item_required=Pluto Server RegEx>\d*(?=:%item_required%),items,0,matches,nm,0 If>nm>0 Let>index=matches_1 MessageModal>Index of %item_required% is %index% Endif
The script here gets the items of the session selection box in the Putty SSH client. The first thing it does is get Putty’s window handle, then find the handle of the list box we want to inspect. It then runs ListInspector.exe and passes the listbox handle to it, piping the results to a temporary file. It then reads in the file and uses a RegEx to extract the index for the item we are looking for.
This is useful for those listboxes which don’t allow us to “drill down” properly and where the items and order of items may be dynamic, leaving us with no easy way to automate the selection of an item. Using ListInspector we can get the item’s index and then we know how many times we need to “Press Down” (first using Home to make sure we’re at the top).
You can download ListInspector here. The zip file contains the example script and a small readme file. .Net 2.5 or higher is required.
June 25, 2012
Sorting a String
In the forums this morning PepsiHog asked for some code to sort a string of numbers.
We have a built-in method for sorting arrays. But here we want to sort a string of characters. E.g. we want the string “4731” to end up as “1347”.
I ended up writing a reusable subroutine which will sort any length string alphanumerically. I thought I’d post it here as it demonstrates a few useful ideas:
//CharSort, parm1: the string to sort, parm2: name of variable to return SRT>CharSort Let>LOCALVARS=1 Let>tmp= RegEx>.,CharSort_Var_1,0,matches,nm,0 ArraySort>matches Let>x=0 Repeat>x Let>x=x+1 Let>this_char=matches_%x% Let>tmp=%tmp%%this_char% Until>x=nm Let>LOCALVARS=0 Let>%CharSort_Var_2%=tmp END>CharSort
To use the subroutine do something like:
Let>MyString=317536492750930 GoSub>CharSort,MyString,{"MyString"} MessageModal>MyString
This demonstrates a number of things:
– How we can call a subroutine with parameters, and how we can use one of those parameters to set a “result” variable (note the second parameter is set to a string value, and the subroutine sets a variable with this name to the result).
– How we use LOCALVARS to ensure the subroutine has local scope (except when we need to set the return variable). This is important for functions we might re-use elsewhere and drop into other scripts, as we would want to avoid the subroutine modifying any existing script variables. By using local scope we make sure the variables used in the subroutine are local only to that subroutine.
The subroutine works first by splitting the string into an array of characters. It does this by using a Regular Expression which identifies all characters (“.”). RegEx returns an array of matches, which will therefore be all the characters in the string (because “.” means match any character). We can then sort this array using ArraySort and then finally loop through the array and concatenate each item back into a string.