Jump to content
Macro Express Forums
TwoWheels

Can A Macro Be Run Via Windows Api?

Recommended Posts

I've followed the steps outlined in the Help topic "Macro Activation" and the macro I'm trying to run doesn't run.

 

C++ code below. I am trying to invoke a macro whose nickname is "zquickly".

 

Has anybody actually done this?

 

Thanks,

Rich

 

#include <windows.h>
#include <stdio.h>
int main()
{
 HWND handle = FindWindow(NULL, "Macro Express Player");
 if(handle == NULL) {
   printf("FindWindow returned NULL handle.\n");
   exit(0);
 }
 // Invoke the desired macro by sending Windows messages.
 // Evidently we're transmitting a NULL terminated string
 // to the player program, character-by-character.
 UINT nicknameConstant = WM_APP + 20;
 char *macroname = "zquickly";
 for(int pm = 0; pm < strlen(macroname); pm++) {
   PostMessage(handle, nicknameConstant, macroname[pm], 0);
 }
 PostMessage(handle, nicknameConstant, 0, 0);
 return 0;
}

Share this post


Link to post
Share on other sites

I've been trying this in VBA (Microsoft Access) but also have not yet been able to get it to work.

 

I'll repost here if I ever get it figured out.

 

I did find this in the KB which indicates the characters streamed to Macro Express should be the ordinal values of the characters in the Macro nickname:

 

http://www.macros.com/faq//4.23.html

 

Looks like they are casting the character to its ordinal value.

 

I haven't yet figured out how to do that in MS Access VBA.

 

Jace

Share this post


Link to post
Share on other sites

Well, I finally found the ASC function in VBA and wrote this sub:

 

===================================================

Sub RunMacro(Command As Long, MacroName As String)

'Runs a macro express macro

Dim hwnd, I, Result As Long

 

hwnd = FindWindow(vbNullString, "Macro Express Player")

 

For I = 1 To Len(MacroName)

Result = PostMessage(hwnd, Command, Asc(Mid(MacroName, I, 1)), 0)

Next I

Result = PostMessage(hwnd, Command, 0, 0)

 

End Sub

===================================================

 

