当 SHCameraCapture 返回 E_FAIL 时怎么办?
本迷你系列文章是关于使用 SHCameraCapture 的两个场景的一对文章。
引言
本文演示了在使用 SHCameraCapture
或 CameraCaptureDialog
时,如何解决不可靠的问题。
背景
本迷你系列文章是关于使用 SHCameraCapture
的两个场景的一对文章。
第一篇名为 从 C++ Pocket PC 2003 应用程序中使用 SHCameraCapture。它描述了从为 Pocket PC 2003 设备构建的 C++ 项目访问 SHCameraCapture API 所需的步骤。
第二篇名为 如果 SHCameraCapture 返回 E_FAIL 该怎么办? 在本文中,我描述了我使用该 API 的经验,以及我最终能够解决这个问题的方法。
在上一篇文章中,我描述了如何为 Pocket PC 应用程序编译 SHCameraCapture
。它可以工作,但它不可靠。通常,该调用返回 E_FAIL
(但不是 E_OUTOFMEMORY
)。
从我找到的 Web 帖子中,我了解到这与上述解决方案无关,而是与内存问题有关。在我们的例子中,E_FAIL
的发生率随着内存压力、图像大小以及应用程序的“复杂性”(调用捕获的地方)的增加而增加。而且,当 SHCameraCapture
从我们的应用程序中失败时,通过按下设备上的硬件捕获按钮,同样的相机对话框也能正常使用。
我的直觉是,这个问题是由内存碎片引起的:设备上可能还有 10MB 的可用空间,但如果它被分成更小的块,捕获应用程序就无法分配 1MB(对于 640x480x24 位图像)连续的内存来存储图像,然后再将其压缩成小的 JPEG 文件。
但是,在 Pocket PC 中,每个进程都有自己的虚拟内存空间;这可以解释为什么单独的应用程序运行良好。所以,我决定将捕获放在一个单独的进程中。
我为相机捕获创建了一个包装应用程序,它从命令行获取图像参数,将状态信息作为应用程序退出代码返回,并始终将捕获的图像存储在相同的文件名下。结果很酷:从那时起,我再也没有遇到过 E_FAIL
。
使用代码
这个实现(包装器)使用 CF1。 CF1 或 CF2 内置于 Windows Mobile 5 和 6 设备中,因此我们不必下载额外的代码。对于较早的设备(例如 Pocket PC 2003),无论如何都无法使用相机 API,因此 CF1 是一个安全的选择。(如果需要,您可以轻松地使用一个小的 Win32 或 MFC 应用程序来实现相同的行为。)
您可以在此处下载 Visual Studio 2005 的完整捕获包装器解决方案 here。提供的实现仅支持静止图像,分辨率为 320x240、640x480 和 1280x960。如果需要,您可以使用更多命令行参数来扩展它以支持视频。
以下是调用相机捕获包装器应用程序并处理结果的代码
HRESULT hr = E_FAIL;
PROCESS_INFORMATION pi;
LPSTARTUPINFOW psi = NULL;
BOOL bExec = FALSE;
HINSTANCE hInstance = GetModuleHandle(NULL);
TCHAR strMsg[MSGBUFLEN+1];
TCHAR strCaption[MSGBUFLEN+1];
HANDLE hProc = NULL;
DWORD nCaptureAppExitCode;
// Start the capture application in a separate process.
CString strCaptureAppNameWithPath;
strCaptureAppNameWithPath.Format( _T("%s\\%s"),
Utils::GetAppBasePath(), CAPTURE_APP_NAME );
CString strCommandLineArgs;
strCommandLineArgs.Format( _T("/dir=\\Captures /res=1280x960 /stillquality=high") );
bExec = CreateProcess( (LPCTSTR)strCaptureAppNameWithPath, (LPCTSTR)strCommandLineArgs,
NULL, NULL, NULL, 0, NULL, NULL, psi, &pi );
if (!bExec)
{
// CreateProcess failed.
LoadString(hInstance, IDS_COULD_NOT_START_CAPTUREAPP, strMsg, MSGBUFLEN);
MessageBox ( NULL, strMsg, strCaption, MB_ICONEXCLAMATION | MB_OK );
hr = E_FAIL;
} // if (!bExec)
else
{
// CaptureApp was successfully started.
// Wait for CaptureApp to finish.
hProc = OpenProcess ( 0, FALSE, pi.dwProcessId );
WaitForSingleObject( hProc, INFINITE );
// Get the exit code from CaptureApp.
GetExitCodeProcess ( hProc, &nCaptureAppExitCode );
switch ( nCaptureAppExitCode )
{
case CAPTURE_EXITCODE_OK:
hr = S_OK;
strFileName = CAPTURE_IMAGE_NAME;
break;
case CAPTURE_EXITCODE_CANCELLED:
case CAPTURE_EXITCODE_ARGERROR:
default:
hr = S_OK;
strFileName = _T("");
break;
} // switch
} // else (!bExec)
return hr;
// END OF CODE
上面的示例(调用包装器应用程序的示例)使用 C++ 和 MFC。当我浏览网络时,我发现这个问题不仅限于本机应用程序,因此也可以将相同的方法应用于 .NET Compact Framework 应用程序。