C++内存泄漏——需要同行评审
我的截图代码中有一个很大的漏洞 <>我的C++很糟糕,所以如果有人能指出那些非常奇妙的问题! 以下是违规代码:C++内存泄漏——需要同行评审,c++,memory-leaks,C++,Memory Leaks,我的截图代码中有一个很大的漏洞 我的C++很糟糕,所以如果有人能指出那些非常奇妙的问题! 以下是违规代码: FString AWindow::CaptureWindow(HWND hwnd) { HDC hdcSrc = GetWindowDC(hwnd); RECT rawRect; LPRECT rect = &rawRect; GetWindowRect(hwnd, rect); int width = rect->right - rect->left; int
FString AWindow::CaptureWindow(HWND hwnd) {
HDC hdcSrc = GetWindowDC(hwnd);
RECT rawRect;
LPRECT rect = &rawRect;
GetWindowRect(hwnd, rect);
int width = rect->right - rect->left;
int height = rect->bottom - rect->top;
HDC hdcDest = CreateCompatibleDC(hdcSrc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdcSrc, width, height);
HGDIOBJ h0ld = SelectObject(hdcDest, hBitmap);
BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, SRCCOPY);
SelectObject(hdcDest, h0ld);
DeleteDC(hdcDest);
char* pImage = NULL;
pImage = (char*)GlobalLock(hBitmap);
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmp);
//Convert the color format to a count of bits
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
//Allocate memory for the BITMAPINFO structure.
if (cClrBits < 24) {
pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1i64 << cClrBits));
}
else {
pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
}
//Initialize the field in the BITMAPINFO structure
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biClrUsed = (1i64 << cClrBits);
pbmi->bmiHeader.biCompression = BI_RGB;
//Computer the number of bytes in the array of color
//indices and store the result in biSizeImage.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits + 31) & ~31) / 8
* pbmi->bmiHeader.biHeight;
//Set biClrImportant to 0, indicating that all of the device
//colors are important
pbmi->bmiHeader.biClrImportant = 0;
FString file = FPaths::Combine(*FPaths::GameDir(), TEXT("ScreenGrab/"), TEXT("Desktop.bmp"));
std::string mystring(TCHAR_TO_UTF8(*file));
std::wstring lpstring = std::wstring(mystring.begin(), mystring.end());
LPCWSTR realfile = lpstring.c_str();
FString error = CreateBMPFile(hwnd, realfile, pbmi, hBitmap, hdcSrc);
ReleaseDC(hwnd, hdcSrc);
DeleteObject(hBitmap);
return error;
}
再次感谢您的帮助!为解决这一混乱局面的任何人准备的数字布朗尼 pbmi是本地分配的 您忘记使用LocalFree来释放分配 可能还有更多,但这是我发现的第一个。使用资源获取是初始化模式,您不必在每次返回之前都进行关闭和释放。此外,如果引发异常(例如UTF8翻译中的错误字符编码),您将防止泄漏。以下是@JesperJuhl在代码上下文中所指的习惯用法:
using file_raii_t = std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::CloseHandle)>;
using gmem_raii_t = std::unique_ptr<std::remove_pointer<HGLOBAL>::type, decltype(&::GlobalFree)>;
gmem_raii_t gmem(::GlobalAlloc(GMEM_FIXED, size), ::GlobalFree);
// gmem can be used like (!GetDIBits(..., gmem.get(), ...);
file_raii_t fh(::CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, (DWORD)0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL), ::CloseHandle);
//fh can be used like FileRead(fh.get()...);
这可以扩展到包括其他API,甚至包括作为out参数返回资源的API。在您的例子中:GetWindowDC,LocalAlloc。使用RAII,您可以编写更少的异常安全代码行
注意:代码来自我老化的记忆。未在代码中测试。谢谢!改变了!实际上,我将严重泄漏的范围缩小到了CreateBMPFile,我正在文章底部的编辑中添加该文件。介意看一下吗?这个函数会被连续调用一整串,并快速地将MB的内存分配到MB。在每次返回之前,您需要全局释放内存,这会节省大量内存。只需确保你不会有两次空闲。哦,还有一件事,在每次返回之前使用CloseHandle-把手将保持打开状态,这会导致泄漏。谢谢!我读了你的回复,把它全部看了一遍。找到了罪魁祸首,并用其他更改更新了我的代码,我需要在每次返回之前释放HP。尽管hp是指向全局alloc的,但它仍在使用智能指针和自定义删除程序来释放和关闭您的资源。这样你就不用到处手动管理了。顺便说一句,这个问题更适合于哦,我不知道codereview是一个东西。我想知道为什么所有的人都投了反对票。我不确定智能指针与虚幻引擎的兼容性如何,但我会在前进的过程中记住它们:@JesperJuhl Bjarne会为你的RAII评论感到骄傲:注意:GlobalAlloc和CreateFile需要RAII.TCHAR_到_UTF8*文件,这没有意义。您应该在Windows中使用UTF16。例如std::wstring s=Lc:\\temp\\filename.bmp;你不需要在这里进行任何转换。
using file_raii_t = std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::CloseHandle)>;
using gmem_raii_t = std::unique_ptr<std::remove_pointer<HGLOBAL>::type, decltype(&::GlobalFree)>;
gmem_raii_t gmem(::GlobalAlloc(GMEM_FIXED, size), ::GlobalFree);
// gmem can be used like (!GetDIBits(..., gmem.get(), ...);
file_raii_t fh(::CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, (DWORD)0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL), ::CloseHandle);
//fh can be used like FileRead(fh.get()...);