【发布时间】:2016-03-27 16:09:28
【问题描述】:
[ - 简介 - ]
我有与一些测量设备接口的软件 (Altair)。该软件的有限功能集作为 API 公开,由制造商以其 MATLAB 实现的形式提供给我(没有额外的文档)。根据提供的来源,我知道与此应用程序的所有通信都使用Kernel32.dll 或user32.dll(Windows API 库),更具体地说,使用以下方法:
-
RegisterWindowMessage (
user32) -
PostMessage (
user32) -
PeekMessage (
user32) -
GlobalGetAtomName (
Kernel32)
我在 API 中缺少的功能之一是能够检索隐藏在该软件某处的特定文本设置。幸运的是,该设置出现在其 UI 中的 TextBox(带有不可选择的文本)中。
我的目标是在 MATLAB 中获得一个出现在这个单独的非 MATLAB 窗口中的字符串。
[ - 我的尝试 - ]
快速的互联网搜索显示1,2这实际上是可能的,通过 Windows API,如果HWND(窗口句柄) 可以为持有所需String 的特定控件(或“窗口”)获取。然后向控件发送WM_GETTEXT,理论上返回字符串。
我采取的第一步是检查HWND 是否可用。这是使用Microsoft Spy++ utility(VS2015 可选)完成的。结果如下:
上述层次结构意味着第 1st的第 3rdchild 类 Static 的第 4thchild >child .....窗口“Altair”是我要找的。p>
就 Windows API 而言,这些方法似乎对遍历窗口层次结构和获取字符串很有用:
-
通过将每个子窗口的句柄依次传递给应用程序定义的回调函数来枚举属于指定父窗口的子窗口。 EnumChildWindows 一直持续到枚举最后一个子窗口或回调函数返回 FALSE。
不幸的是,这是不可用的,因为“MATLAB 共享库接口不支持带有函数指针输入的库函数。”(from the docs of
loadlibrary),这恰好是一个强制性输入EnumChildWindows. -
检索其类名和窗口名与指定字符串匹配的顶级窗口的句柄。此函数不搜索子窗口。此函数不执行区分大小写的搜索。
要从指定的子窗口开始搜索子窗口,请使用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