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.

Thursday, March 29, 2018

XPS driver certification for Windows 10

If you follow Microsoft's Attestation signing a kernel driver for public release guide and try to get your driver signed, you may encouter some troubles as I did. Unfortunately, MS guide does not cover all steps required (and possible errors), neither is there any other resource to read and follow, so I went the hard way through the darkness of the signing process alone and wrote down some notes, which I would like to share with you now.

NOTE: If you have some knowledge of (or experience working with) INF installation files, you may find this article stupid or useless. I do not have any, so I was writing it from my point of view.

After uploading your CAB file in the Dashboard's first step, you have to specify the W10 versions the driver has been tested on. You may be confused as I was when seeing RS2 and RS3 options for the first time:

So here is a little light for this darkness:
Windows 10 RS2 means Redstone 2 which is a codename for build 1703, known as Creators Update
RS3 = build 1709 - Fall Creators Update

The Preparing stage starts and there is an error almost immediately: "Could not load the Inf file install.inf due to Version section is missing from inf"
First lesson learned: Well, apparently the INF file that you use for CAB creation must have a Version section
 (https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-version-section)Where is this mentioned in your guide, Microsoft??!

Specifying just the two mandatory attributes Signature and DriverVer seems to be enough for now to eliminate this error:

[Version]
Signature="$Windows NT$"
DriverVer=07/14/2017,2.0.153.0

(But there is a lot more on this. See below.)

As you may have noticed, when the process fails dashboard allows you to download a file with an error message. Pity that sometimes that file is empty... Thanks, Microsoft! REALLY HELPFUL!!!
Second lesson learnedDo not forget to specify the DestinationDir attribute in your ddf script, otherwise the resulting CAB file will contain no folder and the driver files will be placed in the "root", which is a no-go. Cab file must contain a folder with the driver files.
.Set DestinationDir=XPS

- If you are lucky enough and make it to the Catalog creation stage, it may fail saying
No installation INF found in the root path of the driver. For the driver to be digitally signed, and for it to install properly the installation inf must be in the root of each driver path you have provided. If you have separate folders with different driver packages for different languages, operating systems or device categories then specify each driver set as a separate driver. Each driver package must be selected separately and must contain an installation INF.
Oh, well. So my Inf is not an Inf or what? Should it be in CAB root or inside a folder? After a while spent with unsuccessful attempts, I decided to only work with just one platform (x86) at once - no mixed CAB.

