尝试打印时堆损坏

Heap corruption when attempting to print

本文关键字:损坏 打印      更新时间:2023-10-16

我一直在开发一个原生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;
}

实际上我没有看到任何可能导致堆损坏的直接问题。

然而,上面的代码中还有其他几个问题:

  1. CreateDC的第三个参数应该是NULL。
  2. CreateDC的第二个参数不应该被强制转换为LPSTR!(这在这里不是问题,因为该方法无论如何都需要一个LPCSTR,但是仍然:只有在编写错误的库的极少数情况下才需要转换const…使用它总是有未定义行为的风险)。
  3. General:仅在必要时进行强制类型转换。(在大多数情况下,在你的代码,它实际上不是)
  4. 永远不要在堆栈(BYTE pdBuffer[16384])上分配16k内存:基本上你应该调用GetPrinter()两次:一次不提供缓冲区。此调用将失败,但它将返回实际所需的缓冲区大小。然后为该大小分配一个缓冲区,并提供给第二次调用。

编辑: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;

实际错误很有可能出现在代码的其他部分。