Windows 发送UVC XU Set命令卡死的一种处理办法
Windows系统应用层可以通过获取UVC设备的IKsControl对象,然后调用KsProperty来GET/SET XU命令。经过测试发现,如果调用SET命令的时候设备拔出了,有一定的概率会出现卡死现象,KsProperty这个方法卡住了。复现方法很简单,获取IKsControl对象,然后Sleep 5秒钟,调用Sleep之后立即手动拔出设备,然后再调用KsProperty去Set命令,100%会卡死。
如果是在UI线程调用Set命令导致卡死就惨了,整个UI都会卡住,UI程序就挂了,没得恢复。由于KsProperty参数很简单,没有哪个参数设置可以避开卡死问题,因此需要调用者去处理规避。
STDMETHOD(KsProperty)(
THIS_
IN PKSPROPERTY Property,
IN ULONG PropertyLength,
IN OUT LPVOID PropertyData,
IN ULONG DataLength,
OUT ULONG* BytesReturned
) PURE;
经过思考,我设计了一种普遍性的方法可以规避这种调用有可能卡死的问题。 代码如下:
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
BOOL
CallTimeoutFunction(
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD timeoutSec)
{
if (NULL == lpStartAddress)
{
return FALSE;
}
BOOL retVal = FALSE;
HANDLE hThread = CreateThread(NULL, 0, lpStartAddress, lpParameter, 0, NULL);
if (WAIT_OBJECT_0 == WaitForSingleObject(hThread, timeoutSec))
{
retVal = TRUE;
printf("finish!\n");
}
else
{
TerminateThread(hThread, -1);
printf("timeout!\n");
}
CloseHandle(hThread);
return retVal;
}
DWORD WINAPI TimeoutFunc(LPVOID lpParameter)
{
printf("call func\n");
Sleep(50*1000);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
CallTimeoutFunction(TimeoutFunc, NULL, 100);
printf("main over\n");
}
在main中调用了一个Sleep 50s的函数,如果没有处理,程序必定会卡住50s, 处理之后,立马就调用完毕了,输出结果:
call func
timeout!
main over
CallTimeoutFunction是一个超时调用函数,可以设置调用某个函数的等待超时时间,超过了就返回。CallTimeoutFunction内部实现也比较简单,开启一个线程,然后调用WaitForSingleObject设置好超时时间,等待线程结束,如果超时就返回FALSE,否则返回TRUE.
这样就把一个卡死的调用变成超时等待调用,传递合适的参数,再怎么调用都不会卡死了。
当然可以再深度封装一下,在线程里while(1)等待调用事件,只有第一次或者卡死之后调用再创建线程,这样就大大减小了线程的创建次数,提高了效率。
在我的XU调试工具就加了这样的处理,打开设备之后,拔出设备,再去点击SET按钮,UI程序并不会卡死,会提示超时了,用起来体验也好一些。