Hello,

welcome to my blog of various technical solutions, experiences and tutorials.

Please feel free to use the Archive menu to go through my articles.

Tuesday, January 24, 2012

2. Implementing custom simple XPS filter

Hi,


in this article I will show you what to do if you want to create your own XPS print filter and use it in the existing XPSDrvSmpl pipeline. Thanks to the modular structure of pipeline it's quite easy to do so :)

Prerequisities
This article assumes that:
  • you are at least a bit familiar with XPS, its pipeline and the code used in XPSDrvSmpl from WDK
  • you have WDK installed and XPSDrvSmpl built (see my previous post "Building and using XPSDrvSmpl Under Windows XP"))
  • you have Windows SDK Tools installed (or at least have the GUID Generator utility)

So, we are going to create a quite simple filter - its only purpose will be reverting the order of pages in document being printed. That means if you print a document (for example from MS Word) with 10 pages, this filter will make the order of the pages go descending (from 10 to 1). It will be used always (during every printing) as we will not provide any setting possibilities yet.

We will use this filter (let's call it Reverse) as the first one in the pipeline, before any other changes are made to the document.
Here are all the steps we need to take before we proceed to coding (of course we can code the filter first and then take all the other steps, but I believe the order presented here will be better for the article structure ;) )

(Note: throughout the steps I often use the term "sample root" - it denotes the directory in which you have the XPSDrvSmpl sample copied, e.g. c:\projects\xpssample or its original location in WDK directory or whatever else it is)
  1. Ok, let's move on the first step. Go to [sample root]\src\filters and create a new subfolder here, called reverse.
  2. Go to Start menu and choose Programs - Windows SDK Tools and run GUID Generator utility. Generate new unique ID (it should read {8-4-4-4-12 hex chars}, for example {AA356934-2EC6-43af-BF20-05F05D2448D6}), copy it to clipboard and keep somewhere for later use.
  3. You need to edit the XPS print pipeline itself and add the new (yet to come) filter at the first position (to revert pages before any other action is performed).
    Open [sample root]\install\xdsmpl-PipelineConfig.xml for edit. Move behind the Filters opening tag and paste the following code (explanation below):

     <Filter dll   = "XDReverse.dll"
            clsid  = "<your generated GUID from step 2>"
            name   = "Reverse Filter">
            <Input guid  = "{b8cf8530-5562-47c4-ab67-b1f69ecf961e}" comment="IID_IXpsDocumentProvider"/>
            <Output  guid  = "{4368d8a2-4181-4a9f-b295-3d9a38bb9ba0}" comment="IID_IXpsDocumentConsumer"/></Filter>

    The value of dll attribute denotes the name of our (yet to come) filter dynamic library. The clsid attribute should contain your generated GUID exactly in the form which it has in your clipboard. The name can be whatever you want, but should be related to the function of the filter. Please note that there is a closing bracket after the name value.
    Since our filter is of XPS type (not the Stream one), it uses already existing XPS input and output providers with given GUIDs. If you look at the other filters in the pipeline, almost all of them (except the Scale filter, which uses Stream) use XPS interface and thus have the same input and output interfaces. Just copy and paste this items and don't worry about it ;)
  4. Go to [sample root]\install\ and open xdsmpl.inf for edit.
    1. Find the [XPSDrvSample] section and add the xdreverse.dll line to it.
    2. Find the [SourceDisksFiles] section and add the xdreverse.dll = 2 line to it.
    The former line tells the install script that we want to include our dll in the installation. The latter says where the installator should look for it.
  5. Add two new empty files to [sample root]\src\filters\reverse directory (we will fill them later):
    1. reverse.h
    2. reverse.cpp
  6. You need to add the dllentry.cpp file. But it will be almost the same as in the other filters, so go to some other filter directory (e.g. booklet) and copy the dllentry.cpp file from here to the reverse directory. Open it and make following edits:
    1. on the line 28 replace the bkflt.h include with reverse.h (for the new dll to know about our filter class)
      #include "bkflt.h" ==> #include "reverse.h"
    2. Move down to the end of the file and look for DllGetClasObject function. At the top of the function there are commented lines with the GUID of the Booklet filter. I recommend you to replace this GUID with your own (generated in step 2) to easily make following changes.
    3. Rename the bookletCLSID variable (e.g. to reverseCLSID).
    4. Now follow the way of transforming the GUID in this variable exactly and replace the hexadecimal values with your own. Provided your generated GUID is  {AA356934-2EC6-43af-BF20-05F05D2448D6}, you should get something like this:

      CLSID reverseCLSID = {0xAA356934, 0x2EC6, 0x43af, {0xBF, 0x20, 0x05, 0xF0, 0x5D, 0x24, 0x48, 0xD6}};
    5. On the return line change the parameter of GetFilterClassFactory from CBookletFilter to CReverseFilter. Then change the third parameter from bookletCLSID to reverseCLSID. Save the file and close it. Now the dll interface knows (by the changed GUID) which filter to handle when it gets called.

  7. Add sources file (no extension). It is similar to the way of obtaining dllentry.cpp in previous step. Just copy it from some other filter. Then open it and:
    1.  edit the SOURCES variable to have only two files: reverse.cpp and dllentry.cpp (these files will be compiled when build is run). So it should read

      SOURCES=$(SOURCES) \
       dllentry.cpp      \
       reverse.cpp       \

      Bear the backslashes in mind! Keep an empty line after reverse.cpp.
    2. edit the TARGETNAME variable value to xdreverse (or whatever name you wish the created dll should have. But keep in mind that the same name must be listed in the filter pipeline file!). Save the file and close it.
  8. Copy the makefile and makefile.inc files from other filter without any other changes made.
  9. Copy xdbook.inc from the booklet filter directory, rename it to xdreverse.inc, open it and change the value of LIBRARY parameter from xdbook.inc to xdreverse.inc
  10. You are now ready to write the reverse-filter source code and build the driver. The source code is available through my Google Documents storage. It is short and clear as it contains only the class definition and two overriden functions and will probably be discussed later in the future.
    Source code: reverse.h, reverse.cpp
  11. Run x86 checked build utility from Start - Programs - Windows Driver Kits - WDK [version] - Windows Vista and Windows Server 2008 (or higher). After the console comes up, use cd command to move to your [sample root] directory, write build -cZ and hit Enter.
  12. Hurray! Your driver is built and ready to use. Now install (or reinstall) the driver following the steps in Part 1 of this tutorial:)

