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

标签 matlab user-interface pointers winapi ipc

[ - 介绍 - ]

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

  • RegisterWindowMessage ( user32 )
  • PostMessage ( user32 )
  • PeekMessage ( user32 )
  • GlobalGetAtomName ( Kernel32 )

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

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

    [ - 我的尝试 - ]

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

    我采取的第一步是检查 HWND甚至可以获得。这是使用 Microsoft Spy++ utility 完成的(可选地与 VS2015 一起使用)。结果如下:

    Finding the text field in Spy++

    上面的层次结构意味着第 4 个 child类(class)Static第三期 child第一期 child ..... 窗口“Altair”就是我要找的。

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

    Enumerates the child windows that belong to the specified parent window by passing the handle to each child window, in turn, to an application-defined callback function. EnumChildWindows continues until the last child window is enumerated or the callback function returns FALSE.



    然而不幸的是,这是无法使用的,因为“MATLAB 共享库接口(interface)不支持具有函数指针输入的库函数”。 ( from the docs of loadlibrary ),恰好是 EnumChildWindows 的强制性输入.
  • FindWindow FindWindowEx :

    Retrieves a handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. This function does not perform a case-sensitive search.

    To search child windows, beginning with a specified child window, use the FindWindowEx function.

  • GetWindowText :

    Copies the text of the specified window's title bar (if it has one) into a buffer. If the specified window is a control, the text of the control is copied. However, GetWindowText cannot retrieve the text of a control in another application.

  • SendMessage :

    Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.


  • 所以我开始创建一个 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 方法生成 header :

    [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 [in]

    Type: WPARAM

    Additional message-specific information.

    lParam [in]

    Type: LPARAM

    Additional message-specific information.



    WM_GETTEXT 的情况下是:

    wParam

    The maximum number of characters to be copied, including the terminating null character.

    ANSI applications may have the string in the buffer reduced in size (to a minimum of half that of the wParam value) due to conversion from ANSI to Unicode.

    lParam

    A pointer to the buffer that is to receive the text.



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

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

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

    ...为了创建正确的 LPARAMS来自另一种类型的输入。不幸的是,我发现这无法帮助我。

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

    最佳答案

    MATLAB 的 External Functions interface允许调用各种语言的函数,其中包括Java。如 this answer 中所述,与 Windows API 接口(interface)的流行 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然后将包含所需的 String . Q.E.F.

    关于matlab - 使用 MATLAB 从另一个应用程序中的控件获取文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34398149/

    相关文章:

    c++ - 将 void 函数转换为 char* C++

    objective-c - Objective-C header 中的 C 指针损坏

    matlab - LibSVM一类分类nu参数是不是离群值的一小部分?

    arrays - MATLAB 中整个多维数组(不仅仅是一维)的中位数

    matlab - 查找 3D 矩阵中第三维的最小绝对值并确保符号保持不变

    python - 为什么我天真的执行阿特金斯筛法排除了 5?

    java - 多项式 GUI 不起作用

    shell - 如何在 AppleScript 中抑制/自动关闭错误对话框

    c - 无法在 C 中初始化指针数组

    .net - 如何获取ToolStripButton的坐标?