How can I run a Perl script through an ActiveX Control within Excel? - perl

I want to run a Perl script at the click of a button inside an Excel spreadsheet.
As the button is assigned to execute a VB macro, the macro should effectively execute the program.
As my first ever VB script, this is what I came up with, which throws up an irritating Run-time error '424': Object required error.
Sub RunPerlScript()
System.Diagnostics.process.Start ("perlscript.pl")
End Sub
How can I get this script to do what I want it to do?

I didn't write this snippet, but it would seem to be a good answer to your question.
From the article "How to execute a perl script from VBA":
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Sub RunPerl()
MsgBox ("Start of macro")
Dim oWsc As Object
Set oWsc = CreateObject("WScript.Shell")
Dim oExec As Object
Set oExec = oWsc.Exec("perl C:\temp\myperl.pl StartParam")
While oExec.Status <> 1 ' Wait for process
Sleep 1000
Wend
MsgBox ("STDOUT" + oExec.StdOut.ReadAll())
MsgBox ("STDERR" + oExec.StdErr.ReadAll())
Set oWsc = Nothing
MsgBox ("End of macro")
End Sub
You might need to install ActivePerl first

You cannot use .NET classes in a VBA macro.
Use the VBA Shell function.
Shell "p:\ath\to\perlscript.pl"
The documentation:
Shell Function
Runs an executable program and returns
a Variant (Double) representing the
program's task ID if successful,
otherwise it returns zero.
Syntax
Shell(pathname[,windowstyle])
The Shell function syntax has these
named arguments:
Part Description pathname Required;
Variant (String). Name of the program
to execute and any required arguments
or command-line switches; may include
directory or folder and drive. On the
Macintosh, you can use the MacID
function to specify an application's
signature instead of its name. The
following example uses the signature
for Microsoft Word: Shell
MacID("MSWD") windowstyle Optional.
Variant (Integer) corresponding to the
style of the window in which the
program is to be run. If windowstyle
is omitted, the program is started
minimized with focus. On the Macintosh
(System 7.0 or later), windowstyle
only determines whether or not the
application gets the focus when it is
run.
The windowstyle named argument has
these values:
Constant Value Description vbHide 0
Window is hidden and focus is passed
to the hidden window. The vbHide
constant is not applicable on
Macintosh platforms. vbNormalFocus 1
Window has focus and is restored to
its original size and position.
vbMinimizedFocus 2 Window is displayed
as an icon with focus.
vbMaximizedFocus 3 Window is maximized
with focus. vbNormalNoFocus 4 Window
is restored to its most recent size
and position. The currently active
window remains active.
vbMinimizedNoFocus 6 Window is
displayed as an icon. The currently
active window remains active.

ActiveState's PDK has PerlCtrl which lets you package a perl script as an ActiveX control. It gathers up your script and all dependencies into a tidy DLL.

Related

WinActivate does not work as expected. Re-activating focus to the starting window

