尝试打印时堆损坏
Heap corruption when attempting to print
我一直在开发一个本地 Unity 插件,它允许用户在 Windows 上的默认打印机上打印一些东西(目前是文本)。
我的 (EDIT: OLD) 打印文本的代码如下:
bool PrintText(const char* pText, int pTextWidth, int pTextHeight, const char* pPrinterName) {
LPCSTR szDriver = (LPCSTR)"WINSPOOL";
TCHAR szPrinter[256];
DWORD cchBuffer = 255;
HDC hdcPrint = NULL;
HDC hdcPrintImg = NULL;
HANDLE hPrinter = NULL;
PRINTER_INFO_2 *pPrinterData;
BYTE pdBuffer[16384];
BOOL bReturn = FALSE;
LPCSTR documentFilename = "PrintTest";
LPCSTR documentText = (LPCSTR)pText;
DWORD cbBuf = sizeof(pdBuffer);
DWORD cbNeeded = 0;
pPrinterData = (PRINTER_INFO_2 *)&pdBuffer[0];
bReturn = GetDefaultPrinter(szPrinter, &cchBuffer);
if (bReturn) {
bReturn = OpenPrinter((LPSTR)pPrinterName, &hPrinter, NULL);
}
if (bReturn) {
bReturn = GetPrinter(hPrinter, 2, &pdBuffer[0], cbBuf, &cbNeeded);
ClosePrinter(hPrinter);
}
if (bReturn) {
hdcPrint = CreateDC(szDriver, (LPSTR)pPrinterName, pPrinterData->pPortName, NULL);
}
if (hdcPrint) {
Escape(hdcPrint, STARTDOC, 8, documentFilename, NULL);
TextOut(hdcPrint, pTextWidth, pTextHeight, documentText, strlen((const char*)documentText));
Escape(hdcPrint, NEWFRAME, 0, NULL, NULL);
Escape(hdcPrint, ENDDOC, 0, NULL, NULL);
DeleteDC(hdcPrint);
}
return bReturn;
}
这会将文档发送到打印后台处理程序并成功打印,但是,我在 VS 中触发了一个断点,提示堆已损坏。
我对 C++ 和一般的非托管语言还很陌生,所以任何指点(哈!)将不胜感激:)
编辑:
工作人员帮助查明了问题。
将文本打印到打印机的最少代码:
bool PrintText(char* inputText, int positionX, int positionY, char* printerName)
{
HDC printerDeviceContext = NULL;
HANDLE printerHandle = NULL;
BOOL bReturn = FALSE;
LPCSTR documentFilename = "PrintTest";
LPCSTR documentText = (LPCSTR)inputText;
DWORD buffer;
DWORD bytesRequired;
bReturn = OpenPrinter((LPSTR)printerName, &printerHandle, NULL);
GetPrinter(printerHandle, 2, NULL, 0, &buffer);
BYTE* printerBuffer = new BYTE[buffer]; //allocate buffer
bReturn = GetPrinter(printerHandle, 2, printerBuffer, buffer, &bytesRequired);
ClosePrinter(printerHandle);
printerDeviceContext = CreateDC(NULL, printerName, NULL, NULL);
if (printerDeviceContext)
{
Escape(printerDeviceContext, STARTDOC, 8, documentFilename, NULL);
TextOut(printerDeviceContext, positionX, positionY, documentText, strlen((char*)documentText));
Escape(printerDeviceContext, NEWFRAME, 0, NULL, NULL);
Escape(printerDeviceContext, ENDDOC, 0, NULL, NULL);
DeleteDC(printerDeviceContext);
}
delete[] printerBuffer; //free buffer
return bReturn;
}
我实际上没有看到任何可能导致堆损坏的直接问题。
但是上面的代码还有一些其他问题:
- CreateDC的第三个参数应该为NULL。
- CreateDC 的第二个参数不应转换为 LPSTR!!! (这不是问题,因为无论如何该方法都会排除一个 LPCSTR,但仍然:只有在极少数情况下库编写不正确的情况下才需要放弃 const ......使用它总是有未定义行为的风险)。
- 一般:只有在必要时才进行转换。 (在大多数情况下,在您的代码中,它实际上不是)
- 切勿在堆栈上分配 16k 内存 (
BYTE pdBuffer[16384]
):基本上您应该调用 GetPrinter() 两次:一次不提供缓冲区。此调用将失败,但它将 return 实际需要的缓冲区大小。然后为该大小分配一个缓冲区并将其提供给第二次调用。
编辑:
4 实际上应该看起来像这样:
GetPrinter(hPrinter, 2, NULL, 0, &cbNeeded);
BYTE* pBuffer = new BYTE[cbNeeded]; //allocate buffer
bReturn = GetPrinter(hPrinter, 2, pBuffer, cbNeeded, &cbActual);
// do something with pBuffer
delete[] pBuffer; //free buffer
编辑 2:
基本上有三件事可能会出错:
您尝试释放已经释放的内存
delete pObject;
//some code
delete pObject;
您尝试使用已经释放的内存
delete pObject;
pObject->use();
堆上某处发生缓冲区溢出
pBuffer = new BYTES[3]
memcpy(pBuffer, pSomeMemory, 100); //copy 100 bytes into a 3 byte buffer
delete[] pBuffer;
实际错误出现在代码的其他部分的可能性非常高。
我一直在开发一个本地 Unity 插件,它允许用户在 Windows 上的默认打印机上打印一些东西(目前是文本)。
我的 (EDIT: OLD) 打印文本的代码如下:
bool PrintText(const char* pText, int pTextWidth, int pTextHeight, const char* pPrinterName) {
LPCSTR szDriver = (LPCSTR)"WINSPOOL";
TCHAR szPrinter[256];
DWORD cchBuffer = 255;
HDC hdcPrint = NULL;
HDC hdcPrintImg = NULL;
HANDLE hPrinter = NULL;
PRINTER_INFO_2 *pPrinterData;
BYTE pdBuffer[16384];
BOOL bReturn = FALSE;
LPCSTR documentFilename = "PrintTest";
LPCSTR documentText = (LPCSTR)pText;
DWORD cbBuf = sizeof(pdBuffer);
DWORD cbNeeded = 0;
pPrinterData = (PRINTER_INFO_2 *)&pdBuffer[0];
bReturn = GetDefaultPrinter(szPrinter, &cchBuffer);
if (bReturn) {
bReturn = OpenPrinter((LPSTR)pPrinterName, &hPrinter, NULL);
}
if (bReturn) {
bReturn = GetPrinter(hPrinter, 2, &pdBuffer[0], cbBuf, &cbNeeded);
ClosePrinter(hPrinter);
}
if (bReturn) {
hdcPrint = CreateDC(szDriver, (LPSTR)pPrinterName, pPrinterData->pPortName, NULL);
}
if (hdcPrint) {
Escape(hdcPrint, STARTDOC, 8, documentFilename, NULL);
TextOut(hdcPrint, pTextWidth, pTextHeight, documentText, strlen((const char*)documentText));
Escape(hdcPrint, NEWFRAME, 0, NULL, NULL);
Escape(hdcPrint, ENDDOC, 0, NULL, NULL);
DeleteDC(hdcPrint);
}
return bReturn;
}
这会将文档发送到打印后台处理程序并成功打印,但是,我在 VS 中触发了一个断点,提示堆已损坏。
我对 C++ 和一般的非托管语言还很陌生,所以任何指点(哈!)将不胜感激:)
编辑: 工作人员帮助查明了问题。 将文本打印到打印机的最少代码:
bool PrintText(char* inputText, int positionX, int positionY, char* printerName)
{
HDC printerDeviceContext = NULL;
HANDLE printerHandle = NULL;
BOOL bReturn = FALSE;
LPCSTR documentFilename = "PrintTest";
LPCSTR documentText = (LPCSTR)inputText;
DWORD buffer;
DWORD bytesRequired;
bReturn = OpenPrinter((LPSTR)printerName, &printerHandle, NULL);
GetPrinter(printerHandle, 2, NULL, 0, &buffer);
BYTE* printerBuffer = new BYTE[buffer]; //allocate buffer
bReturn = GetPrinter(printerHandle, 2, printerBuffer, buffer, &bytesRequired);
ClosePrinter(printerHandle);
printerDeviceContext = CreateDC(NULL, printerName, NULL, NULL);
if (printerDeviceContext)
{
Escape(printerDeviceContext, STARTDOC, 8, documentFilename, NULL);
TextOut(printerDeviceContext, positionX, positionY, documentText, strlen((char*)documentText));
Escape(printerDeviceContext, NEWFRAME, 0, NULL, NULL);
Escape(printerDeviceContext, ENDDOC, 0, NULL, NULL);
DeleteDC(printerDeviceContext);
}
delete[] printerBuffer; //free buffer
return bReturn;
}
我实际上没有看到任何可能导致堆损坏的直接问题。
但是上面的代码还有一些其他问题:
- CreateDC的第三个参数应该为NULL。
- CreateDC 的第二个参数不应转换为 LPSTR!!! (这不是问题,因为无论如何该方法都会排除一个 LPCSTR,但仍然:只有在极少数情况下库编写不正确的情况下才需要放弃 const ......使用它总是有未定义行为的风险)。
- 一般:只有在必要时才进行转换。 (在大多数情况下,在您的代码中,它实际上不是)
- 切勿在堆栈上分配 16k 内存 (
BYTE pdBuffer[16384]
):基本上您应该调用 GetPrinter() 两次:一次不提供缓冲区。此调用将失败,但它将 return 实际需要的缓冲区大小。然后为该大小分配一个缓冲区并将其提供给第二次调用。
编辑: 4 实际上应该看起来像这样:
GetPrinter(hPrinter, 2, NULL, 0, &cbNeeded);
BYTE* pBuffer = new BYTE[cbNeeded]; //allocate buffer
bReturn = GetPrinter(hPrinter, 2, pBuffer, cbNeeded, &cbActual);
// do something with pBuffer
delete[] pBuffer; //free buffer
编辑 2: 基本上有三件事可能会出错:
您尝试释放已经释放的内存
delete pObject; //some code delete pObject;
您尝试使用已经释放的内存
delete pObject; pObject->use();
堆上某处发生缓冲区溢出
pBuffer = new BYTES[3] memcpy(pBuffer, pSomeMemory, 100); //copy 100 bytes into a 3 byte buffer delete[] pBuffer;
实际错误出现在代码的其他部分的可能性非常高。