I have written an Excel COM Add-In in C++ for automation of Excel with VBA. It contains an own dialog showing some general informations about the Add-In.
Now i create a button in Excel that opens the dialog. Leaving the dialog with the escape key leads to an Excel message that the script is being interrupted instead of just closing the dialog. I could suppress the interruption message with:
Application.EnableCancelKey = xlDisabled
But that seems not to be the solution as the script can not be interrupted any more.
Here is an example how i use VBA to open the dialog:
Private Sub ShowAboutDialog_Click()
Dim oComAddIn As COMAddIn
Set oComAddIn = Application.COMAddIns.Item("MyComAddIn.Example")
oComAddIn.Connect = True
Call oComAddIn.Object.ShowAboutDlg
End Sub
My guess is that the problem is somewhere in the message handler of the dialog:
INT_PTR CALLBACK CAboutDialog::AboutDlg(
HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
...
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
// Here, the ESCAPE key should also be trapped?
EndDialog(hwndDlg, LOWORD(wParam));
return TRUE;
}
...
}
return FALSE;
}
The Dialog is created with:
DialogBox(g_hModule, MAKEINTRESOURCE(IDD_ABOUT), hWndParent, (DLGPROC)AboutDlg)
Thanks a lot!
Dialogs should return their exit status to the calling routine rather than allowing to terminate code execution. So I suggest you to
convert your ShowAboutDlg from a Sub() to a Function()
returning a constant out of the VbMsgBoxResult Enum set (avoid hardcoding!)
trap the ESC key in your dialog and return a VbCancel (or VbAbort)
Good luck
MikeD
Related
I'm trying to add a keybinding to my rc.lua for shutting down my computer, which will display a prompt like "Shutdown (y/n)? ", and will call a shutdown if "y" is pressed, but will close if anything else is pressed. Here's my attempt so far:
...
awful.key({ modkey, "Control", "Shift" }, "q",
function ()
awful.prompt.run {
prompt = "Shutdown? (y/n) ",
textbox = awful.screen.focused().mypromptbox.widget,
keypressed_callback = function (_, key, _)
if key == "y" then
naughty.notify {
text = "Shutting down!"
}
else
naughty.notify {
text = "Not shutting down!"
}
end
return
end,
}
end,
{description = "shutdown", group = "awesome"}),
...
However, the prompt remains active after keys are pressed - the keypressed_callback will continue to trigger every another key is pressed, until I press either Return or Escape.
This is sensible default behaviour, but in my case I want the prompt to close after the first keypressed_callback event. My first thought was to use a return within the keypressed_callback to try to escape/cancel/destroy the prompt, but that isn't doing anything.
Is there anyway to achieve this?
You can call awful.keygrabber.stop().
I must admit this isn't ideal and that function is actually deprecated. I think this is indeed a feature gap in the prompt module itself.
What I would propose for single character prompts is to use https://awesomewm.org/apidoc/core_components/awful.keygrabber.html directly and implement a non-interactive widget using the textbox.
(another alternative is to use root.fake_input or awful.keyboard to emulate Escape/Enter, but that's a very bad hack)
I'm writing a lua program that needs to call an external popup (let's call it Poppy) provided as an external library/API by another active programme (environment is Windows).
Poppy has a bad habit; when invoked, it pops up, does its work and then vanishes -- leaving the current Lua dialog hidden behind Poppy's parent program.
After calling Poppy, dlg.BRINGFRONT="YES" will bring the iup dialog dlg to the front.
Is there a technique to identify what dlg should be -- i.e. what was the topmost iup dialog before the external API was invoked?
I've read the iup documentation and can't see a way to do this. However, as I need to call Poppy in a large number of instances, I'd like to simplify the process of bringing the current dialog to the front.
I'm invoking Poppy thus:
local res = Poppy('INDI')
dlg.BRINGFRONT="YES"
return res
Inside the code that invokes Poppy in Windows, you can call:
HWND dlg_handle = GetActiveWindow();
Then when Poppy returns, call:
SetForegroundWindow(dlg_handle);
I've succeeded in doing this using the Winapi library and the following code:
winapi = require ("winapi")
currentwin = winapi.get_foreground_window()
--invoke Poppy here and wait for control to return
currentwin:set_foreground()
I've created a Dart console app and need to process keycodes like Arrow keys and function keys from stdin? The samples I've seen are typically String based :
Stream readLine() => stdin.transform(UTF8.decoder).transform(new LineSplitter());
readLine().listen(processLine);
I modified the above sample hoping to get the raw ints like this:
Stream readInts() => stdin;
readInts().listen(processInts);
void processInts(List<int> kbinput) {
for (int i=0;i<kbinput.length;i++){
print ("kbinput:${kbinput[i]}");
}
}
It seems stdin provides only printable characters and not all ascii keycodes. If it is not possible from stdin, can I create & load a stream within my native extension with the keycodes? How can my console app get to the ascii keycodes of any keypress? Thanks for your help!
One way would be
import 'dart:io' as io;
import 'dart:convert' show UTF8;
void main() {
io.stdin.echoMode = false;
var input;
while(input != 32) { // leave program with [Space][Enter]
input = io.stdin.readByteSync();
if(input != 10) print(input); // ignore [Enter]
}
io.stdin.echoMode = true;
}
but it only returns a value after Enter is pressed.
For one key press it returns from one up to three bytes.
It seems it's not easy to get a keystroke from console without pressing Enter
see Capture characters from standard input without waiting for enter to be pressed for more details.
You could create a native extension that implements the suggested solution in the linked question.
I'm debugging an application at a point where it uses a dialog box to get some information from the user, and then does some processing on that information. By setting a breakpoint on USER32!CreateDialogParamW I have found the address of its dialog procedure.
At first I just wanted to break when the procedure receives a WM_COMMAND message, so I used the following command: bp 00cfa1c0 "j (dwo(esp+8) == 0x111) ''; 'gc'"
This is unfortunately not enough as the dialog procedure for some reason receives WM_COMMAND messages even when ALT-TABbing between WinDbg and the application. So, now I want it to break when it receives WM_COMMAND with a notification code of BN_CLICKED from the OK button on the dialog. The control ID of the button in the dialog template is 1, and BN_CLICKED is defined as 0 in winuser.h. This means the WPARAM argument of the dialog procedure should be 1 when clicking the OK button.
I tried the following command: bp 00cfa1c0 "j (dwo(esp+8) == 0x111 && dwo(esp+12) == 0x1) ''; 'gc'". This is initially accepted, but when the breakpoint is evaluated it complains: Numeric expression missing from '& dwo(esp+12) == 0x1) ''; 'gc''
Surrounding the 2 expressions with ()'s did not help. I had a look at the help file, but to be honest that confuses me even more. I'm pretty new to WinDbg and English is not my native language. Can someone point me in the right direction?
Thanks in advance.
PS: This is a 32-bit application for which I do not have the source code.
Use a single & - the default syntax for expressions is MASM. && is part of C++ syntax.
The following expressions would work for you:
(dwo(#esp+8) == 0x111 & dwo(#esp+12) == 0x1)
or
##c++(*(int*)(#esp+8) == 0x111 && *(int*)(#esp+12) == 0x1)
I'm working on Word automation and to get rid of "Call was rejected by callee" / "the message filter indicated that the application is busy" errors I implemented an IMessageFilter. The messagefilter works like a charm when I automate Word directly like:
Word.Documents.Open(...)
Document.SaveAs(...)
But when I call TOleContainer.DoVerb(ovPrimary), I still get errors when Word is displaying a modal dialog. Why does the MessageFilter not work with TOleContainers DoVerb methode?
"Call was rejected by callee" is what you always get when Word is in interactive state, ie displaying a dialog. This is not restricted to Word. It also happens with Excel, for example when the user was editing a cell. And it does not have to be obvious in the user interface either. When you start editing a cell, move focus to another application and come back to Excel, the UI doesn't give you a clue but it is still in "interactive" mode and will reject automation calls with the "Call was rejected by callee" error.
So basically when you automate Word in conjunction with user interaction (and not just with Word in a background process), you should be prepared to get and handle these errors.
Edit
If you want to know whether Excel or Word is in interactive mode before calling any other COM method: just ask the COM-server whether it is "Ready":
Result := _GetActiveOleObject('Excel.Application');
try
aSharedInstance := not VarIsClear(Result);
if aSharedInstance then
Version := Result.Version; // If this produces an exception, then use a dedicated instance.
// In case checking the version does not produce an exception, but Excel still isn't
// ready, we'll check that as well.
// By the way, for some unclear reason, partial evaluation does not work on .Ready,
// so we'll do it like this:
if aSharedInstance and (StrToIntDef(StringBefore('.', Version), 0) >= EXCEL_VERSION_2002) then
aSharedInstance := Result.Ready;
except
aSharedInstance := False;
end;
if not aSharedInstance then
Result := CreateOleObject('Excel.Application');
Update
Apparently Word doesn't have a "Ready" property (whoever said Microsoft was consistent?). In that case you need to determine its readiness yourself by calling a simple (and fast) property before the actual call, and assuming that when that throws an exception, Word isn't ready. In the above example the Version is retrieved before the Ready property. If that throws an exception, we just assume that the application (Excel in this case) isn't ready and proceed accordingly.
Something along the lines of:
while Tries <= MaxTries do
try
Version := Word.Version;
Tries := MaxTries + 1; // Indicate success
Word.TheCallYouReallyWantToDo;
except
Inc(Tries);
sleep(0);
end;
Note Word.Version does not throw an exception when a dialog is open, so that is no use for figuring out whether Word is ready. :( You will have to experiment to find one that does.
IMessageFilter doesn't handle all exceptions, for example, at some points, office applications 'suspend' their object model, at which point it cannot be invoked and throws: 0x800AC472 (VBA_E_IGNORE)
In order to get around this, you have to put your call in a loop and wait for it to succeed:
while(true)
{
try
{
office_app.DoSomething();
break;
}
catch(COMException ce)
{
LOG(ce.Message);
}
}
// continue after successful call
See here for more details.