【问题标题】:Getting text from a control in another application, using MATLAB使用 MATLAB 从另一个应用程序中的控件获取文本
【发布时间】:2016-03-27 16:09:28
【问题描述】:

[ - 简介 - ]

我有与一些测量设备接口的软件 (Altair)。该软件的有限功能集作为 API 公开,由制造商以其 MATLAB 实现的形式提供给我(没有额外的文档)。根据提供的来源,我知道与此应用程序的所有通信都使用Kernel32.dlluser32.dllWindows API 库),更具体地说,使用以下方法:

我在 API 中缺少的功能之一是能够检索隐藏在该软件某处的特定文本设置。幸运的是,该设置出现在其 UI 中的 TextBox(带有不可选择的文本)中。

我的目标是在 MATLAB 中获得一个出现在这个单独的非 MATLAB 窗口中的字符串


[ - 我的尝试 - ]

快速的互联网搜索显示1,2实际上是可能的,通过 Windows API,如果HWND(窗口句柄) 可以为持有所需String 的特定控件(或“窗口”)获取。然后向控件发送WM_GETTEXT,理论上返回字符串。

我采取的第一步是检查HWND 是否可用。这是使用Microsoft Spy++ utilityVS2015 可选)完成的。结果如下:

上述层次结构意味着第 1st的第 3rdchildStatic 的第 4thchild >child .....窗口“Altair”是我要找的。​​p>

就 Windows API 而言,这些方法似乎对遍历窗口层次结构和获取字符串很有用:

  • EnumChildWindows:

    通过将每个子窗口的句柄依次传递给应用程序定义的回调函数来枚举属于指定父窗口的子窗口。 EnumChildWindows 一直持续到枚举最后一个子窗口或回调函数返回 FALSE

    不幸的是,这是不可用的,因为“MATLAB 共享库接口不支持带有函数指针输入的库函数。”(from the docs of loadlibrary),这恰好是一个强制性输入EnumChildWindows.

  • FindWindowFindWindowEx

    检索其类名和窗口名与指定字符串匹配的顶级窗口的句柄。此函数不搜索子窗口。此函数不执行区分大小写的搜索。

    要从指定的子窗口开始搜索子窗口,请使用FindWindowEx 函数。

  • GetWindowText:

    将指定窗口标题栏(如果有的话)的文本复制到缓冲区中。如果指定的窗口是控件,则复制控件的文本。但是,GetWindowText 无法在另一个应用程序中检索控件的文本。

  • SendMessage:

    将指定的消息发送到一个或多个窗口。 SendMessage 函数调用指定窗口的窗口过程,直到窗口过程处理完消息才返回。

所以我开始创建一个 c 头文件,该文件将与 MATLAB 的 loadlibrary 一起使用,我最终得到以下 (graphic_hack.h ????):

// Windows Data Types: 
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
typedef unsigned int UINT;
typedef UINT WPARAM;
typedef long LPARAM;
typedef long LRESULT;
typedef unsigned long HANDLE;
typedef unsigned long HWND;
typedef unsigned long HICON;
typedef unsigned long HINSTANCE;
typedef int BOOL;
typedef const char *LPCSTR;
typedef char *LPSTR;
typedef char TCHAR;
typedef LPCSTR LPCTSTR;
typedef LPSTR LPTSTR;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef long LONG;
typedef unsigned long ULONG;

#define STDCALL  __stdcall
#define CALLBACK __stdcall

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633499%28v=vs.85%29.aspx
HWND STDCALL FindWindowA(LPCTSTR,LPCTSTR);               
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633500%28v=vs.85%29.aspx
HWND STDCALL FindWindowExA(HWND,HWND,LPCTSTR,LPCTSTR);
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633520%28v=vs.85%29.aspx
int STDCALL GetWindowTextA(HWND,LPTSTR,int);             
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms644950%28v=vs.85%29.aspx     
LRESULT STDCALL SendMessageA(HWND,UINT,WPARAM,LPARAM);   

以上是建议的值得注意的方法here手动替代方法,它使用以下方法为所有可用的 API 方法生成标头:

[nf,warn] = loadlibrary('user32.dll',...
 'C:\Program Files (x86)\Windows Kits\8.1\Include\um\windows.h',...
'alias','user','includepath','C:\Program Files (x86)\Windows Kits\8.1\Include\um\',...
'includepath','C:\Program Files (x86)\Windows Kits\8.1\Include\shared\',...
'addheader','WinUser','mfilename','user_header');

获取控件的HWND 的 MATLAB 代码如下:

if (libisloaded('gh'))
    unloadlibrary('gh')
end
[~,~]=loadlibrary('user32.dll', 'graphic_hack.h','alias','gh');
javaaddpath(fullfile(pwd,'User32Util.jar'));
%Debug: libfunctionsview gh;
%       libfunctions('gh','-full');
%% Obtain the Altair field handle using various trickery:
hwndAltair = calllib('gh','FindWindowA','Altair',[]); %Find the Altair Window
hTmp(1) = calllib('gh','FindWindowExA',hwndAltair,0,'AfxControlBar70','Capture Manager');
hTmp(2) = calllib('gh','FindWindowExA',hTmp(1),0,'Afx:00400000:48:00000000:01100078:00000000',[]);
hTmp(3) = calllib('gh','FindWindowExA',hTmp(2),0,'SysTabControl32',[]);
hTmp(4) = calllib('gh','FindWindowExA',hTmp(3),0,[],'');
hTmp(5) = calllib('gh','FindWindowExA',hTmp(4),0,'Static',[]);
for k = 1:4
  hTmp(5) = calllib('gh','FindWindowExA',hTmp(4),hTmp(5),'Static',[]);
