winapi获取和修改camera raw界面元素数据

        camera raw 界面如下:

        需求就是根据 windows api 来操作界面右边的色温、色调、曝光等属性,进而对图片进行调色。根据 spy++ 捕获的窗口信息,理论上是可以拿到并修改值的。

winapi获取和修改camera raw界面元素数据_第1张图片

        根据 class 可以先拿到窗口句柄:

#define CAMERA_RAW_CLASS_NAME "PSFilter_WindowClass"
HWND getCameraRawHwnd()
{
    HWND h;
    int len;
    while (true) {
        h = FindWindow(TEXT(CAMERA_RAW_CLASS_NAME), NULL);
        if (h == 0) {
            return 0;
        }
        len = GetWindowTextLength(h);
        if (len == 0) {    // 删除空窗口句柄
            SendMessage(h, WM_CLOSE, NULL, NULL);
            SendMessage(h, WM_DESTROY, NULL, NULL);
            SendMessage(h, WM_NCDESTROY, NULL, NULL);
        } else {
            break;
        }
    }

    return h;
}

        这里并没有直接调用 FindWindow() 方法,是因为在后来的实践中发现,当关掉 camera raw 窗口时,句柄可能还在,这样当再次打开 camera raw 窗口时,就存在两个 class 都为 PSFilter_WindowClass 的的句柄,像下面这种情况:

winapi获取和修改camera raw界面元素数据_第2张图片

        而 FindWindow() 方法只能获取到第一个,而这个正是已关闭窗口的句柄缓存,拿不到任何数据。解决这个问题其实也可以调用 EnumWindows() 方法枚举出所有的窗口,然后拿 text 是否为空进行比较。但私以为这样效率较低,故而写了个 while 循环,若查询到的窗口 text 为空,那么直接调用 SendMessage() 方法向系统发送 WM_DESTROY 等消息删除该句柄,再重新查询,直到找到这个窗口为止,就目前来看循环也只会执行两三次而已。

        找到主窗口句柄后,就该寻找具体某个属性的值了,其实这里每个属性也都是一个子窗口。

winapi获取和修改camera raw界面元素数据_第3张图片

         代码如下:

TestCache testCache;    // TestCache 类型后文有说明
static int num = 0;
EnumChildWindows(h, getMapData, NULL);

// 获取所有属性及对应值并写入 map
BOOL CALLBACK getMapData(HWND hwnd, LPARAM lparam)
{
    int len = SendMessage(hwnd, WM_GETTEXTLENGTH, NULL, NULL);
    if (len == 0) {
        cout << endl;
        return true;
    }
    ++num; 
    ++len;
    TCHAR *text = (TCHAR *)malloc(sizeof(TCHAR) * len);
    memset(text, 0, len);
    SendMessage(hwnd, WM_GETTEXT, (WPARAM)len, (LPARAM)text);
    try {
        float val = stof(text);
        testCache[num].h = hwnd;
        testCache[num].val = val;
    } catch (exception &e) {
        cout << e.what() << endl;
    }
    
    return true;
}

        这里主要是调用 EnumChildWindows() 方法枚举出 camera raw 的所有子窗口,并逐一调用自定义的 getMapData() 方法,方法体中主要是调用 SendMessage() 向系统发送消息 WM_GETTEXTLENGTHWM_GETTEXT 来获取具体的值,可以看到程序中定义了个全局 static 变量 num,是因为每个属性并没有唯一的 id 与其对应,只能按某种规则对其筛选并排序,然后将排序的序号和每个属性对应,所以 ++num 的位置就比较重要了。

        拿到值后将所有的值以及对应的序号就存入了 map,如果只是查询属性值,直接定义个简单的 map 就可以了,但是如果要修改这个值呢?是再重新调用 EnumChildWindows() 方法枚举所有子窗口,找到要修改的序号,修改对应的值?这样虽然是可以,并且在修改对应值后立即中止枚举,但效率还是蛮低下的,

        另外,我们已经知道获取元素值是调用 SendMessage() 方法向窗口句柄发送 WM_GETTEXT 消息,那么修改自然是向对应窗口发送 WM_SETTEXT 消息,既然是通过窗口句柄直接操作,那么在第一次枚举查询时可以将每个属性的窗口句柄一起记下来,这样在修改时直接向这个窗口发送修改消息即可。所以 testCache 结构定义如下:

typedef struct hwnd_val {
	HWND h;       // 窗口句柄
	float val;    // 属性值
} hwndVal;
typedef std::map TestCache;

        当遍历完所有属性后,属性序号、属性所属窗口句柄、属性值就都存在 testCache 中了,这时就可以根据序号对某个属性进行修改了:

int id = 2;
float step = 1.0;
string vals = "";
float new_val = testCache[id].val + step;
if (new_val > 0) {
    vals = "+" + to_string(new_val);
} else if (new_val < 0) {
    vals = to_string(new_val);
} else {
    vals = "0";
}
char* val = const_cast(vals.c_str());
SendMessage(testCache[id].h, WM_SETTEXT, 0, (LPARAM)val);

        这里假设的是对序号为 2 的属性值做 +1 处理,待程序执行后,就可以看到 camera raw 界面的色温值 +1 了。

        至此也就实现了对 camera raw 界面元素数据的获取和修改,但具体到项目中应用,还需解决字符集、宽字符与字符转化、生成 dll 等诸多问题,此处不再赘述。

你可能感兴趣的:(windows,windows,c++,camera,raw,winapi)