I am having some serious struggles fully grasping the control on activating windows and forcing their focus and foremost position.
In order to debug a larger script I made a separate script to test the use of WinActivate and again I am observing frustrating behaviour as it either all together ignores the title I have defined or is failing in some other way. In the smaller test script I am simply requesting that the window in which the hotkey was triggered be set as active after another action, specifically an input box
Below is the simple code for testing:
F10::
SetTitleMatchMode, 1
DetectHiddenWindows, Off
WinGetTitle, startTitle, A
msgbox % "Start Title = <" . startTitle . ">"
;WinActivate, startTitle
inputbox, mode, Test box, Testing,,260,160
sleep 500
WinActivate, startTitle
Return
This code does not properly activate the starting window. For example I execute the hotkey in an empty notepad window and upon submitting blank into the input box the focus becomes notepad++ on my second monitor. The second time I press the hotkey from within notepad (or another application) notepad does not lose focus. In a third execution I begin from notepad again and after the input box appears I switch the focus to another window. I again submit blank to the inputbox but that new window remains the focus and notepad is not activated or brought to the foremost position.
Can someone please explain to me what is going on with WinActivate?
I was having similar frustration with unexpected results making a windows script host file and I think I must be missing some fundamental detail in windows.
You are trying to activate a window that start with the literal text "startTitle".
You forgot(?) to either enter expression syntax with % or use the legacy way of referring to a variable %startTitle% (please don't use legacy).
Extra stuff:
You shouldn't specify SetTitleMatchMode and DetectHiddenWindows inside your hotkey statement. There is no need (unless there actually is) to set those every time you hit the hotkey. Just specify them at the top of your script once.
Both of them are useless for you though, below I'll show why. Also DetectHiddenWindows is already off by default.
WinGetTitle is not good to use for this. What you actually want to do is get the hwnd of the window you wish by using e.g. WinExist().
And then refer to the window by its hwnd. Much better than working with window titles, and impossible to match the wrong window as well. To refer to a window by its hwnd, you specify ahk_id followed by the hwnd on a WinTitle parameter.
And lastly, the concatenation operator . is redundant. Of course you may prefer to use it, but in case you didn't know, it can just be left out.
Here's your revised code:
F10::
_HWND := WinExist("A")
MsgBox, % "Start hwnd = <" _HWND ">"
InputBox, mode, Test box, Testing,,260,160
Sleep, 500
WinActivate, % "ahk_id " _HWND
Return

How can I run Matlab .m file repetitive in background?

I have project that was wrote in MATLAB. It has Main file like Main.m, I want to run Main.m repetitive every 1 Second in background.
I don't want to see any display and opening windows from MATLAB.
How can I do this ?
There are two steps to achieve this. First write some m script which calls your Main function every 1s. You can use a loop like this one. Getting the time via toc is important in case your main function takes some time to compute. An alternative are timers, which avoid any time drift (The loop is typically slightly above 1s, the timer will be 1s on average).
Once your MATLAB knows what to do, the question is who to start it. There is the -batch option:
Execute MATLAB script, statement, or function non-interactively.
MATLAB:
Starts without the desktop
Does not display the splash screen
Executes statement
Disables changes to preferences
Disables toolbox caching
Logs text to stdout and stderr
Does not display dialog boxes
Exits automatically with exit code 0 if script executes successfully.
Otherwise, MATLAB terminates with a non-zero exit code.
statement is MATLAB code enclosed in double quotation marks. If
statement is the name of a MATLAB function or script, do not specify
the file extension. Any required file must be on the MATLAB search
path or in the startup folder.
Use the -batch option in non-interactive scripting or command line
work flows. Do not use this option with the -r option.
To test if a session of MATLAB is running in batch mode, call the
batchStartupOptionUsed function.
Example: -batch "myscript"
This means MATLAB will not open any window, instead you see any output in the calling command line. How it looks on LINUX:
x#y ~ $ matlab -batch "1+1"
ans =
2
Assuming there is a top-level function in Main.m that you would like to execute once every 1 second, one possibility is to start an instance of Matlab and create another script that calls your function in a forever loop with a 1 second pause (making sure that this other script has Main.m file in the PATH so it can see it)
function run_main_forever()
while true
my_function()
pause(1)
end
end
You can have a .bat file start matlab in the background and run the script like this:
matlab -nodesktop -nosplash -r "cd('C:\Path\To\'); run_main_forever();"
See this link for further details on launching MATLAB without the desktop: https://blogs.mathworks.com/community/2010/02/22/launching-matlab-without-the-desktop/

programmatically press an enter key after starting .exe file in Matlab

In Matlab I can start external .exe files that sometime have a pop up that requires an enter key pressed. For example:
system('C:\Program Files (x86)\WinZip\WINZIP32.EXE')
will start Winzip, and then in order to use it you need to pass the "buy now" pop up window by pressing enter.
Now my problem is not with winzip, I only gave it as an example (i use winrar anyway :).
How can I programmatically press an enter key in Matlab in such cases ? (I use win 7)
Can an event listener be used to solve that?
EDIT: The java.awt.Robot class indeed works on explorer, but not on any software that has a pop up window with an OK button that needs to be pressed. I don't know why it doesn't work for that. I gave the winzip example because I assume everybody has winzip/winrar installed in their machine. The actual software I have is different and irrelevant for the question.
There is a way using Java from Matlab, specifically the java.awt.Robot class. See here.
Apparently there are two types of programs, regarding the way they work when called from Matlab with system('...'):
For some programs, Matlab waits until the program has finished before running the next statement. This happens for example with WinRAR (at least in my Windows 7 machine).
For other programs this doesn't happen, and Matlab proceeds with the next statement right after the external program has been started. An example of this type is explorer (the standard Windows file explorer).
Now, it is possible to return execution to Matlab immediately even for type 1 programs: just add & at the end of the string passed to system. This is standard in Linux Bash shell, and it also works in Windows, as discussed here.
So, you would proceed as follows:
robot = java.awt.Robot;
command = '"C:\Program Files (x86)\WinRAR\WinRAR"'; %// external program; full path
system([command ' &']); %// note: ' &' at the end
pause(5) %// allow some time for the external program to start
robot.keyPress (java.awt.event.KeyEvent.VK_ENTER); %// press "enter" key
robot.keyRelease (java.awt.event.KeyEvent.VK_ENTER); %// release "enter" key
If your applications are only on Windows platform, you can try using .net objects.
The SendWait method of the SendKeys objects allows to send virtually any key, or key combination, to the application which has the focus, including the "modifier" keys like Alt, Shift, Ctrl etc ...
The first thing to do is to import the .net library, then the full syntax to send the ENTER key would be:
NET.addAssembly('System.Windows.Forms');
System.Windows.Forms.SendKeys.SendWait('{ENTER}'); %// send the key "ENTER"
If you only do it once the full syntax is OK. If you plan to make extensive use of the command, you can help yourself with an anonymous helper function.
A little example with notepad
%% // import the .NET assembly and define helper function
NET.addAssembly('System.Windows.Forms');
sendkey = #(strkey) System.Windows.Forms.SendKeys.SendWait(strkey) ;
%% // prepare a few things to send to the notepad
str1 = 'Hello World' ;
str2 = 'OMG ... my notepad is alive' ;
file2save = [pwd '\SelfSaveTest.txt'] ;
if exist(file2save,'file')==2 ; delete(file2save) ; end %// this is just in case you run the test multiple times.
%% // go for it
%// write a few things, save the file then close it.
system('notepad &') ; %// Start notepad, without matlab waiting for the return value
sendkey(str1) %// send a full string to the notepad
sendkey('{ENTER}'); %// send the {ENTER} key
sendkey(str2) %// send another full string to the notepad
sendkey('{! 3}'); %// note how you can REPEAT a key send instruction
sendkey('%(FA)'); %// Send key combination to open the "save as..." dialog
pause(1) %// little pause to make sure your hard drive is ready before continuing
sendkey(file2save); %// Send the name (full path) of the file to save to the dialog
sendkey('{ENTER}'); %// validate
pause(3) %// just wait a bit so you can see you file is now saved (check the titlebar of the notepad)
sendkey('%(FX)'); %// Bye bye ... close the Notepad
As explained in the Microsoft documentation the SendKeys class may have some timing issues sometimes so if you want to do complex manipulations (like Tab multiple times to change the button you actually want to press), you may have to introduce a pause in your Matlab calls to SendKeys.
Try without first, but don't forget you are managing a process from another without any synchronization between them, so timing all that can require a bit of trial and error before you get it right, at least for complex sequences (simple one should be straightforward).
In my case above for example I am running all my data from an external hard drive with an ECO function which puts it into standby, so when I called the "save as..." dialog, it takes time for it to display because the HDD has to wake up. If I didn't introduce the pause(1), sometimes the file path would be imcomplete (the first part of the path was send before the dialog had the focus).
Also, do not forget the & character when you execute the external program. All credit to Luis Mendo for highlighting it. (I tend to forget how important it is because I use it by default. I only omit it if I have to specifically wait for a return value from the program, otherwise I let it run on its own)
The special characters have a special code. Here are a few:
Shift +
Control (Ctrl) ^
Alt %
Tab {TAB}
Backspace {BACKSPACE}, {BS}, or {BKSP}
Validation {ENTER} or ~ (a tilde)
Ins Or Insert {INSERT} or {INS}
Delete {DELETE} or {DEL}
Text Navigation {HOME} {END} {PGDN} {PGUP}
Arrow Keys {UP} {RIGHT} {DOWN} {LEFT}
Escape {ESC}
Function Keys {F1} ... {F16}
Print Screen {PRTSC}
Break {BREAK}
The full list from Microsoft can be found here
There is a small javascript utility that simulates keystrokes like this on the Windows javascript interpreter.
Just create a js file with following code:
var WshShell = WScript.CreateObject("WScript.Shell");
WshShell.SendKeys(WScript.Arguments(0));
then call it from Matlab after the necessary timeout like this:
system('c:\my\js\file\script.js {Enter}');
Can't test here now, but I think this should work...
If you need to run a console-only program in a context that permits full DOS redirection, you can create a file called, say, CR.txt containing a carriage return and use the '<' notation to pipe the value into the program.
This only works if you can provide all the keyboard input can be recorded in the file. It fails dismally if the input has to vary based on responses.
An alternative is to duplicate the input (and possibly output) stream(s) for the program and then pipe data into and out of the program. This is more robust and can permit dynamic responses to the data, but will also likely require substantial effort to implement a robot user to the application.
Rog-O-Matic is an example of a large application completely controlled by a program that monitors screen output and simulates keyboard input to play an early (1980s) ASCII graphic adventure game.
The other responses will be required for GUI-based applications.
Python package pywinauto can wait any dialog and click buttons automatically. But it's capable for native and some .NET applications only. You may have problems with pressing WPF button (maybe QT button is clickable - not checked), but in such case code like app.DialogTitle.wait('ready').set_focus(); app.DialogTitle.type_keys('{ENTER}') may help. Your case is quite simple and probably some tricks with pywinauto are enough. Is your "app with popup" 64-bit or 32-bit?
wait and wait_not functions have timeout parameter. But if you need precisely listener with potentially infinite loop awaiting popups, good direction is global Windows hooks (pyHook can listen mouse and keybd events, but cannot listen dialog opening). I'll try to find my prototype that can detect new windows. It uses UI Automation API event handlers... and... ops... it requires IronPython. I still don't know how to set UI Automation handler with COM interface from standard CPython.
EDIT (2019, January): new module win32hooks was implemented in pywinauto a while ago. Example of usage is here: examples/hook_and_listen.py.

How to return to prompt after running an exe file compiled with mcc (Matlab compiler)

I have an executable created with mcc. The .m file has a simple function that reads and plots values. After I run it from DOS, it freeze without returning execution to DOS. 2 questions:
1) How can i return execution to dos? I tried "return" and "exit" commands but didnt help
2) How to close the dos windows? is the only way to use a batch file or can I do it with a command in the .m file?
thanks
A.
There are 2 scenarii:
If your run your matlab executable from a DOS window, the DOS window will not get control back until the program terminates. If the program generate matlab figures (plot, surf, etc...), the program will not return to the console until all of the figures are closed.
You may think it is a waste for a simple plot, but after all your figure could be a evolved gui with a lot of code to execute. Or even a simple figure with a closeRequestFcn. So in Matlab terms, your program may still have instructions to execute as long as a figure is opened, so it will not return until it sure there is nothing more to do.
If you simply double clicked on your executable, the DOS looking console which open with your program will have the same behaviour. It will not disappear until the program returns (so until all your figures are closed if relevant).
I am not sure about linux, versions, but if you are running on windows, there is a way to suppress the DOS looking console for your graphic applications. Look at the -e switch in the mcc options.
This switch will compile your program in a way that no DOS console is opened when you double click on your executable.
So to summarize, I would recommend:
If your program is a 'command line' type (a function that takes input from the console and return values to the same). => Compile with normal options, and execute it from a DOS window (you do not want the window to disapear as soon as the program terminates.)
If your program is a gui or even simple plotting functions, with no need for console interactions, then compile with the -e switch and execute it by double clicking the .exe file.
note that in case you use the -e switch, it is recommended to direct potential output to a logfile. Look at the mcc documentation for more info.
edit
If you really need the DOS console and some graphic output, run your program form the command window with the following syntax:
start /b YourProgram
This will start the program in "background mode" (use YourProgram & in Linux terminal). You will be able to do anything in this console window, and you will also see the output from your matlab executable.
It can be confusing because the output from your program will be added to the simple dos prompt and you may think you do not have the control but if you type any command it will work. You can even start many program this way and retain control in your console, but all the output will arrive in the same window and they may be difficult to differentiate.