end

[ - 实际问题- ]

我正在努力解决的最后一步是发送WM_GETTEXT,这将使我得到字符串。具体来说,我理解的问题与SendMessage的输入参数的定义有关:

LRESULT WINAPI SendMessage(
  _In_ HWND   hWnd,
  _In_ UINT   Msg,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
);

wParam [输入]

类型:WPARAM

其他特定于消息的信息。

lParam [输入]

类型:LPARAM

其他特定于消息的信息。

WM_GETTEXT的情况下是:

wParam

要复制的最大字符数,包括终止空字符。

由于从 ANSI 到 Unicode 的转换,ANSI 应用程序可能会减小缓冲区中的字符串大小(最小为 wParam 值的一半)。

lParam

指向要接收文本的缓冲区的指针。

这给我带来了一个问题:一方面我应该传递一个LPARAMS,根据头文件中的typedef,它是一个long(这意味着MATLAB需要一个数字输入),但它必须是指向“文本缓冲区”的指针,这意味着我可能应该传入类似libpointer​('String') 的内容。

碰巧,其他人过去也遇到过相关问题,it was suggested 使用所谓的MAKELPARAM macro 定义为:

#define MAKELPARAM(l, h) ((LPARAM) MAKELONG(l, h))

...为了从另一种类型的输入创建正确的LPARAMS。不幸的是,我发现这对我没有任何帮助。

这可能是我对如何在 MATLAB 中正确使用指针的误解3,4,或者是我遇到的 MATLAB 的限制。无论哪种方式,我在问如何继续从 MATLAB 调用 SendMessage

【问题讨论】:

  • 太棒了...没有 TL;博士?;)
  • @AndrasDeak 这就是介绍:)
  • 你确实让生活变得比需要的更艰难。当为此使用适当的系统提供的接口时,您的大多数问题都得到了解决:UI Automation。如果您绝对需要遵循您提出的策略,至少请致电SendMessageTimeout 以防止死锁。但实际上,调用 ANSI API 是错误的。
  • @IInspectable ----需要详细说明吗? ...也许作为答案?--- 看到你的编辑。
  • 解释太多了。该链接提供了比我想出的更好的解释。如果您想要现场演示,请查看Inspect,它具有与 Spy++ 相似的功能,只是它在可访问的项目树(不是本机窗口)之外运行。

标签: matlab user-interface pointers winapi ipc


【解决方案1】:

MATLAB 的External Functions interface 允许调用各种语言的函数,其中包括Java。如this answer 中所述,与Windows API 交互的流行Java 库是Java Native Access (JNA)

this answer 中演示了如何利用 JNA 发送WM_GETTEXT 消息。针对本题的具体需求进行了改编,转化为static方法,所需的Java-JNA代码如下所示:

package hack.graphic.devil

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.win32.StdCallLibrary;

public class User32Util {
    interface User32 extends StdCallLibrary {
        User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class);
        int WM_GETTEXT = 0x000D;

        LRESULT SendMessageA(HWND editHwnd, int wmGettext, long l, byte[] lParamStr);
    }

    public static String getStringFromHexHWND(String args0) {
        User32 user32 = User32.INSTANCE;
        HWND target = new HWND(new Pointer(Long.decode(args0)));
        byte[] lParamStr = new byte[512];
        user32.SendMessageA(target, User32.WM_GETTEXT, 512, lParamStr);
        return Native.toString(lParamStr);
    }
}

上面的代码导入了an older branch of JNA(特别是its /src/com/sun/jna/ folder)中的类。在packaging as a .jar 之后,可以使用以下命令从 MATLAB 调用它:

javaaddpath(fullfile(pwd,'User32Util.jar'));
...
str = char(hack.graphic.devil.User32Util.getStringFromHexHWND(['0x' dec2hex(hTmp(5))]));

str 然后将包含所需的StringQ.E.F.

【讨论】:

  • 对于“如何从 MATLAB 调用 Win32 API 函数”来说,这似乎是一段史诗般的文本?
  • @DavidHeffernan - “一切都应该尽可能简单,但不要简单”。如果我这样问,那将是"XY problem"。相反,我的问题是从另一个应用程序获取一个字符串——这就是我试图传达的。其他一切都回答了经典的“你试过什么?”。我看到了价值,就本问答对未来读者的帮助而言,展示了我的思想轨迹。特别是如果/当出现其他答案时,会显示出更好的方法。
  • 不,这不是一个 XY 问题。更不用说这是从另一个进程的窗口中获取文本的错误方法。
猜你喜欢
  • 1970-01-01
  • 2013-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-17
  • 2015-07-11
  • 1970-01-01
  • 2017-06-27
相关资源
最近更新 更多