2

I am trying to retrieve the text of a button on Calculator (calc.exe) using Winapi. I have hooked WH_CALLWNDPROC and the hook works fine. But I am unable to retrieve the text of any button (say numeric 7). I am using GetDlgItemText:

TCHAR text[256];
GetDlgItemText((HWND)0x7068c, 0x89, text, strlen(text));

Here 0x7068c is the parent window handle of the numeric 7 button, while 0x89 is its control id. No text is returned, though I am able to click it programmatically.

What am I doing wrong? I tried to use SendMessage with WM_GETTEXT, but it still doesnt work. I want to take this approach and retrieve the text from buttons, textboxes in other applications.

stamhaney
  • 1,246
  • 9
  • 18
  • Please give a reason for the downvote – stamhaney Sep 30 '15 at 13:16
  • Why would you expect to be able to do this? – David Heffernan Sep 30 '15 at 13:21
  • @DavidHeffernan using hooks, it should be possible to retrieve text from another application – stamhaney Sep 30 '15 at 13:27
  • 1
    Hooks are needless. But that's not the point. Why do you believe that these buttons have window text that is the number of the button? And what's the real problem? You can't really be interested in calc.exe. Also, every version of Windows has a different calc.exe. Why are you not using UI Automation? – David Heffernan Sep 30 '15 at 13:31
  • I would check the return value of the function, GetLastError and so on. That should provide you and us with more information. – Jonathan Sep 30 '15 at 13:34
  • @DavidHeffernan I want to retrieve the text off some buttons in a window in a different legacy application. I tried UI Automation, the autmation id changes every time on each run also there is no Automation name for that button, So it is not possible to do this using UI Automation – stamhaney Sep 30 '15 at 13:36
  • That doesn't sound quite right. All the same it's pointless doing this on calc since it isn't the target app. There a gazillion different button implementations out there. Why would you be so sure calc use the same as the target app? – David Heffernan Sep 30 '15 at 13:38
  • 1
    @DavidHeffernan the button is just a normal window. Shouldnt it be possible ?I have checked its properties in Spy++ – stamhaney Sep 30 '15 at 13:45
  • Just a normal window? What class is it? How familiar are you with Win32? Did you read the part of my comment where I said that every Windows version has a different calc.exe? Why did you neglect error checking? Why did you feel the need to add a `WH_CALLWNDPROC` hook? Why can we not see a [mcve]? – David Heffernan Sep 30 '15 at 13:47
  • It is a custom class. There is no error. As I cannot use UI auotmation ,and I also tried using Inspect to retrieve MSAA properties, but cannot get suitable properties for locating the button, so used hooks. The hook itself is working fine. And the code is big, so wanted to just show the part which is a problem – stamhaney Sep 30 '15 at 13:55
  • 1
    The hook won't help you. It's a custom class is it? So, I ask yet again, what makes you believe that this custom class keeps the button's display title in its window text? And yet again I point out that there are many different versions of calc.exe. And again that your target app is almost certainly implemented differently. It's not going to have calc's custom button class now is it? Am I wasting my time. – David Heffernan Sep 30 '15 at 14:08

2 Answers2

6

You could simply use Accessibility:

        #include "windows.h"
        #include "Oleacc.h"
        #include "atlbase.h"
        #pragma comment(lib,"Oleacc.lib")

        int main()
        {
            CComPtr<IAccessible> pAcc;
            VARIANT varChild;
            if (SUCCEEDED(AccessibleObjectFromWindow((HWND)0x000d18e0, 
                OBJID_WINDOW,IID_IAccessible, reinterpret_cast<void**>(&pAcc))))
            {
                CComBSTR bstrName;
                varChild.vt = VT_I4;
                varChild.lVal = CHILDID_SELF;
                if (SUCCEEDED(pAcc->get_accName(varChild, &bstrName)))
                {
                    wprintf(L"%s", bstrName.m_str);
                }

            }
            return 0;
        }
user1730969
  • 462
  • 3
  • 10
  • You just leaked a `BSTR` – David Heffernan Sep 30 '15 at 13:45
  • 1
    @Ben Sure why not, also added entire code for easy testing – user1730969 Sep 30 '15 at 14:00
  • ....aaaaand `CComVARIANT`... :-) These are suggestions only... my favourite is actually `_com_ptr_t` and `_bstr_t` and `_variant_t`. `#import` **FTW** – Ben Sep 30 '15 at 14:26
  • @Ben: In terms of providing insight, nothing beats C and manual resource management. No invisible destructor code, or exception paths. It's truly WYSIWYG. It also easily translates to any smart pointer library, if you wish to use one. And if you are using a smart pointer library in an answer, someone **will** come along, to tell you, that you're using the wrong one. – IInspectable Sep 30 '15 at 18:08
  • @IInspectable This is not a question about gaining insight into resource management, nor C. Nobody should be using manual resource management for this. – Ben Oct 01 '15 at 08:58
  • @Ben: Like with any other question, an answer **is** about gaining insight. Using a random smart pointer library hides unknown code. If there was a single C++ smart pointer type for COM interfaces, and a single canonical way of using it, you might even have a point. This is not the case, and randomly picking one smart pointer type needlessly limits the audience, that will find this answer helpful. Omitting smart pointers and using explicit code instead does not impose that restriction. – IInspectable Oct 02 '15 at 11:51
  • @IInspectable #makesnosense Most important insight is "use smart wrappers for COM types and don't do manual memory management" – Ben Oct 02 '15 at 12:13
  • Thanks @user1730969, accessibility allows me to retrieve the name, automation id. etc of the control. However it does not help me to retrieve the actual text. I want to take this approach and use it for a legacy application. But in the legacy application, I get Name blank and automation id changes on each run. – stamhaney Oct 03 '15 at 07:04
  • @stamhaney, you should be aware that there is no generic solution for getting text from application. My approach does work in calculator (which was the question scope) because it is a native win32 application using native controls. A QT/Swing/Wpf/embedded web/etc... Will need a case by case solution. Generally speaking though, most support accessibility to some extent. Just look at a tool like TestComplete, it had multiple solutions for multiple frameworks. – user1730969 Oct 03 '15 at 11:00
1

I finally succeeded in doing this by using EasyHook for hooking DrawTextW, DrawTextExW, ExtTextOutW API functions for retrieving the text off buttons, labels, etc. In the hooked functions I was able to retrieve the text. This SO thread was a great help to me.

Community
  • 1
  • 1
stamhaney
  • 1,246
  • 9
  • 18
  • 1
    You wouldn't happen to have your source on Git or someplace would you? I'm going through a similar exercise... – Jeff Oct 25 '16 at 19:59