But Macro Express does not repspond. :(

 

Ideas anyone??

Share this post


Link to post
Share on other sites

I use the following VBA code to start one of my ME macros

 

Private Sub cmd_CreateAcct_Click()
On Error GoTo Err_cmd_CreateAcct_Click

Call Shell("C:\Program Files\Macro Express3\MeProc.exe //MXEN:\Info Resources\Client Electronic Solutions\TOROL\USER DOCUMENTATION\Create Torol Account.mxe", 1)

Exit_cmd_CreateAcct_Click:
   Exit Sub

Err_cmd_CreateAcct_Click:
   MsgBox Err.Description & vbCrLf & "Error #" & Err.Number
   Resume Exit_cmd_CreateAcct_Click
   
End Sub

Share this post


Link to post
Share on other sites

jowensii,

 

The approach you're using is basically what I'm trying to get away from. In C++, I'm currently spawning the meproc.exe program.

 

The Windows API approach should be a fair amount faster. I've written a stock market trading program, and shaving a quarter second off my execution time is quite important to me.

 

Rich

Share this post


Link to post
Share on other sites

There is an error in the help. The command sent to PostMessage should be WM_USER+20, not WM_APP+20 as documented. There was a version of Macro Express that used WM_APP+20 but when it was changed to WM_USER+20 the help file was not updated.

 

This example should work for C++

 

HWND hwnd = FindWindow(“TMainWin”, “Macro Express Player”);
if( IsWindow(hwnd) ) {
 for(int x = 0; x < strlen(s); x ++)
   PostMessage(hwnd, WM_USER + 20, s[i], 0);
 PostMessage(hwnd, WM_USER + 20, 0, 0);
}
else
 MessageBox(GetForegroundWindow(), “Macro Express is not running”, “Error”, 0);

I created an application in Delphi that runs a macro using the Windows PostMessage API. If anyone is interested in an example written in Delphi that I know works, I can provide it.

Share this post


Link to post
Share on other sites

Kevin,

 

Thank you so much! It does indeed work. I made that one change to the source code posted earlier, and it worked perfectly.

 

Now that the macro activation is successful, does this Windows API technique also allow for passing command line parameters? I have been passing data in to my macros by writing it to an ASCII file and reading the file from MacroExpress, but that's probably lots slower than passing parameters.

 

I really, really like Macro Express.

 

Rich

Share this post


Link to post
Share on other sites

Here here!!! I got mine working too. Thanks Kevin.

 

Same question. You guys cook up a way to pass parameters without all the ASCII file nonsense or shoving stuff into the clipboard and parsing it out again in the macro?

 

Thanks.

 

Jace

Share this post


Link to post
Share on other sites

jrgreenman,

Would you be able to post an example of your working VBA code?

 

TwoWheels and jrgreenman,

TwoWheels  - The Windows API approach should be a fair amount faster.

This question is for both. Is the Windows API route really that much faster than starting ME macro with MeProc.exe and command line switches?

Share this post


Link to post
Share on other sites

randallc:

 

Well, anytime you have to write something to the hard disk, that's relatively slow. But it's still milliseconds (instead of microseconds). But I have a consicientious objection to it because it's inelegant. Same w/ passing stuff thru the clipboard (which should much faster in any case since it does not involve writing to disk).

 

jowensii:

 

Would you be able to post an example of your working VBA code?

I sent it to Kevin Heaton who is packaging it up for public consumption, but here's what I'm using:

 

===================================================

Private Declare Function FindWindow _

  Lib "User32" Alias "FindWindowA" ( _

      ByVal lpClassName As String, _

      ByVal lpWindowName As String) As Long

         

Private Declare Function PostMessage _

  Lib "User32" Alias "PostMessageA" ( _

      ByVal hwnd As Long, _

      ByVal wMsg As Long, _

      ByVal wParam As Long, _

      ByVal lParam As Long) As Long

 

Private Sub RunMacro()

  'Runs a macro express macro

 

  Const WM_USER = &H400

  Const Command = WM_USER + 20

  Const Macroname = "Macroname"  'Replace this with your macro name

 

  Dim hwnd, I, Result As Long

 

  hwnd = FindWindow("TMainWin", "Macro Express Player")

  For I = 1 To Len(Macroname)

    Result = PostMessage(hwnd, Command, Asc(Mid(Macroname, I, 1)), 0)

  Next I

  Result = PostMessage(hwnd, Command, 0, 0)

 

End Sub

===================================================

 

Is the Windows API route really that much faster than starting ME macro with MeProc.exe and command line switches?

 

Again, starting MeProc.exe involves the hard disk. And if, for example, your VBA code is looping through a long file processing each line, you could be invoking MeProc.exe a zillion times.

 

But in many cases, and for a non-real-time application, I'm sure it's perfectly serviceable.

 

Jace

Share this post


Link to post
Share on other sites
You guys cook up a way to pass parameters without all the ASCII file nonsense or shoving stuff into the clipboard and parsing it out again in the macro?
This is possible using the MEProc approach to launching macros along with using the /V command line option. If you use this technique then you will need to add a Variable Restore All or Variable Restore Text command to read the values passed from the command line.

 

At this point in time, there isn't an API message to pass variables.

 

Is the Windows API route really that much faster than starting ME macro with MeProc.exe and command line switches?
Again, starting MeProc.exe involves the hard disk. And if, for example, your VBA code is looping through a long file processing each line, you could be invoking MeProc.exe a zillion times.

 

But in many cases, and for a non-real-time application, I'm sure it's perfectly serviceable.

You have hit on the reason for MEProc in the first place. The executable file for the Macro Express Player is named MacExp.exe. The command line parameters to invoke macros (/A) or to pass parameters (/V) work with MacExp.exe or MEProc.exe. However, since MacExp.exe is over 3M in size and MEProc.exe is only 159K, MEProc.exe can be loaded much faster than MacExp.exe. Just how much faster depends on the speed of your computer, the speed of your hard drive, and whether or not the executable is cached by Windows.

 

So, if you need to run a macro where speed is not an issue, then you could use (not necessarily recommended):

macexp.exe /AMacroName /VT1:Variable One /VN23:150 /VD6:22.23

(edited)

If you need to run a macro where speed is a little bit of an issue then you could use (recommended):

meproc.exe /AMacroName /VT1:Variable One /VN23:150 /VD6:22.23

(edited)

But if you need to run a macro (or macros) and speed is a big issue then you need to use the API calls. Before calling the macro you need to save the variable values in the Registry, or in an INI, TXT or CSV file.

 

When (or if) Windows caches the meproc.exe file, the speed difference between techniques may not be much of an issue.

Edited by kevin

Share this post


Link to post
Share on other sites

Kevin,

 

meproc.exe /AMacroName /VT1:Variable One /VN23:150 /D6:22:23

 

What's that last thing at the end? I don't see any documentation for the /D switch. If that is intended to be an example of passing a decimal variable, shouldn't it be /VD? And if so, what's the second colon (betw the 22 and the 23)?

 

Seems like the last parameter oughtta read: /VD6:22.23

 

Yes? No?

 

Jace

Share this post


Link to post
Share on other sites

Got a new question. Well, it's kind of related and since we're already gathered here, why start a new thread? :)

 

