Detailed explanation of Windows Forms data capture

Recently, I just encountered a problem in a customer project. The project requirement is to obtain the real-time status of a certain machine tool. The problem is that the machine tool is not a CNC machine tool in the traditional sense, nor is it a PLC controller. There is only one upload and download program file. , there are just a few buttons on it that can roughly judge the current working status. In a blink of an eye, it is possible to obtain the status of several buttons in real time, so as to determine the machining status of the machine tool through a simple analysis.

Go ahead and start picking up the long-lost Win32API to try. The idea is roughly as follows:

  • First of all, what we know is the process name of the application such as: notepad.exe
  • Then, it is necessary to obtain the window handle (HWND) by the process name
  • Secondly, traverse the child window handle through the window handle, and obtain relevant data through it, such as: whether the Button is available, the Text of the Button, whether the CheckButton is selected, etc. some desired operations. Here we grab the Notepad content (the content is in the Edit control)
  • Finally, it is only necessary to update and store data in real time, and perform post-processing logic.

get process id

First, when we know the process name and obtain the process ID through the process name, we need to use a Win32 process snapshot module: CreateToolhelp32Snapshot can obtain the process information for the specified process, the heap [HEAP], module [MODULE], thread used by the process Create a snapshot.

HANDLE WINAPI CreateToolhelp32Snapshot(
  __in          DWORD dwFlags,
  __in          DWORD th32ProcessID
);

parameter:

  • dwFlags is used to specify the object to be returned in the "snapshot", and the system content contained in the snapshot. This parameter can use one or more of the following values ​​(constants).
    1. TH32CS_INHERIT - declares that the snapshot handle is inheritable.
    2. TH32CS_SNAPALL - Include all processes and threads in the system in the snapshot.
    3. TH32CS_SNAPHEAPLIST - Include all heaps of the process specified in th32ProcessID in the snapshot.
    4. TH32CS_SNAPMODULE - Include all modules for the process specified in th32ProcessID in the snapshot.
    5. TH32CS_SNAPPROCESS - Include all processes in the system in the snapshot.
    6. TH32CS_SNAPTHREAD - Include all threads in the system in the snapshot.
  • th32ProcessID Specifies the process ID that will be snapshotted. If this parameter is 0, it means to snapshot the current process. This parameter is only valid when TH32CS_SNAPHEAPLIST or TH32CS_SNAPMODULE is set. In other cases, this parameter is ignored and all processes will be snapshotted.

Return value: If the call succeeds, it returns the handle of the snapshot. If the call fails, it returns INVALID_HANDLE_VALUE.

Without further ado, let’s go directly to the code:

DWORD   GetProcessIdFromProcessName(std::string processname)
{
    DWORD dwRet = 0;
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);
    HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap != INVALID_HANDLE_VALUE)
    {
        BOOL bMore = ::Process32First(hProcessSnap, &pe32);
        while (bMore)
        {
            if (boost::iequals(pe32.szExeFile, processname))
            {
                dwRet = pe32.th32ProcessID;
            }
            bMore = ::Process32Next(hProcessSnap, &pe32);
        }
        ::CloseHandle(hProcessSnap);
    }
    return dwRet;
}

Call the test:

std::string str1 = "notepad.exe";
DWORD dwProcessId = GetProcessIdFromProcessName(str1);
std::cout << dwProcessId << std::endl; //

Process ID

Once the process is obtained, go to the next section to get the window handle.

Get the window handle HWND

Get the window handle through the process ID, then we need to traverse the window and find the window handle corresponding to the process that meets our needs. A function will be used in this place: EnumWindows enumerates all the top-level windows on the screen, and puts the window handle Passed to an application-defined callback function.

BOOL EnumWindows(   
    WNDENUMPROC lpEnumFunc,   
    LPARAM lParam 
); 

parameter:

  • lpEnumFunc: Pointer to an application-defined callback function, see EnumWindowsProc.
  • lPararm: Specifies an application-defined value passed to the callback function.

Return value: If the function succeeds, the return value is non-zero; if the function fails, the return value is zero. To get more error information, call the GetLastError function.

回调函数:
BOOL CALLBACK EnumWindowsProc(  
    HWND hwnd,   
    LPARAM lParam 
);

parameter:

  • hwnd: the handle of the top-level window
  • lparam: a value defined by the application (ie lParam in EnumWindows)
    Return value: TRUE to continue traversal, FALSE to stop traversal

Note: The EnumWindows function does not enumerate child windows.

Then the next step is to start getting the window handle.

First define a structure

The body of the body is marked with process and window handles:

typedef struct tagHWNDINFO
{
    DWORD   dwProcessId;
    HWND    hWnd;
} HWNDINFO, *LPHWNDINFO;