Catalog creation stage sometimes recommends using Inf2Cat utility to check your INF file used in created CAB. And it turns out, it is quite a good thing,
(https://docs.microsoft.com/en-us/windows-hardware/drivers/install/using-inf2cat-to-create-a-catalog-file). I did not manage to find its W10 version on my system, so I used W8.0 version - just to check what it does and what the result is (c:\Program Files (x86)\Windows Kits\8.0\bin\x86\) - it works fine, just don't forget you need to set the OS parameter to Win8, not Win10.

So I took the bunch of files I use for CAB creation (specified in ddf script file), put them in a folder and ran inf2cat:

inf2cat /driver:c:\development\xpsdriver\files /os:8_X86

...and it leads to the exact same error as in Dashboard's Catalog creation stage: No installation INF found in the root path of the driver...
Fine, I can now get the error prior to uploading, which saves some time, but hey, what now...?!

Being more and more desperate I decided to run the Inf2Cat over a Microsoft XPSDrvSample from WDK directory (WinDDK\7600.16385.1\src\print\XPSDrvSmpl\install\) - and wow, the error changes to just some file not found complaints! So now it can find the INF, apparently there must be something special I am missing!

ATTENTION: Here comes a few hours trial-error part where I just desperately tried some things without even knowing why and what it would result in (having no experience with or knowledge of INF). So I can say for sure that I will NOT be able to help you with your questions, should there be any.

NOTE: I can do it this way because of the way I install my printer - I use c++ code to process driver files "manually" and call Windows API, so the INF file works rather as a list of needed files, than an installation script. The parts taken from sample INF file are just for making Inf2Cat (and the Dashboard) happy.


Long story short - I was trying to use parts of xdsmpl.inf file in my own INF file. Turned out that the "right" (read "working") parts are these:

[Manufacturer]
%Microsoft%=Microsoft,NTx86

[Microsoft.NTx86]
"XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_VISTA

[INSTALL_XDSMPL_FILTERS_VISTA]
DriverFile=mxdwdrv.dll
CoreDriverSections="{D20EA372-DD35-4950-9ED8-A6335AFE79F0},UNIDRV.OEM", "{D20EA372-DD35-4950-9ED8-A6335AFE79F5},XPSDRV.OEM,XPSGPD.OEM"

[Strings]
Microsoft="Microsoft"

(the Strings section is mostly placed at the end of the INF file, so don't forget it, otherwise the signing process will fail).

After I added this block to my INF file, Inf2Cat stopped complaining - just printed out some warning. This looked promising. So i uploaded yet another submission to the Dashboard, and after few minutes - Heureka! All stages are green and my driver files are now finally signed by Microsoft!


My resulting INF file follows (highlighted are parts that needed to be added - and Microsoft did not consider it important to tell us in the guide). Just please note company name has been changed to ACME.

<cab_input_path>\files\acme_xps_install86.inf

[Version]
Signature="$Windows NT$"
DriverVer=mm/dd/yyyy,1.2.345.6 ;use your date and version number
Provider="<your_company_name_here>" ;not sure if it has to match your name in EV cert
Class=Printer ;oh yes, we are "installing" printer
ClassGuid={4d36e979-e325-11ce-bfc1-08002be10318} ;Windows defined guid for printers
CatalogFile=xps.cat ;this is IMPORTANT for INF2CAT - the named file gets created but is not necessary for your CAB; if you do not specify this, inf2cat will fail with "CatalogFile not specified in Version section" (or something like that)

[Manufacturer]
%Microsoft%=Microsoft,NTx86

[Microsoft.NTx86]

"XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_VISTA

[INSTALL_XDSMPL_FILTERS_VISTA]

DriverFile=mxdwdrv.dll
CoreDriverSections="{D20EA372-DD35-4950-9ED8-A6335AFE79F0},UNIDRV.OEM", "{D20EA372-DD35-4950-9ED8-A6335AFE79F5},XPSDRV.OEM,XPSGPD.OEM"

[Monitor]
MonitorName=ACME XPS port monitor
MonitorDll=acme_localmon.dll
MonitorUiDll=acme_localui.dll
MonitorEnv=Windows NT x86

[Driver]
DriverName=ACME XPS printer driver
DriverEnv=Windows NT x86
DriverFile=mxdwdrv.dll
DataFile=acme_xps.gpd
ConfigFile=UniDrvUI.dll
HelpFile=UniDrv.HLP
XpsSvcFile=xpssvcs.dll
DependentFiles=acme_xps.gpd,acme_xpsnames.gpd,acme_xpspipeline.xml,acme_xps_install86.inf,acme_xps.ini,acme_xpsui.dll,xdCMYKPrinter.icc,xdwscRGB.icc,xpssvcs.dll,
DataType=RAW

[PrintProcessor]
;WinPrint can be used on XP as well
PrintProcessorPreVista=WinPrint
PrintProcessor=WinPrint

[Printer]
PrinterName=ACME XPS Printer
PrinterEnv=Windows x86

[Strings]
Microsoft="Microsoft"

DDF file for CAB creation:
<cab_input_path>\xps.ddf

.OPTION EXPLICIT     ; Generate errors
.Set CabinetFileCountThreshold=0
.Set FolderFileCountThreshold=0
.Set FolderSizeThreshold=0
.Set MaxCabinetSize=0
.Set MaxDiskFileCount=0
.Set MaxDiskSize=0
.Set CompressionType=MSZIP
.Set Cabinet=on
.Set Compress=on
;Specify file name for new cab file
.Set CabinetNameTemplate=xps.cab
; Specify the subdirectory for the files.
; Your cab file should not have files at the root level,
; and each driver package must be in a separate subfolder.
.Set DestinationDir=XPS
;Specify files to be included in cab file
<cab_input_path>\files\acme_localmon.dll
<cab_input_path>\files\acme_localmon.pdb
<cab_input_path>\files\acme_localui.dll
<cab_input_path>\files\acme_localui.pdb
<cab_input_path>\files\acme_xpsui.dll
<cab_input_path>\files\acme_xps_install86.inf

64bit driver 

For attestation signing of a 64bit driver use NTamd64 decorated model section in your INF file (using NTia64 leads to an error "Inf does not have NTAMD64 decorated model sections" in Inf2Cat and Dashboards also fails):

[Manufacturer]
%Microsoft%=Microsoft,NTamd64

[Microsoft.NTamd64]
"XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_VISTA

[INSTALL_XDSMPL_FILTERS_VISTA]
DriverFile=mxdwdrv.dll
CoreDriverSections="{D20EA372-DD35-4950-9ED8-A6335AFE79F0},UNIDRV.OEM", "{D20EA372-DD35-4950-9ED8-A6335AFE79F5},XPSDRV.OEM,XPSGPD.OEM"




Hope you will get the whole sh*t working too soon!

Monday, May 5, 2014

Setting up WNS on Windows Phone 8.1 - Part 2

Hi again everyone.

Just wanted to let you know of another problem in setting up the Windows Notification Service (Push notifications) on Windows Phone 8.1.

In my previous article I was following up the WNS tutorial from Microsoft, resolving an issue from step 2. As I supposed, this one is not the only one.

In Step 3 "Send the channel URI to your server" a System.NotSupportedException occurs when you try to read the server response using GetResponseAsync function.



It is a shame that Microsoft's official tutorial is so buggy. As in my previous post, not a single mention about possible error is available. At first I though there is a problem with Internet connection on my emulator. However it turned out that the problem is somewhere else and this exception is thrown when the request stream has not been flushed and closed before trying to read the response from server.



Many thanks to StackOverflow user Kristof Van De Voorde, who has proposed the solution to this issue.

Hope this will help someone.

Setting up WNS on Windows Phone 8.1 - Part 1

If you try to add the WNS (Windows Notification Service - aka Push Notifications) to your Windows Phone 8.1 application, you may encouter the same problem as I did recently.

Microsoft provides nice tutorial, however they do not tell you everything. First pain comes in the block of code labeled Step 2. These two lines of code seem quite nice until you run the code. The call of CreatePushNotificationChannelForApplicationAsync() will probably result in "The application does not have the cloud notification capability." error  (HRESULT value 0x803E0110). Now this is where Microsoft's tutorial (as well as Google) leaves you with no help. Neither the PushNotificationChannel class nor CreatePushNotificationChannelForApplicationAsync function documentation mentions what to do.

Perhaps the first thing that will come to your mind is setting this capability in the application manifest (WMAppManifest.xml). But whoops - there is no such capability listed! The only one close to this is ID_CAP_PUSH_NOTIFICATION. Yeah, but the scenario (and result) is exactly the same.
So, after some time, being quite desperate I tried to search Google for the error code (0x803E0110). Bah, just 5 links - and all look similar, being just lists of error codes. I tried this one which in fact turned to be a good one - it contains a list of HRESULTs with respective codes, messages and also defines. Here I learned that the one I am investigating is WPN_E_CLOUD_INCAPABLE - let's Google this. And voilá - second link lead me to this page (being a copy of MS documentation). Slightly below the half of that page called "Troubleshooting tile, toast, and badge notifications" there is a short paragraph titled "Errors when attempting to create a push notification channel", explaining three common errors that may occur:


The reason is missing Internet capability declaration! Quite different from the actual error message, isn't it?

Okay, so enable it. But there is one more small thing - do not forget to rebuild and redeploy your application for the change to come into play.

Happy coding!

Thursday, January 26, 2012

4. Propagating UI states to PrintTicket

In last article we learned how to design an user interface (UI) for our XPS filter - simple setting page with checkbox, that was supposed to control the Reverse filter usage. Today we look at how to ensure that the checkbox state will be propagated to the PrintTicket (an XML file carrying all the print settings).

This is going to be a little bit messy and demanding, so please work carefully, as even a tiny mistake can result in complete malfunction of the driver.

Ready?

  1. Go to [sample root]\src\ui and open xdsmpldlg.rc for editing (I recommend you to open it in MS Visual Studio). Open its String Table and add following records to it. The IDs and string values are not so much important (they will probably be displayed in the list of printer capabilities), but be careful to ensure each record has its unique numerical Value to prevent conflicts, that can result in driver malfunction. The best practice is to use numbers a bit further behind the last one used. Here I started with value of 2200.
      IDS_GPD_REVERSETITLE   2200   "Revert page order"
      IDS_GPD_NOREVERSAL     2201   "No reversal"
      IDS_GPD_REVERSAL       2202   "Reversal"

  2. Now go to [sample root]\install and open xdnames.gpd for editing. Go to the end of file and add those new strings to the list of StdFeatureNames. You should have something like this:
      IDS_GPD_REVERSETITLE:     RESDLL.xdsmplui.2200
      IDS_GPD_NOREVERSAL:       RESDLL.xdsmplui.2201
      IDS_GPD_REVERSAL:         RESDLL.xdsmplui.2202
    (The RESDLL macro says the driver to look up the xdsmplui.dll for values of those strings)
  3. Create a new file in [sample root]\install and name it xdreverse.gpd. Copy and paste the following code in it. This is a so called "Generic Printer Description" script which defines one of printer's capabilities.

    *% File Name:
    *%
    *%    xdreverse.gpd
    *%
    *% Abstract:
    *%
    *%    Reverse filter feature specific GPD settings.
    *%

    *%***********************************************************
    *%                            Reverse
    *%***********************************************************
    *%  rcNameID      feature name listed in Printer Capabilities
    *%***********************************************************
    *Feature: PageReversal
    {
        *rcNameID: =IDS_GPD_REVERSETITLE
        *DefaultOption: Reversal
        *PrintSchemaKeywordMap: "PageReversal"

        *Option: NoReversal
        {
            *rcNameID: =IDS_GPD_NOREVERSAL
            *PrintSchemaKeywordMap: "NoReversal"
        }

        *Option: Reversal
        {
            *rcNameID: =IDS_GPD_REVERSAL
            *PrintSchemaKeywordMap: "Reversal"
        }
    }

    It says that we define a new printer feature - PageReversal (this name is very important as we will use it in source code), which has two options (we use checkbox, aren't we? So two options are pretty enough). The Reversal option is taken as default (that means our checkbox is checked by default and the Reverse filter is ON). Keep in mind that the feature (so as both options) needs to have a unique keyword provided in PrintSchemaKeywordMap identifier. This keyword will be used in PrintTicket.
    You can save and close this file now. Please make sure that the PageReversal name is used in revctrls.cpp file.
  4. Open [sample root]\install\xdsmpl.inf for edit and perform following tasks:
    • add xdreverse.gpd in [XPSDrvSample] section
    • add xdreverse.gpd = 1 in [SourceDisksFiles] section
  5. Now open [sample root]\src\ui\uiproperties.cpp and add the following on line 198:

      m_OptItemList.push_back("PageReversal");

    This will add our new feature to the driver's internal feature list, which ensures that our checkbox will get it's default value (defined in xdreverse.gpd file) when the user clicks on Reverse setting page in advanced print dialog and will also "communicate" with UniDrv to update PrintTicket when it's state changes.
  6. Save all files and rebuild and reinstall the driver (as we have changed its sources).
  7. Now when you open the print dialog, go to Advanced and open the Reverse page, you should see your checkbox checked (or unchecked in case you changed the default value in GPD file to NoReversal).
  8. Try to print some document. When it's done, change the output xps file's extension to zip and go inside and lookup \Metadata\Job_PT.xml file. This is the PrintTicket associated to your printed document which controlled its printing. Open it for reading (in Notepad, for example). You should be able to find the following near the end of the file:

      <psf:Feature name="psk:PageReversal">
            <psf:Option name="psk:NoReversal"/>
      </psf:Feature>

  9. If so, congratulations! Your PrintTicket knows about your checkbox and its state now. But one thing still remains - we have to edit our Reverse filter sources for it to follow the PrintTicket settings and control its function. See you in next part of this tutorial, which is hopefully coming very soon :)
Should you not find the piece of XML text listed in step 8, be sure to revise all the steps carefully with stress on PageReversal attribute as it is one of the main identifiers, which all the action is controlled by.

HINT:  If you would like to edit your GPD file after installation (e.g. to change your default option of PageReversal feature), you can do it without subsequent driver reinstallation. You just need to know where to find it - the xdreverse.gpd file (together with all the others) resides in c:\windows\system32\spool\drivers\w32x86\3 (in case you use 64 bit Windows go to ...\drivers\x64\3 instead).

Wednesday, January 25, 2012

3. Creating XPS filter settings page

Welcome.
Today's tutorial will guide you through the process of creating filter settings page for the Reverse filter we created in last article. The page will be displayed as a part of the regular system dialog after clicking the "Advanced" button for our sample XPS driver and will be very simple as it will contain just one checkbox (for enabling/disabling the reverse function).
The settings, however, will have no effect yet - the filter will be always active. Connecting the settings to the filter will be discussed in some future article.

Unlike the previous article, today's all work will be done only in [sample root]\src\ui directory.

  1. Open [sample root]\src\ui\xdsmpldlg.rc resource file for edit (in MSVC). 
    1. Right-click the Dialog tree item, choose "Add resource..." and create new dialog. In Properties set this dialog's ID to IDD_REVERSE. Then also make following changes:
      • set 3D look to true
      • set Border to none 
      • set Style to Child
      • set System menu to false
      • set Disabled to true
    2. Now add a checkbox control to the dialog. Call it IDC_CHECK_REVERSE and let its caption be "Revert page order".
    3. Go back to resources tree and double click the String table [English (U.S.)] to open it. Choose "New string" on right click to add new string into table. Call it IDS_REVERSE and let its caption be "Reverse". This string will represent the title of our new settings page.
  2. That's all for visual design. Now let's move to the second part - coding. To follow the style of coding of the other filters, we now have to create 4 new files in current ui directory:
    • revctrls.h
    • revctrls.cpp
    • revppg.h
    • revppg.cpp
    Every settings page must have its own class (inherited from CDocPropPage); what's more - even each control on the setting page must do so (and inherit from default control class (CUICtrlDefaultCheck in our case)). Thus, the first two files (their abbreviated names stand for ReverseControls) will contain the class declarations and definitions for every control used on our page (fortunately we have just one - checkbox), while the other two files (the name stands for ReversePropertyPage) will contain the declarations and definitions for the page class.
    • Every control class must contain: constructor, virtual destructor, private static string. If the control need some initialisation (e.g. setting default value), the class must also contain an overriden OnInit method.
    • Every page class must contain constructor, virtual destructor and overriden method InitDlgBox (which will set the dialog UI template and title).
  3. Open xdsmplui.cpp for edit and add the #include "revppg.h" line to the beginning (right after the line 31 where the ftrppg.h file is included). Go to line 920 inside the CreatePropertyPages function and add the following CReversePropPage class instance creating:

    if (SUCCEEDED(hr))

    {

       pPropPage = new CReversePropPage();

       if (SUCCEEDED(hr = CHECK_POINTER(pPropPage, E_OUTOFMEMORY)))
       {
           m_vectPropPages.push_back(pPropPage);
       }
    }


    This code snippet creates a new instance of our setting page and adds it in the pages vector if successful. When user opens the "Advanced" dialog of our XPS printer (in Windows Control Panel), the system calls DocumentPropertySheets function where the content of this vector is processed (PropPageInit function is then called on every page which invokes the provided InitDlgBox function of each page).
  4. We are almost done here. Now open the sources file and add revctrls.cpp and revppg.cpp to SOURCES variable for these files to get compiled.
  5. Run x86 build environment (for Vista or later), type build -cZ, hit enter.
  6. Reinstall the driver of our XPS sample printer.
  7. Open any document and call print dialog. After selecting our sample XPS printer, click "Advanced" button and see the new tab with your brand new settings page :)
Sourcesrevctrls.hrevctrls.cpp, revppg.h, revppg.cpp

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:)

