使用MATLAB从另一个应用程序中的控件获取文本

Dev*_*-iL 11 matlab user-interface winapi pointers ipc

[ - 介绍 - ]

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

我在API中缺少的一个功能是能够检索隐藏在该软件某处的某个文本设置.幸运的是,该设置显示TextBox在其UI中的(具有不可选文本)内.

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


[ - 我的尝试 - ]

一个快速的互联网搜索显示1,2,这实际上是可能的,通过Windows API,如果HWND(句柄窗口)可以为特定的控制(或"窗口")获得有所需String.WM_GETTEXT然后将A 发送到控件,理论上返回该字符串.

我采取的第一步是检查a HWND是否可以获得.这是使用Microsoft Spy ++实用程序(可选配VS2015)完成的.结果如下:

在Spy ++中查找文本字段

上述层次结构意味着,4 childStatic的3 child的1 child .....窗口"牵牛星"的就是我要找的.

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

  • EnumChildWindows:

    枚举由句柄传递到每个子窗口,反过来,一个应用程序定义的回调函数属于指定的父窗口的子窗口.EnumChildWindows继续,直到枚举最后一个子窗口或回调函数返回FALSE.

    然而不幸的是,这是不可用的,因为" MATLAB共享库接口不支持带有函数指针输入的库函数. "(来自文档loadlibrary),恰好是强制性的输入EnumChildWindows.

  • FindWindow并且FindWindowEx:

    检索顶级窗口的句柄,该窗口的类名称和窗口名称与指定的字符串匹配.此功能不搜索子窗口.此功能不执行区分大小写的搜索.

    要搜索子窗口,从指定的子窗口开始,请使用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);   
Run Code Online (Sandbox Code Playgroud)

以上是此处建议的值得注意的方法的手动替代方法,它使用以下方法为所有可用的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');
Run Code Online (Sandbox Code Playgroud)

让我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
Run Code Online (Sandbox Code Playgroud)

[ - 实际问题 - ]

最后一步,我正在努力,它派遣WM_GETTEXT那将得到我的字符串.具体来说,问题,我理解它的方式,与SendMessage的输入参数的定义有关:

LRESULT WINAPI SendMessage(
  _In_ HWND   hWnd,
  _In_ UINT   Msg,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
);
Run Code Online (Sandbox Code Playgroud)

wParam [in]

类型:WPARAM

其他特定于消息的信息.

lParam [in]

类型:LPARAM

其他特定于消息的信息.

在以下情况下WM_GETTEXT:

wParam中

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

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

lParam的

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

这给我带来了一个问题:一方面我应该传递一个LPARAMS,根据typedef头文件中的一个long(这意味着MATLAB需要一个数字输入),但它需要是一个指向"文本缓冲区"的指针",这意味着我应该传递类似的东西libpointer?('String').

碰巧,其他人在过去遇到了相关的问题,并建议使用所谓的MAKELPARAM定义为:

#define MAKELPARAM(l, h) ((LPARAM) MAKELONG(l, h))
Run Code Online (Sandbox Code Playgroud)

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

这可能是对我的如何在MATLAB正确使用指针部分误解3,4,或者我遇到了MATLAB的限制.无论哪种方式,我都在问如何SendMessage从MATLAB 进行调用

Dev*_*-iL 7

MATLAB的External Functions接口允许以各种语言调用函数,其中包括Java.如本回答所述,与Windows API交互的流行Java库是Java Native Access(JNA).

这个答案中演示了如何利用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码导入了在JNA的旧分支(特别是/src/com/sun/jna/文件夹)中找到的类.打包.jar后,可以使用以下方法从MATLAB调用:

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

str然后将包含所需的String.优质教育基金

  • @DavidHeffernan - "_所有事情应尽可能简单,但不能简单".如果我这样问,那就是["XY问题"](http://meta.stackexchange.com/q/66377).相反,我的问题是从另一个应用程序获取一个字符串 - 这是我试图传达的.其他一切都回答了经典的"你尝试了什么?".我认为这个Q&A对未来读者有多大帮助,在展示我的思想轨迹方面看到了价值.特别是如果/当其他答案出现时会显示更好的方式. (2认同)