Anyway, I now notice that if I use meproc (yes, I concede passing variables this way is fewer lines of code) to run a macro from VBA, that the meproc process exits upon _starting_ the macro. That is, it runa asynchronously.

 

If my VBA app depends on the results of the macro after it concludes running, I'm outta luck. My app races ahead even though the macro is still running.

 

Is there a way to tell meproc to run synchronously, that is, NOT to exit until the macro concludes execution?

 

Jace

Share this post


Link to post
Share on other sites
meproc.exe /AMacroName /VT1:Variable One /VN23:150 /D6:22:23

What's that last thing at the end?

A typo. :blink:

 

This should have been

meproc.exe /AMacroName /VT1:Variable One /VN23:150 /VD6:22.23

Thanks for pointing out this error. To avoid confusion for other readers I edited the orginal post.

Share this post


Link to post
Share on other sites
Anyway, I now notice that if I use meproc ... to run a macro from VBA, that the meproc process exits upon _starting_ the macro. That is, it runs asynchronously.

 

If my VBA app depends on the results of the macro after it concludes running, I'm outta luck. My app races ahead even though the macro is still running.

 

Is there a way to tell meproc to run synchronously, that is, NOT to exit until the macro concludes execution?

No.

 

I recommend that you have your macro do something that your VBA app looks for such as create a file, change the content of a file, or write something to the registry or clipboard. Have your VBA app loop until the 'something' occurs. But, make sure you program a timeout so if the macro fails to write the 'something' that your VBA app doesn't lock up.

Share this post


Link to post
Share on other sites

I wonder if I've found a bug in Meproc /V?

 

I have a macro M1 with 2 lines in it:

Variable Restore All Variables

Text Box Display %T1%

 

I've run this:

meproc /AM1 /VT1:Now is the time

 

and M1 correctly displays the value of the passed in %T1%

 

Now I run this:

meproc /AM1

 

and the same value is displayed by M1!

 

Shouldn't %T1% (and all variables) have been reinitialized to empty?

Share this post


Link to post
Share on other sites

Hello Paul!

 

I think what you are seeing is correct. When a Variable Restore is the first command in a macro, it retrieves the values that were last placed in the memory space. As a side note, when a macro ends the memory variables remain in the memory space until a new macro is fired. This is why you can View Memory Variables in the debug window after a macro stops.

 

What I believe happened in your case, is that your first call to meproc pushed "Now is the time" into the memory space prior to the first line (Variable Restore) firing, therefore, the text string is restored and displayed. It remains in the memory space until the next macro is run. However, since the first command to fire is Variable Restore, it retrieves the last value stored, which is "Now is the time".

 

The memory space is cleared automatically when a macro runs without Variable Restore as the first line or when Macro Express is terminated.

Share this post


Link to post
Share on other sites

I tried your suggestion, by moving the Variable Restore All Variables to the second line of the macro, where the first line now sets a different variable to some value. This didn't make any difference - calling the macro twice, the first time with parameters and the second time without, still enables the macro to "remember" the contents of the variables passed in the first time.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...