enumerate all windows

BOOL CALLBACK EnumWindowProc(HWND hWnd, LPARAM lParam)
{
    DWORD dwProcId;
    GetWindowThreadProcessId(hWnd, &dwProcId);
    LPHWNDINFO pInfo = (LPHWNDINFO)lParam;
    if (dwProcId == pInfo->dwProcessId)
    {
        if (GetParent(hWnd) == NULL && IsWindowVisible(hWnd))  //判断是否顶层窗口并且可见  
        {
            pInfo->hWnd = hWnd; 
            return FALSE;
        }
        else
            return TRUE;
    }

    return TRUE;
}

Get the window handle of the specified process ID

HWND GetProcessMainWnd(DWORD dwProcessId)//获取给定进程ID的窗口HWND
{
    HWNDINFO wi;
    wi.dwProcessId = dwProcessId;
    wi.hWnd = NULL;
    EnumWindows(EnumWindowProc, (LPARAM)&wi);

    return wi.hWnd;
}

Call the test:

std::string processname = "notepad.exe";
DWORD dwProcessId = GetProcessIdFromProcessName(processname);
std::cout << dwProcessId << std::endl;
if (dwProcessId != 0)
{
    HWND hwnd = GetProcessMainWnd(dwProcessId);
    if (hwnd != NULL)
    {
        char WindowTitle[100] = { 0 };
        ::GetWindowText(hwnd, WindowTitle, 100);
        std::cout << WindowTitle << std::endl;
    }
}   

Result output:

11712
无标题 - 记事本

text content

Now that the handle to the main Notepad window has been obtained, the next step is to traverse the child windows.

Traverse child windows

Our goal is to capture the information in the form. At this time, we will introduce a tool, which is quite easy to use Spy++ (how to use it, just Baidu)

spy++

Now what we want to get is the content of the Edit box below. Here we need to traverse the child windows again, we need to use a method EnumChildWindows to enumerate all child windows of a parent window.

BOOL EnumChildWindows(          
    HWND hWndParent,
    WNDENUMPROC lpEnumFunc,
    LPARAM lParam
);

parameter:

  • hWndParent: parent window handle
  • lpEnumFunc: the address of the callback function
  • lParam: custom parameter

The callback function is as follows:

BOOL CALLBACK EnumChildProc(          
    HWND hwnd,
    LPARAM lParam
);

parameter:

  • hwnd: a child window handle specified by the parent window
  • lParam: parameter specified by EnumChidWindows
    Return value: If it returns TRUE, the enumeration continues until the enumeration is completed; if it returns FALSE, the enumeration will be aborted.

Direct light code:

BOOL CALLBACK EnumNotepadChildWindowsProc(HWND hWnd, LPARAM lParam)
{
    char szTitle[100] = { 0 };
    ::GetWindowText(hWnd, szTitle, 100);

    long lStyle = GetWindowLong(hWnd, GWL_STYLE);
    if (lStyle & ES_MULTILINE)
    {
        long lineCount = SendMessage(hWnd, EM_GETLINECOUNT, 0,0);
        for (int i = 0; i < lineCount; i++)
        {
            //long chCount = SendMessage(hWnd, EM_LINELENGTH, (WPARAM)i, 0);
            char szContent[200] = { 0 };
            szContent[0] = 200; //此处注意下,如果不设置EM_GETLINE无法获取内容
            long ret = SendMessage(hWnd, EM_GETLINE, (WPARAM)i, (LPARAM)(LPCSTR)szContent);
            if (ret > 0)
            {
                szContent[ret] = '\0';
                std::cout << "line: " << i << ", Content: " << szContent << std::endl;
            }
        }
    }
    else
    {
        std::string title(szTitle);
        if (!title.empty())
            std::cout << title << std::endl;
    }

    return true;
}

The call is as follows:

std::string processname = "notepad.exe";
DWORD dwProcessId = GetProcessIdFromProcessName(processname);
std::cout << dwProcessId << std::endl;
if (dwProcessId != 0)
{
    HWND hwnd = GetProcessMainWnd(dwProcessId);
    if (hwnd != NULL)
    {
        char WindowTitle[100] = { 0 };
        ::GetWindowText(hwnd, WindowTitle, 100);
        std::cout << WindowTitle << std::endl;
        EnumChildWindows(hwnd, EnumNotepadChildWindowsProc, NULL);
    }
}

The result is as follows:

line: 0, Content: 第一行 iceman
line: 1, Content: 第二行 Hello
line: 2, Content: 第三行 World

At this point, the set goal has been completed

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324652806&siteId=291194637