C语言窗口截图代码调用GetDIBits返回ERROR_INVALID_PARAMETER错误
GetDIBits 调用返回 ERROR_INVALID_PARAMETER 的核心排查方向
常见的跨语言移植差异根因
- C语言栈局部结构体未清零,导致字段为随机垃圾值
PowerBuilder 中实例化结构体时会自动将所有成员初始化为0,而C语言的栈上局部变量不会自动初始化内存。原代码中的BITMAPINFO lstr_Info是栈变量,仅给bmiHeader.biSize赋值,其余字段(biWidth、biHeight、biPlanes、biBitCount、biCompression等)全是随机残留值,GetDIBits做参数校验时检测到非法字段值,会直接抛出ERROR_INVALID_PARAMETER错误。 - 未校验前置API的执行结果
调用BitBlt后如果不检查返回值就直接执行GetDIBits,一旦BitBlt执行失败,关联到内存DC的位图内容未定义,同样会触发参数校验失败。 - 非法的入参值
函数入参的width、height如果为0,会导致创建兼容位图、BitBlt调用、GetDIBits全链路触发参数错误,需要在函数入口增加入参合法性判断。
修正后的核心代码片段
extern "C" __declspec(dllexport) BOOL __stdcall WindowScreenShot(const wchar_t* fileName, unsigned long x, unsigned long y, unsigned long width, unsigned long height) { // 入参合法性校验 if (width == 0 || height == 0) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } HWND ll_hWnd; HDC ll_hdc, ll_hdcMem; HBITMAP ll_hBitmap, ll_hOldBmp; HANDLE hDib = NULL, hFile = NULL; char* lpBitmap = NULL; BOOL lb_result, lb_ok = FALSE; // 结构体直接零初始化,替代手动赋值 BITMAPINFO lstr_Info = {0}; BITMAPFILEHEADER lstr_Header = {0}; int li_pixels; DWORD dwBmpSize, dwBytesWritten; ll_hWnd = GetDesktopWindow(); ll_hdc = GetDC(ll_hWnd); ll_hdcMem = CreateCompatibleDC(ll_hdc); ll_hBitmap = CreateCompatibleBitmap(ll_hdc, width, height); if (ll_hBitmap != 0) { ll_hOldBmp = (HBITMAP)SelectObject(ll_hdcMem, ll_hBitmap); lb_result = BitBlt(ll_hdcMem, 0, 0, width, height, ll_hdc, x, y, SRCCOPY); if (lb_result) { lstr_Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); // 此处调用不再触发参数错误 if (GetDIBits(ll_hdcMem, ll_hBitmap, 0, height, NULL, &lstr_Info, DIB_RGB_COLORS) > 0) { // 后续位图写入逻辑 } } // 资源还原 SelectObject(ll_hdcMem, ll_hOldBmp); } // GDI资源清理逻辑 DeleteObject(ll_hBitmap); DeleteDC(ll_hdcMem); ReleaseDC(ll_hWnd, ll_hdc); return lb_ok; }
补充说明
- 调用GetDIBits获取位图信息时,不需要额外给biBitCount赋值,API会自动根据关联位图的实际参数填充合法值;只要提前将结构体清零,仅设置biSize字段,即可满足入参要求。
- GDI资源使用完成后必须还原之前选中的对象、删除兼容DC、释放设备上下文,避免产生GDI资源泄漏。
内容的提问来源于stack exchange,提问作者Dan Cooperstock
相关产品推荐
相关产品推荐

