Use SendMesage in VBA

Ken Krugh 116 Reputation points
2021-03-24T14:55:38.837+00:00

We have a VBA macro in Outlook for printing emails to a file using a printer that displays a very simple dialog with only a caption, a text box into which to type the filename, an OK and Cancel button.

Using the .Printout method brings up this dialog and we want to type a filename and hit the OK button with the macro. For some reason the SendKeys function makes the Outlook/this printer lock up on certain emails and we've not figured out why. It seems to have something to do with the email size as emails with many pictures in them are problematic.

Instead of SendKeys we've worked out using the FindWindow API to find the text box on the dialog, and the SendMessage API which is filling in the filename just fine. But we've been unsuccessful to using SendMessage to send an Enter key to "click" the OK button.

We were able to get the PostMessage API working to send an Enter key but it has the same problem on certain emails as SendKeys.

Is there a way to use SendMessage to "click" the OK button or can someone suggest an alternative?

Thanks a bunch,
Ken

0 comments No comments
{count} votes

Accepted answer
  1. Sam of Simple Samples 5,546 Reputation points
    2021-03-24T20:06:28.147+00:00

    If you can do what I describe in Clicking a Button in Another Application then that would be more reliable. If possible, you should get the control id of the text box and send a WM_SETTEXT message to fill in the filename then get the control id of the button to send a BN_CLICKED notification message. My sample code is for a C# Windows Forms application but I hope that is enough to get it working in VBA.

    Here is a bit about BN_CLICKED. The documentation says:

    The parent window of the button receives this notification code through the WM_COMMAND message.

    So the message id in SendMesage is WM_COMMAND. The wParam is a combination of the button's control id and the notification code. Since the notification code is zero, for a BN_CLICKED you can omit it but you should make a comment in your code indicating something appropriate. The lParam is the window handle for the button. So in the following:

    int wParam = (BN_CLICKED << 16) | (ButtonId & 0xffff);  
    

    I am shifting BN_CLICKED (which is actually zero) and oring that with the button id; The & 0xffff just ensures that we use just 16 bits but that is likely not important. So it will likely work to just set wParam to the button's control id.

    The advantage of BN_CLICKED is that it is just one message sent to the parent. It seems cleaner to me, at least.

    1 person found this answer helpful.
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Ken Krugh 116 Reputation points
    2021-03-25T02:29:46.757+00:00

    Thanks very much for answering, Simple. We managed to find another example that got it working, doing something very similar.

    We are doing exactly as you suggest, using the WM_SETTEXT with the handle of the text box, that part was working.

    We found another code example here that uses WM_LBUTTONDOWN and WM_LBUTTONUP. We used these three lines to perform the click on the OK.

        OKHWnd& = FindWindowEx(WinHWnd&, 0, vbNullString, "OK")
        Call SendMessage(OKHWnd&, WM_LBUTTONDOWN, ByVal 1&, ByVal 0&)
        Call SendMessage(OKHWnd&, WM_LBUTTONUP, ByVal 0&, ByVal 0&)
    

    Unfortunately I'm not familiar enough with C# to know what you're doing to set up the wParam in your example, or how to replicate it in VBA. Is there an advantage to using the BN_CLICKED that makes it worth trying to decipher?

    Thanks again for answering.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.