11 comments:

  1. hello,
    you know how i can get the count of all pages befor print ?
    Thanx.

    ReplyDelete
    Replies
    1. Hi shadi,
      I'm sorry, but I am not aware of any possibility of how to do this (which however doesn't mean there isn't any) at the moment. In my opinion the XPS document is processed sequentially with no (or not much) information about upcoming parts.

      Delete
  2. Hey! Great info and a related question (hope you see it!) Is there a way to know when the XPS sample print driver completes a print job?

    ReplyDelete
    Replies
    1. Hi Neil, thanks for your comment. As far as I know (but I may be mistaken, since it's been almost one year since I worked with XPS), it isn't possible directly (from the filter). However, you can try to use the same approach as I did, although it takes more effort - you can implement your own PortMonitor (based on the WDK sample) and process the LcmEndDocPort method, which gets called by driver right after all data has been sent to a port. Good luck with your coding ;)

      Delete
    2. Thanks! I was afraid of that! lol

      Delete
  3. Hello kibitzer ,
    thank you very much for your valuable tutorial I ever seen .
    Would you please make us a youtube channel , explaining all your information about xps starting with the very beginning .
    Thanks in Advance
    Best regards

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. This comment has been removed by a blog administrator.

    ReplyDelete
  7. This comment has been removed by a blog administrator.

    ReplyDelete