Freeze mouse and keyboard input temporarily from VBA - vba

I have a Word VBA procedure which takes abnormally long time to complete bookmark jumps (probably the code opens the small but many frames containing document a second time, what takes ~6 seconds upon the first positioning function call).
The code then send keys and moves the mouse to perform simple operations in a freeware window (CodeTwo QR …). In order the sequence to work properly, I had to insert Sleep codelines. Thus, the whole operation is slow.
However, any other key press or mouse movement interferes with the code and leads to errors (e.g. when the mouse isn't on the very precise position to click a button). Thus, I am looking for a method to inhibit any unintentional interference from the user. Note that the code is started by clicking a control button, the user's hand is still on the mouse…
SetWindowsHookEx() seems to be a possibility. There is no need to callback the trapped trash (these functions don't appear to be implemented in VBA). But I am a hobby programmer and have no idea how to implement this. A piece of code to try would be highly welcome, thanks in advance !
Of course a PowerShell command could eventually help, too. I have seen following code on Reddit, but it needs to be run as adminstrator (is that possible from vba ?) and even then it performs just nothing and terminates after < 1s while the mouse still moves and keystrokes further go to the editor …
param([switch]$Elevated)
function Test-Admin {
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Test-Admin) -eq $false) {
if ($elevated) {
# tried to elevate, did not work, aborting
} else {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
}
exit
}
'running with full privileges'
$code = #"
[DllImport("user32.dll")]
public static extern bool BlockInput(bool fBlockIt);
"#
$userInput = Add-Type -MemberDefinition $code -Name UserInput -Namespace UserInput -PassThru
function Disable-UserInput($seconds) {
$userInput::BlockInput($true)
Start-Sleep $seconds
$userInput::BlockInput($false)
}
Disable-UserInput -seconds 10 | Out-Null
Otherwise I tried to use AutoIt script language but the generated .exe files lack rights (even when run as administrator). Surely I didn't write an adequate script. Can somebody advice me on suitable .au3 code which would work for disabling external input and enabling it again after a few seconds ?

What I was (and still do) looking for is a Windows method or a script language which I can control myself.
However, I found the small freeware KeyFreeze (909 KB) which does exactly the desired work perfectly:
Sub Finish() ' (in a Word form module)
Dim KF_PID As LongPtr
' …
Me.MousePointer = fmMousePointerHourGlass
Me.WaitMessage.Visible = True
DoEvents
KF_PID = Shell("C:\Programs(portable)\KeyFreeze\KeyFreeze_x64.exe", vbNormal) ' ×32 version available, too
' starts minimized, if desired without any message
' …
' critical part of the sub with many slow commands:
' Selection.GoTo What:=wdGoToBookmark, PasteIntoBookmark(),
' SetCursorPos, Sleep, Mouse_Event and SendKeys commands
' …
Call Shell("C:\Windows\SysWOW64\taskkill.exe /f /pid " & KF_PID)
End Sub
Note that this freeware can be easily configured to block keyboard and mouse already on its start, thus it is not even necessary to send the blocking key sequence (by default <CTRL><ALT>F, configurable). It is also not necessary to send the same key sequence again to unlock, as killing the process is sufficient to reactivate keyboard and mouse: one doesn't have to bother about which window has the focus.
I would of course prefer to call Windows functions, but I believe that this freeware can be trusted as VirusTotal reports "No security vendors flagged this file as malicious (0/65)".
BlueLife KeyFreeze v1.4 (Block keyboard and mouse)

Related

Malicious macro warning on code that is doing exactly what I want it to

I am building an energy system model that is using a combination of VBA and Python (Pyomo). I am using a small piece of VBA code to call a command line, which in turn uses Pyomo. This code is looping to execute a separate Pyomo optimisation for each day.
The code is working perfectly, apart from the message in the image below being triggered. When I step through the code slowly, the message seems to be triggered by the following line of code which is calling a command line:
Call Shell("cmd.exe /S /c" & "pyomo solve --solver=glpk " & Pyomo_file & " " & DAT_file & " --save-results " & Results_file, vbHide)
I have also tried to run this with the string being simplified down into a single string variable, which I called "Command", but the message below was still triggered.
The hyperlink in the message box (https://support.microsoft.com/en-us/office/malicious-macros-were-found-9e461c61-69d1-4ea9-a386-9ad0deaccfdd?ns=excel&version=90&syslcid=1033&uilcid=1033&appver=zxl900&helpid=56779&ui=en-us&rs=en-us&ad=us) suggests that this issue is triggered by AMSI and that my computer thinks this code is malicious. It is not - it is doing exactly what I want it to!! Digging further I think (but may be wrong) that the virus checks are being triggered by the fact that this string is a combination of so many inputs - the virus checker thinks this is obfuscation.
A few further points:
This line of code is within the daily loop I mentioned above. The
error does not get triggered until the loop has already happily
executed this line >30 times.
Bizarrely it seems to get thrown out on
the same date each time I try to run the loop. If I skip that date, it falls over again at a later date...
I have set my macro security settings to "enable all macros" and this
still happens. I only have the one Office 365 licence in my
organisation (my own company) so I don't think there can be any odd admin
permissions causing this problem.
Finally, I have tried to define the folders I am using as 'safe locations' and this doesn't help either.
Ideally, I would like to just disable the AMSI software while this command line is being executed. But I don't know how to do this of whether it is even possible. If not, any alternative ideas?!
I tried to run the MpCmdRun.exe script suggested by MS in this post, and it seems to have worked. It's possible that is just fluke, but it is working for now! Fingers crossed!

VB.net app will only run as Administrator only

I made a few improvements to code that has been functioning without issues. On the development, the app runs with a problem. Installed on a test computer and it does not run unless I right click and run as administrator. The below code is where gets not running as Administrator.
The for loop is adding file names to a list. Administrator the program just ends no errors just quits. i counter gets to 3 of a total of 6 files. Never gets through to post the message "Did it make it here". But does post the message inside the for loop. i counts to 3. If run as admin then no issues. All file names are short and basic with no special characters. The files locations is Application.CommonAppDataPath. The loop is called from Form load.
Hoping someone could tell me what is going on.
Try
Selected_Machine = XMLReadSetting("Machine", "Selected", "Selexx")
For i = 0 To UBound(files)
MessageBox.Show("Here 2 i " & i & " File Name" & files(i))
cboMachine.Items.Add(IO.Path.GetFileNameWithoutExtension(files(i)))
If Selected_Machine = IO.Path.GetFileNameWithoutExtension(files(i)) Then
cboMachine.SelectedItem = IO.Path.GetFileNameWithoutExtension(files(i))
End If
Next
Catch err As SyntaxErrorException
MessageBox.Show(err.Message)
End Try
MessageBox.Show("Did it make it here?")
Found the issue. Inside the for loop it make a change that calls the text changed event of the list box. In the text changed event I write data to a settings file. The settings file did not have write access. Not sure how that happend but problem solved
Thank you all for the help.

AppleScript path being replaced at compile time (New Keynote scripting trouble)

We've got an applescript that tells keynotes to delete slides based on criteria. The new keynote does not have an applescript dictionary, but leaves the old keynote in a subdirectory. So I'm trying to tell AppleScript to talk with the older app rather than the new one.
If I just
tell application "Clean Install:Applications:iWork '09:Keynote.app"
It works, but it doesn't recognize any of the keynote dictionary terms. (delete a slide). So I need to pull out my old friend "using terms from". The challenge here is that this is a precompile directive, so you have to use a string literal, which I don't have on the end user's machine due to different hard drive names.
Ok, still have a plan here. I will write out a new applescript file with the 'using terms from application "Clean Install:Applications:iWork '09:Keynote.app"' and then execute that file... Genius... except for the fact that when AppleScript compiles this line:
using terms from application "Clean Install:Applications:iWork '09:Keynote.app"
Gets changed to:
using terms from application "Keynote"
Which of course calls the new keynote's dictionary which is empty.
Any thoughts on how to keep applescript from helping me out in this way? (or is there a better plan?)
full code:
using terms from application "Clean Install:Applications:iWork '09:Keynote.app"
--using terms from application "Clean Install:Applications:iWork '09:Keynote.app"
tell application "Clean Install:Applications:iWork '09:Keynote.app"
activate
end tell
end using terms from
many thanks!
I'm flyin' blind here (don't have keynote) ... but have you tried using a pre-defined app string as a variable and raw event codes?
You can use Smile to easily get raw event codes by
making a new script window in Smile;
using the "tell" menu item from the Action menu to make that script window application-specific (no tell
block needed);
write a line of code; select line of code
then use the "Copy translate" menu item (cmd-shift-C)
to copy the raw event codes
pasting that raw event code code into a different window that has your working (normal tell block) script
This is what it looks like when I do this for the Mail app:
set origMail to "MyDrive:Applications:Mail.app"
tell application origMail
delete («class mssg» 1 of «class mbxp» "INBOX" of «class mact» 1)
end tell
( When put in a normal tell block, that line of code would be "delete (message 1 of mailbox "INBOX" of account 1)" )
I haven't tried this but I think it will work... when you compile your code with "using terms from" just place your new version of Keynote in the trash. That should force it to use the old version of Keynote's dictionary and your code should compile. If you then save that code as an "applescript application" then it should work on anybody's computer without need to recompile. NOTE: you may need to restart your computer before this trick would work.
Then you just have the problem of targeting the right application on the user's computer because they too might have both versions. Here's some code to find the path to older versions of Keynote and how you would target that.
set lsregister to "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister"
set appName to "Keynote.app"
set nonapplescriptVersion to "6.0"
-- get the path to all Keynote apps
set appPaths to paragraphs of (do shell script lsregister & " -dump | grep " & quoted form of appName)
-- find the one with a version number less than the nonapplescriptVersion of Keynote
set appPath to missing value
repeat with anApp in appPaths
try
-- extract the path
set AppleScript's text item delimiters to "/"
set tis to text items of anApp
set thisPath to "/" & (items 2 thru end of tis) as text
set AppleScript's text item delimiters to ""
-- check the version
if (version of application thisPath) is less than nonapplescriptVersion then
set appPath to thisPath
exit repeat
end if
end try
end repeat
if appPath is missing value then error "Needed application version not installed."
-- use the older version
using terms from application "Keynote"
tell application appPath
activate
-- do whatever
end tell
end using terms from

Stop vbscript deactivating current window when running?

I'm trying to write a simple script to fill in login details on a program that requires signing in using a keyboard shortcut.
set wshshell = wscript.CreateObject("wscript.shell")
WshShell.SendKeys "username{tab}passowrd{enter}"
This works as desired on my work computer (xp) but on my home computer (windows 8) the keyboard shortcut deactivates the current window so that the details are not entered.
Any help would be greatly appreciated.
You may want to look into the shell's AppActivate method. You could write something like this:
boolSuccess = False
do until boolSuccess = True
boolSuccess = WshShell.AppActivate("AppName")
WshShell.sleep 500
loop
WshShell.SendKeys "username{tab}password{enter}"
This will move the focus to your target app, and then send the keys. The tragically kludgey part is that you may need that 'sleep' between the AppActivate and the SendKeys to be sure that your target app has time to receive the focus.

Applescript: GUI Programming for recursively printing to PDF for Appleworks

The Goal: thousands of old Clarisworks and Appleworks documents need to become PDFs.
The Problem: Scripting Pages is not an option; it doesn't preserve formatting reliably. It must be Appleworks. This script works, when it works; but it skips files for reasons I cannot identify.
set appleworksFolder to choose folder
tell application "Finder"
set folderItems to (files of entire contents of appleworksFolder)
repeat with I from 1 to number of items in folderItems
set the_doc to item I of folderItems
set doc_name to name of the_doc as text
(* Some files are missing metatags and try to save as "Untitled Document",
this block ensures a file name is unique, later *)
tell application "Finder"
set the clipboard to doc_name & ".pdf"
end tell
(* Each file exists in a folder with a path.txt file that will later
be used to put the file back where it was originally stored prior
to this conversion process *)
if name of the_doc is not "path.txt" then
try
tell application "Finder"
(* Many files no longer have name extensions and appear as UNIX
executables if not repaired *)
try
set nmex to name extension of the_doc as text
on error
set nmex to "ok"
end try
if nmex is not "cwk" or "CWK" then
set the_doc_str to the_doc as text
set doc_path to POSIX path of the_doc_str
do shell script "mv '" & doc_path & "' " & "'" & doc_path & ".cwk'"
end if
delay 1
(* In case Appleworks hangs or otherwise bungs up, I force-quit
it at the end of the script; this ensures its closed before
it tries to open the next file *)
if (application process "Appleworks 6" of application "System Events" exists) then
do shell script "killall 'LaunchCFMApp'"
delay 1
end if
tell application "AppleWorks 6"
open the_doc
end tell
(* Some of the documents are huge, this delay gives the app time to load
since this is all GUI-scripted *)
delay 5
tell application process "Appleworks 6" of application "System Events"
(* This is where I think I am encountering problems; there are two
possible warnings that may or may not appear on opening the doc;
that Appleworks needs to append a version number to the file (if
its old) or that the file may be damaged and thus would need to be
skipped. I get system beeps sometimes in this part, but I don't know
why! *)
if (button "OK" of window 1 of application process "AppleWorks 6" of application "System Events" exists) then
delay 0.5
keystroke return
delay 0.5
end if
delay 2
if (button "OK" of window 1 of application process "AppleWorks 6" of application "System Events" exists) then
delay 0.5
keystroke return
delay 0.5
end if
delay 2
(* If the document loads, the Appleworks welcome pane won't be there;
this part of the script works flawlessly, when it happens. Sometimes
documents are outside of print margins, hence the press-ok-button
conditional *)
if not (window "Starting Points" of application process "AppleWorks 6" of application "System Events" exists) then
tell application process "Appleworks 6" of application "System Events"
keystroke "p" using command down
delay 1
click menu button "PDF" of window "Print" of application process "AppleWorks 6" of application "System Events"
delay 1
click menu item "Save as PDF…" of menu "PDF" of menu button "PDF" of window "Print" of application process "AppleWorks 6" of application "System Events"
delay 1
keystroke "v" using command down
click button "Save" of window "Save" of application process "AppleWorks 6" of application "System Events"
delay 8
keystroke "w" using command down
delay 0.5
if (button 1 of window 1 of application process "AppleWorks 6" of application "System Events" exists) then
delay 0.5
keystroke "d" using command down
delay 0.5
end if
delay 0.5
end tell
end if
do shell script "killall 'LaunchCFMApp'"
delay 3
end tell
end tell
end try
end if
end repeat
end tell
I want to this baby to just run through a couple of thousands files over a weekend and create PDFs, but each time I run it overnight I find a few hundred correctly processed documents, hundreds or thousands of skipped documents, and often a print dialogue for the script itself, which obviously comes from using Command+P outside of an Appleworks context. I'm an Applescript noob, to be sure, this has been driving me nuts for weeks!
This is not really a solution to your problem, as it is highly specific and tailored, but I have some possibly useful tips:
You don't need to close the "Starting Points" window. It will be dismissed when you tell AppleWorks to open a document.
Try to use as few simulated keystrokes as possible. For instance, instead of simulating cmd+P to open the Print dialog, it is better to simulate a click on the application's Print… command inside its File menu:
click menu item "Print…" of menu "File" of menu bar 1 of application process "AppleWorks 6"
and instead of performing a cmd+W to close the document, the proper way is to do
tell application "AppleWorks 6" to close front document saving no
If you do need to simulate keystrokes, ensure that they are received by the correct application, by activating it just before the keystroke command with a tell app "AppleWorks 6" to activate, just in case it wasn't the frontmost app at that point.
When checking for additional dialog windows, it's a bad idea to use if (button 1 of window 1 of (*...*) exists), because on my machine, for instance, checking this while the "Print" dialog was open caused the script to hang for minutes (possibly for good), and because it's always advisable to address a window by name rather than number (since window 1 is simply the frontmost window, and that can quickly become another). So, it's better to check if a window named such and such exists.
In order to process thousands of files where you expect errors to happen, you need to restructure your code and rethink the error handling. For instance, you could log error information to a text file in order to see which files have been skipped and why. To separate program logic from the error handling and details, you could use handlers, so your main loop could look like this:
set logfile to alias "Macintosh HD:Users:user:Desktop:errors.log"
open for access logfile with write permission
repeat with the_doc in folderItems
try
open_with_appleworks(the_doc)
print_to_pdf()
close_document_and_quit_appleworks()
on error error_message
write error_message & "\n" to logfile
close_document_and_quit_appleworks()
end try
end repeat
close access logfile
In a similar way, you can log success messages, too. In the single handlers, then, you are supposed to specify details about how exactly to do every step. They can also provide some error information to be logged:
to open_with_appleworks(the_doc)
tell app "AppleWorks 6"
try
(* do your stuff here *)
on error err_msg
(* re-signal the error to main loop! *)
error "Failed to open " & (the_doc as text) & err_msg
end try
end tell
end open_with_appleworks
Hard-coded delays slow down your script considerably: while only some of your documents might take a few seconds to open, your script will always wait 5 seconds – even if the document opens instantaneously. It's better to have a loop check whether the document has opened yet:
repeat while not (exists front document)
-- just wait
-- or, delay 0.1 -- if you really want to
end repeat
The same holds for nearly all your other delays, most of them can be avoided, as they will be much shorter or longer in practice. Instead of delaying half a second, hoping that the warning or dialog will have appeared by then, it's better to check whether it's there or not.
(one last tip and then I'll be quiet) Unfortunately, you seem to already have gone through the trouble of copying all the files to be processed into one folder, remembering in some "path.txt" where they came from... This can be avoided if the files are in sub-folders of some folder and can be found by a filter: You just create a "smsart folder" with that specific search (example), then say in your AppleScript set theFiles to choose file with multiple selections allowed, then select all files in that particular smart folder, and you're good. The script can then easily find out where a file f is by asking application "Finder" to get parent of f (that will return a Finder file reference, which can be easily converted to an alias and to a POSIX file address). Then, since you know the name and location of the PDF file generated by "printing" the document, you can move that PDF to the folder where its source document is.