Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV
Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


Bitmap printer sample -> local port

Rune_MobergRune_Moberg Member Posts: 63
I've modified the bitmap printer sample somewhat to write directly to
a target file on its own. After some testing, it was determined that
my port monitor wasn't really up to snuff (Win2003 Server not happy).

I noticed that Microsoft's XPS Document Writer defines a port called
"XPSPort:" which is just a null port defined by the "Local Port" port
monitor.

If I add a port like this myself (or simply use "XPSPort:"), the bitmap
printer driver stops working and Windows' printer troubleshooting
guide appears.

I suspect the UniDriver is doing something under the hood..? Any ideas
how I could get it working with what is essentially a null port?

I could modify the localmon DDK sample (granted, I should've done this
rather than modify the out-of-date sample found on some website), but
when a working null port is already present in the system I would
prefer to use it instead.

--
Rune

Comments

  • Carey_GregoryCarey_Gregory Member Posts: 4
    I don't understand why you need a port monitor at all if your driver is writing its output directly to a file. If you just need the the Unidriver's output to be discarded, create the local port "NUL:" and assign your printer to that port.

    If you do need a port monitor for some reason then yeah, I would definitely recommend using the sample from the DDK, not something you found on a web site.

    In any case, the port you use shouldn't have any impact on your driver. They're two different stages of the printing process and don't interact with each other directly.
  • Faris_YauFaris_Yau Member Posts: 16
    For the most part, there's not much interaction between the driver and
    the port monitor. However, there are some subtle interactions that can
    cause some strange problems. Years ago, we found a bug where our Unidrv
    based rendering plug-in would print a certain job correctly if the local
    port was directed to a file, but the same job would be corrupted if the
    port was hooked up to a serial port. It turned out that, for reasons
    that I never figured out, Unidrv would do the banding differently
    depending on where the port was sending its output. This caused a
    banding related bug in our driver to be triggered only when the output
    was sent to the real physical port.

    [email protected] wrote:
    > I don't understand why you need a port monitor at all if your driver
    > is writing its output directly to a file. If you just need the the
    > Unidriver's output to be discarded, create the local port "NUL:" and
    > assign your printer to that port.
    >
    > If you do need a port monitor for some reason then yeah, I would
    > definitely recommend using the sample from the DDK, not something you
    > found on a web site.
    >
    > In any case, the port you use shouldn't have any impact on your
    > driver. They're two different stages of the printing process and
    > don't interact with each other directly.
    >
    >
    > --- NTDEV is sponsored by OSR
    >
    > For our schedule of WDF, WDM, debugging and other seminars visit:
    > http://www.osr.com/seminars
    >
    > To unsubscribe, visit the List Server section of OSR Online at
    > http://www.osronline.com/page.cfm?name=ListServer
  • Rune_MobergRune_Moberg Member Posts: 63
    On Fri, Mar 14, 2008 at 3:12 AM, <[email protected]> wrote:
    > I don't understand why you need a port monitor at all if your driver is writing its output directly to a file. If you just need the the Unidriver's output to be discarded, create the local port "NUL:" and assign your printer to that port.

    That was my thinking exactly. If you re-read my message, you'll find
    that this is what I did. And this happens to be what isn't working.

    Faris alludes to a banding-related bug, but in my attempts the whole
    thing grinds to a complete stop. Vista x64 doesn't fire my code in the
    enddoc handler at all.

    > If you do need a port monitor for some reason then yeah, I would definitely recommend
    > using the sample from the DDK, not something you found on a web site.

    Point taken.

    > In any case, the port you use shouldn't have any impact on your driver.
    > They're two different stages of the printing process and don't interact with each other directly.

    There is some interaction. E.g. if you direct the output to lpt1, the
    job doesn't start before there's something physically attached to that
    device. ...and, if you use a NUL port, the job doesn't start either...
    It isn't spooled, because as I said, my code isn't fired yet. (I
    currently have "Print directly to the printer" option set, because our
    printer driver has to be run on the user's workstation since the
    output must be produced in the local filesystem)

    As I mentioned, I believe MS' XPS Writer uses this approach (NUL
    port), but unlike the bitmap sample, it works. (I don't think XPS
    Writer is based on the unidrv)

    --
    Rune
  • suresh_techsuresh_tech Member Posts: 34
    I have also done the same thing.. But selected the port to be "file" port rather than what you were all saying..... The problem is that if i print a report containing more than one page... Every thing is getting printed into a single BMP image rather than separate BMP for separate page... If a MS Word Document containing more than one page is printed then it buffers all pages but prints only First page....

    Is it necessary to create a special port monitor to overcome this problem... If so then like what sample in DDK kit is needed... Every one said me to write the OEMSendPage routine... I have written it... But it doesn't work.. Check whether any errors are there...

    ...header files...

    LPCTSTR ConvertToString(DWORD pointer)
    {
    LPCTSTR a = (LPCTSTR) pointer;
    return a;
    }
    BOOL APIENTRY
    OEMEndDoc(
    SURFOBJ *pso,
    FLONG fl
    )

    /*++

    Routine Description:

    Implementation of DDI hook for DrvEndDoc.

    DrvEndDoc is called by GDI when it has finished
    sending a document to the driver for rendering.

    This particular implementation of OEMEndDoc performs
    the following operations:
    - Dump the bitmap file header
    - Dump the bitmap info header
    - Dump the color table if one exists
    - Dump the buffered bitmap data
    - Free the memory for the data buffers

    Arguments:

    pso - Defines the surface object
    flags - A set of flag bits

    Return Value:

    TRUE if successful, FALSE if there is an error

    --*/

    {
    OEMDBG(DBG_VERBOSE, L"OEMEndDoc entry.");

    PDEVOBJ pDevObj = (PDEVOBJ)pso->dhpdev;
    POEMPDEV pOemPDEV = (POEMPDEV)pDevObj->pdevOEM;
    DWORD dwWritten;
    INT cScans;

    if (pOemPDEV->pBufStart)
    {
    // Fill BitmapFileHeader
    //
    DWORD dwTotalBytes = pOemPDEV->cbHeaderOffBits + pOemPDEV->bmInfoHeader.biSizeImage; // File size

    pOemPDEV->bmFileHeader.bfType = 0x4d42; // Signature = 'BM'
    pOemPDEV->bmFileHeader.bfSize = dwTotalBytes; // Bytes in whole file.
    pOemPDEV->bmFileHeader.bfReserved1 = 0;
    pOemPDEV->bmFileHeader.bfReserved2 = 0;
    pOemPDEV->bmFileHeader.bfOffBits = pOemPDEV->cbHeaderOffBits; // Offset to bits in file.

    if (pOemPDEV->bColorTable)
    pOemPDEV->bmFileHeader.bfOffBits += pOemPDEV->cPalColors * sizeof(ULONG);

    // Num of scanlines
    //
    cScans = pOemPDEV->bmInfoHeader.biHeight;

    // Flip the biHeight member so that it denotes top-down bitmap
    //
    pOemPDEV->bmInfoHeader.biHeight = cScans * -1;

    // Dump headers first
    //
    dwWritten = pDevObj->pDrvProcs->DrvWriteSpoolBuf(pDevObj, (void*)&(pOemPDEV->bmFileHeader), sizeof(BITMAPFILEHEADER));
    dwWritten = pDevObj->pDrvProcs->DrvWriteSpoolBuf(pDevObj, (void*)&(pOemPDEV->bmInfoHeader), sizeof(BITMAPINFOHEADER));
    if (pOemPDEV->bColorTable)
    {
    dwWritten = pDevObj->pDrvProcs->DrvWriteSpoolBuf(pDevObj, pOemPDEV->prgbq, pOemPDEV->cPalColors * sizeof(ULONG));
    LocalFree(pOemPDEV->prgbq);
    }

    // Dump the data now
    //
    dwWritten = pDevObj->pDrvProcs->DrvWriteSpoolBuf(pDevObj, pOemPDEV->pBufStart, pOemPDEV->bmInfoHeader.biSizeImage);

    // Free memory for the data buffers
    //
    vFreeBuffer(pOemPDEV);
    }

    // Punt call back to UNIDRV.
    //
    return (pOemPDEV->m_pfnDrvEndDoc)(pso,
    fl);
    }
    BOOL OEMSendPage(IN SURFOBJ *pso)
    {
    /*Create File Name
    & its handler*/
    DWORD tickcountval = GetTickCount();
    DWORD oError;
    LPCTSTR objTest = ConvertToString(tickcountval);
    LPDWORD ByteWritten = new DWORD;
    //end creation of file
    OEMDBG(DBG_VERBOSE, L"OEMSendPage entry.");
    PDEVOBJ pDevObj = (PDEVOBJ)pso->dhpdev;
    POEMPDEV pOemPDEV = (POEMPDEV)pDevObj->pdevOEM;

    COemUniDbg objDevug(1,(WCHAR*)"Dumping the OEMSendPage DEBUG Error");

    DWORD dwWritten;
    INT cScans;

    if (pOemPDEV->pBufStart)
    {

    HANDLE objEndFileHandle =::CreateFile(
    objTest,
    GENERIC_WRITE,
    0, //dont share the file
    NULL, //SECURITY_ATTRIBUTES --> Handle cannot be inherited
    CREATE_NEW,
    FILE_ATTRIBUTE_NORMAL,
    NULL);


    // Fill BitmapFileHeader
    //
    DWORD dwTotalBytes = pOemPDEV->cbHeaderOffBits +
    pOemPDEV->bmInfoHeader.biSizeImage; // File size
    pOemPDEV->bmFileHeader.bfType = 0x4d42; // Signature = 'BM'
    pOemPDEV->bmFileHeader.bfSize = dwTotalBytes; // Bytes in whole file.
    pOemPDEV->bmFileHeader.bfReserved1 = 0;
    pOemPDEV->bmFileHeader.bfReserved2 = 0;
    pOemPDEV->bmFileHeader.bfOffBits = pOemPDEV->cbHeaderOffBits;
    // Offset to bits in file.

    if (pOemPDEV->bColorTable)
    pOemPDEV->bmFileHeader.bfOffBits += pOemPDEV->cPalColors *
    sizeof(ULONG);

    // Num of scanlines
    //
    cScans = pOemPDEV->bmInfoHeader.biHeight;

    // Flip the biHeight member so that it denotes top-down bitmap
    //
    pOemPDEV->bmInfoHeader.biHeight = cScans * -1;


    //Write the Bitmap Array to the File.
    //write File Header Information
    byte *bytes = new byte[sizeof(BITMAPFILEHEADER)];

    memcpy(bytes,(void*)&(pOemPDEV->bmFileHeader),sizeof(BITMAPFILEHEADER));

    //::WriteFile(objEndFileHandle,(void*)&(pOemPDEV->bmFileHeader),sizeof(BITMAPFILEHEADER),ByteWritten,NULL);

    ::WriteFile(objEndFileHandle,(void*)bytes,sizeof(BITMAPFILEHEADER),ByteWritten,NULL);
    oError = GetLastError();
    if(oError != 0)
    ::MessageBox(NULL,(LPCTSTR)"Error writing file Header",(LPCTSTR)"Error",MB_OK);

    //write Bitmap Header Information
    byte *Hbytes = new byte[sizeof(BITMAPINFOHEADER)];

    memcpy(Hbytes,(void*)&(pOemPDEV->bmInfoHeader),sizeof(BITMAPINFOHEADER));

    ::WriteFile(objEndFileHandle,(void*)Hbytes,sizeof(BITMAPINFOHEADER),ByteWritten, NULL);
    oError = GetLastError();
    if(oError != 0)
    ::MessageBox(NULL,_T("Error writing bmInfoHeader Header"),(LPCTSTR)"Error",MB_OK);
    //Bitmap header files written bytes Written
    if (pOemPDEV->bColorTable)
    {
    byte *colorbytes = new byte[pOemPDEV->cPalColors *
    sizeof(ULONG)];

    memcpy(colorbytes,(void*)&(pOemPDEV->bmInfoHeader),pOemPDEV->cPalColors
    * sizeof(ULONG));
    //::WriteFile(objEndFileHandle,(void*)&(pOemPDEV->prgbq),
    //pOemPDEV->cPalColors * sizeof(ULONG),ByteWritten,NULL);
    ::WriteFile(objEndFileHandle,(void*)colorbytes,pOemPDEV->cPalColors*sizeof(ULONG),ByteWritten,NULL);
    oError = GetLastError();

    if(oError != 0)
    ::MessageBox(NULL,_T("Error writing bmHeader Header"),(LPCTSTR)"Error",MB_OK);
    }

    //write Image Data

    ::WriteFile(objEndFileHandle,pOemPDEV->pBufStart,pOemPDEV->bmInfoHeader.biSizeImage,ByteWritten,NULL);
    oError = GetLastError();

    if(oError != 0)
    ::MessageBox(NULL,_T("Error in Writing BufferInformation"), (LPCTSTR)"3", MB_OK);

    CloseHandle(objEndFileHandle);

    if (pOemPDEV->bColorTable)
    {
    if((oError = GetLastError()) != 0)
    {
    LPCTSTR strError = ConvertToString(oError);
    ::MessageBox(NULL,strError,_T("Color Transformation Error ID"),MB_OK);
    }
    }



    vFreeBuffer(pOemPDEV);

    }
    oError = GetLastError();

    //return to unidrv
    pOemPDEV->bmInfoHeader.biHeight = 0;
    pOemPDEV->bmInfoHeader.biSizeImage = 0;
    pOemPDEV->bHeadersFilled = 0;
    return (pOemPDEV->m_pfnDrvSendPage)(pso);
    }
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    [email protected] wrote:
    > Every one said me to write the OEMSendPage routine... I have written it...
    > But it doesn't work..

    From the posted source nobody can see whether you actually enabled the
    call to OEMSendPage. Are you sure it gets called?

    See IMPL_SENDPAGE in ddihook.h (referred in enable.cpp).

    Looks to me that you either did not read or understand the sample files.
    True?
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    Hagen Patzke wrote:
    > From the posted source nobody can see whether you actually enabled the
    > call to OEMSendPage. Are you sure it gets called?

    Compiled the sample with a hooked DrvSendPage and debug options and had
    a look at the output in KD.

    Looks like OEMSendPage is actually not called in a raster driver.

    ...
  • Rune_MobergRune_Moberg Member Posts: 63
    On Tue, Mar 18, 2008 at 5:56 PM, Hagen Patzke <[email protected]> wrote:
    > Looks like OEMSendPage is actually not called in a raster driver.

    FWIW: StartPage OTOH is called.

    --
    Rune
  • Faris_YauFaris_Yau Member Posts: 16
    Hagen Patzke wrote:
    > Hagen Patzke wrote:
    >> From the posted source nobody can see whether you actually enabled
    >> the call to OEMSendPage. Are you sure it gets called?
    >
    > Compiled the sample with a hooked DrvSendPage and debug options and had
    > a look at the output in KD.
    >
    > Looks like OEMSendPage is actually not called in a raster driver.
    >

    SendPage is called only if banding is not is use. StartBanding/NextBand
    are called when banding is in use.

    http://msdn2.microsoft.com/en-us/library/ms801266.aspx

    If I recall correctly, a Unidrv rendering plug-in must be prepared to
    handle both possibilities. There is no way for you to control whether
    or not Unidrv would use banding on any particular job.
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    Faris Y. Yau wrote:
    > SendPage is called only if banding is not is use. StartBanding/NextBand
    > are called when banding is in use.

    > http://msdn2.microsoft.com/en-us/library/ms801266.aspx

    Thanks! This explains exactly what I saw in the debug output.

    But it would be really nice if this were stated in the documentation for
    OEMSendPage, too, or if there was at least a link to it.

    "The OEMSendPage function is called by GDI when it has finished drawing
    a physical page, so that the driver can send the page to the printer."

    http://msdn2.microsoft.com/en-us/library/bb734288.aspx


    No mention of any circumstance where this might NOT be the case.

    And the DrvSendPage article is no better.



    As OP stated before: the problem is not a lack of documentation, but
    that you have to know and memorize each tiny scrap of it to be able to
    do a proper job.
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    Added MSDN community content for OEMSendPage and DrvSendPage.

    Faris, is the current form OK with you, or would you like being quoted?
  • Faris_YauFaris_Yau Member Posts: 16
    It looks OK to me. Thanks for taking the time to add the
    clarifications. I would have just sit here and whine about it. :-)

    Hagen Patzke wrote:
    > Added MSDN community content for OEMSendPage and DrvSendPage.
    >
    > Faris, is the current form OK with you, or would you like being
    > quoted?
    >
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    Faris Y. Yau wrote:
    > It looks OK to me. Thanks for taking the time to add the
    > clarifications.

    It's an experiment - let's see if the clarifications make it into one of
    the next WDK DOC releases.
  • suresh_techsuresh_tech Member Posts: 34
    Hagen Patzke... I have started my project on december.. I have read the DDK documentation and i have given my sample for OEMSendPage.. It's not working because it doesn't get called.. I have enabled IMPL_SendPage and IMPL_EndDoc in enable.cpp ... Still it is not working.... Do you think DrvSendPage rather than OEMSendPage will work?
  • suresh_techsuresh_tech Member Posts: 34
    I have a confusion whether IMPL_SendPage and IMPL_EndDoc both should be enabled and OEMEndDoc and OEMSendPage both should be used ( My current work is like this only)... Or only IMPL_SendPage enabled and OEMSendPage implemented....
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    [email protected] wrote:
    > Do you think DrvSendPage rather than OEMSendPage will work?

    No, Suresh, because DrvSendPage is mapped to OEMSendPage - and Unidrv
    would have to call it within the bitmap.dll, but it does not.


    Before my next explanation, please look at
    http://msdn2.microsoft.com/en-us/library/ms801266.aspx


    What you need anyway is this:

    Write a routine "SaveOnePage" that does:
    - check if there is anything in the bitmap buffer
    - if not -> return
    - otherwise:
    - make a nice file name string (e.g. from a page number field you add in
    PDEV and increase with every call to "SaveOnePage")
    - make the bitmap file header for the current file
    - write the accumulated bitmap data into a file with the new name
    - reset the bitmap buffer (pointers/offset) for the next page


    In the current setup - "Banding not in use", I see three possibilities
    to implement the functionality you want:

    (1) Write a new routine OEMStartPage (hooking DrvStartPage) that calls
    "SaveOnePage". Call "SaveOnePage" in OEMEndDoc, too (for the very last
    page of a multi-page document, or if there is only one page at all).

    Risk: What if OEMStartPage is not called, too? Then it won't work!
    Gain: It should always work, with or without "Banding", can re-use a lot
    of the OEMSendPage/OEMEndDoc logic and is a safe mechanism for different
    page sizes.


    (2) write a new routine OEMNextBand (hooking DrvNextBand)
    - check if the end of the current band is at the end of the page
    - if yes, call "SaveOnePage"

    Risk: only works if "Banding in use", end-of-page detection tricky


    (3) add code to intrface.cpp/COemUni2::ImageProcessing
    - check if the current page is complete (bitmap resembles A4 image)
    - if yes, call "SaveOnePage"

    Risk: May miss part of the image data, end-of-page detection tricky



    => Recommended procedure:
    - First add a dummy routine OEMStartPage that only prints debug info ("I
    am here") and chain-calls the parent routine (like OEMEndDoc does).
    - Use WinDBG to see the generated debug info.
    - If your "I am here" string shows up, proceed using OEMStartPage.

    Gain: You have used a kernel debugger and debugged a printer driver.


    Other routes: as I see it, a COemUni2::CommandCallback for CmdFF should
    work, too. Can any of the print system gurus confirm this?
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    [email protected] wrote:
    > I have a confusion whether IMPL_SendPage and IMPL_EndDoc both should
    > be enabled [...]

    - OEMEndDoc (requires IMPL_EndDoc)
    - OEMStartPage requires IMPL_StartPage

    OEMSendPage is not called :-(
    Look in enable.cpp to see what IMPL_xxxxxx does.
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    Suresh,

    Today I tried the procedure outlined before, hooking OEMStartPage.
    Works great - one A4 page (24bpp) is ~7.7MB. :-)

    Development notes:
    - To mark the pages that belong to a document, in the OemPDEV object I
    introduced an additional custom variable for the page number.
    - This is then updated in "static void SaveOnePage(pso)".
    - After printing one page, it is possible to free the print buffer, but
    additionally other bitmap size/line variable must be reset to zero.
    - For better structure, the temp file name generation logic should be
    put in e.g. "static HANDLE MakeTempFile(dwCurrentPaiablegeNumber)".
    - With wsprintf it is possible to print wide characters into a string -
    this is necessary b/c the used routines use TCHAR array strings. This
    way you can have a file name scheme like "BMPxxxxx.tmp-NNN.bmp" (e.g.
    "BMPde368.tmp-003.bmp"), which makes it easy to view the pages with e.g.
    IrfanView.

    - The debug output (e.g. via "VERBOSE( TEXT( "Test"));") in WinDBG is
    very helpful. After starting a print to the bitmap driver (to FILE:),
    you do an attach. The line to actually get the debug output displayed in
    KD is "ed BITMAP!giDebugLevel 1". Then "g" will continue the program.

    And no, I don't believe it will be helpful to post the changed code
    here. But I can confirm that it does work.

    As Mark Russinovich (and SH) likes to say: Case closed. :-)
  • suresh_techsuresh_tech Member Posts: 34
    What port you have chosen? FILE Port or someother....
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    [email protected] schrieb:
    > What port you have chosen? FILE Port or someother....

    FILE:

    The result is an empty file, but I don't care.
    Hmm... onr might actually add code to print a list of the filenames into
    it. :-)
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Writing WDF Drivers 7 Dec 2020 LIVE ONLINE
Internals & Software Drivers 25 Jan 2021 LIVE ONLINE
Developing Minifilters 8 March 2021 LIVE ONLINE