How to execute a dos command from MATLAB and return control immediately to MATLAB (without spawning a dos window)

I want to execute a batch file in dos from MATLAB and have control returned immediately to MATLAB. However, I want to do this without opening up a dos window (or, at the very least, get the dos window to disappear at the end).
If I format my command like this...
s = dos('batchfilename.bat');
then MATLAB runs the batch file without opening up a dos window, but the MATLAB code has to wait for the return.
If I format my command like this...
s = dos('batchfilename.bat &');
Control is returned immediately to MATLAB, but it also displays the dos window, which I don't want. (It's also difficult to detect when the batch file is "done" when you do it this way)
Any help would be appreciated.
Use Matlab's External Interfaces support to get access to a lower level language's process control features.
.NET Version
Use the .NET System.Diagnostics.Process class. It'll let you run a process asynchronously, check for when it's exited, and collect the exit code. And you can optionally hide its window or leave it visible for debugging.
You can call .NET classes directly from M-code.
function launch_a_bat_file()
%//LAUNCH_A_BAT_FYLE Run a bat file with asynchronous process control
batFile = 'c:\temp\example.bat';
startInfo = System.Diagnostics.ProcessStartInfo('cmd.exe', sprintf('/c "%s"', batFile));
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; %// if you want it invisible
proc = System.Diagnostics.Process.Start(startInfo);
if isempty(proc)
error('Failed to launch process');
end
while true
if proc.HasExited
fprintf('\nProcess exited with status %d\n', proc.ExitCode);
break
end
fprintf('.');
pause(.1);
end
Java Version
The .NET version requires a Matlab new enough to have .NET support. Here's a Java-based version for older Matlabs, like OP turns out to have. Should also work on non-Windows systems with a bit of modification.
function launch_a_bat_file_with_java
%LAUNCH_A_BAT_FILE_WITH_JAVA Java-based version for old Matlab versions
batFile = 'c:\temp\example.bat';
cmd = sprintf('cmd.exe /c "%s"', batFile);
runtime = java.lang.Runtime.getRuntime();
proc = runtime.exec(cmd);
while true
try
exitCode = proc.exitValue();
fprintf('\nProcess exited with status %d\n', exitCode);
break;
catch
err = lasterror(); % old syntax for compatibility
if strfind(err.message, 'process has not exited')
fprintf('.');
pause(.1);
else
rethrow(err);
end
end
end
You may need to fiddle with the I/O in the Java version to avoid hanging the launched process; demarcmj notes that you need to read and flush the Process's input stream for stdout to avoid it filling up.
Try using the start cmdlet bundled with the Windows Command Interpreter.
You can probably do just system('start /MIN batchfilename.bat');
Put an exit command at the end of the batch file to make sure you aren't left with a (minimized) command prompt.