ChappieZ
ChappieZ

Reputation: 15

Heap corruption when attempting to print

I've been working on a native Unity plugin that will allow a user to print something (text at the moment) to the default printer on Windows.

My (EDIT: OLD) code is as follows for printing text:

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;
    }

This will send the document to the print spooler and successfully print, however, I get a breakpoint triggered in VS saying that a heap has been corrupted.

I'm very new to C++ and unmanaged languages in general so any pointers (ha!) will be much appreciated :)

EDIT: A person at work helped pinpoint the problems. The bare minimum code to print text to a printer:

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;
}

Upvotes: 0

Views: 555

Answers (1)

Daniel
Daniel

Reputation: 1051

I actually don't see any direct issues which could result in heap corruption.

However there are a couple of other issues in the code above:

  1. The third parameter of CreateDC should be NULL.
  2. the second param for CreateDC should not be cast to LPSTR!!! (it's not a problem here, since the method excpects a LPCSTR anyway, but still: casting away a const is only necessary in very rare cases of incorrectly written libraries... Using it always has the risk of undefined behavior).
  3. General: Only do casts if it is necessary. (in most cases in your code, it actually isn't)
  4. Never allocate 16k memory on the stack (BYTE pdBuffer[16384]): Basically you should call GetPrinter() twice: once without providing a buffer. This call will fail, but it will return the size of the buffer which is actually required. Then allocate a buffer for that size and provide it to the second call.

Edit: 4 should actually look somewhat like this:

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

Edit 2: Basically there are three things that can go wrong:

  • You try to free memory which has already been freed

    delete pObject; //some code delete pObject;

  • You try to use memory which has already been freed

    delete pObject; pObject->use();

  • You have a buffer overflow on the heap somewhere

    pBuffer = new BYTES[3] memcpy(pBuffer, pSomeMemory, 100); //copy 100 bytes into a 3 byte buffer delete[] pBuffer;

Chances are very high that the actual error is in some other part of your code.

Upvotes: 1

Related Questions