1. Building and using XPSDrvSmpl under Windows XP

Microsoft Windows Driver Developer Kit (DDK/WDK) comes with a sample of XPS print driver. This post will tell you what to do to build it and use it under WinXP.

(XPS print path isn't supported in standard Windows XP, you need to have either SP3 with .Net3.0, MS XPS Essentials Pack or this MS hotfix installed in order to use it. We will assume that you have this done)

Having WDK downloaded (for free) and installed, you should find this sample at [WDK root]\[WDK version]\src\print\XPSDrvSmpl folder (e.g. c:\WinDDK\7600.16385.1\src\print\XPSDrvSmpl). Further you should see some records in your Start menu under Programs/Windows Driver Kits. This folder and its content is important, as it provides building environments for various MS operating systems (XPSDrvSmpl must be built using at least Vista build environment, as it is not supported in XP and trying to build the sample using XP build environment will result in lot of errors and no output libraries).

So let's start:
  1. Go to Start - Programs - Windows Driver Kits - WDK [version number] - Build environments and choose Windows Vistaand Windows Server 2008 - x86 checked environment (the checked environment should provide more information at output when errors occur; it's something like Debug/Release configuration in MSVC). A console should start.
  2. If you have copied the XPS sample to a custom directory, use cd command to go there (e.g. cd c:\projects\xpsdrvsmpl). From now on I will call this directory a sample root.
  3. Type build -cZ and hit Enter. After few moments the build should end successfully (without any error and warning) telling you the count of files compiled and libraries and executables built. Now your new dll output files are located in [sample root]\install\x86 directory.
  4. We can now proceed to new XPS printer installation.
    Go to Start - Control Panel - Add new printer. Then choose Local printer and Create new port. Select Local Port in combo box. The dialog asking for a port name will appear. For our XPS driver to print to specified file without asking a user for a location, write in full path for this output file. This file need not to exist at the moment (something like c:\mydriveroutput.xps for example).
  5. Now when you see the list of vendors and devices, click the "From disk" button. Then select [sample root]\install\xdsmpl.inf and click Next. This will load the printer installing instructions.
  6. As you go on, the wizard can ask you for some files it cannot find. But this should be no big trouble if you have SP3 or XPS Extension Pack installed.
    If the wizard asks you for xdsmpl.gpd, click Browse and find this file in [sample root]\install\x86.
    If it asks for xdwmark.dll, navigate to [sample root]\install\x86 as well.
    For msxpsinc.gpd go to c:\windows\Driver cache\i386.
    The xpssvcs.dll file should be found in c:\windows\system32.
  7. And that's it. The printer driver should now be installed and ready to use. Now go to any of your favourite application and under File - Print menu choose XPSDrv Sample Driver. The output XPS file is the one you provided aas the port name at the beginning of printer installation (e.g. c:\mydriveroutput.xps)