I'm trying to write an installer for a Windows print driver and I keep getting error 2 or 87 from GetLastError()

95 Views Asked by At

I called pnputil /a my.inf via CreateProcess() and that was successful. I gave up on trying to use the Windows API. I am then attempting to use AddPrinterDriver() to add the printer, but it keeps failing:

if (RunWindowsPnpUtil(thePrinterInfo))
{
    //std::vector<std::wstring> vecInstallPaths = GetInstalledDriverPaths();
    //std::vector<std::wstring> vecInstanceIDs = GetDeviceInstanceIDsUsingDriver(thePrinterInfo);
    wstring wsDriverName = thePrinterInfo.GetPrinterName(), wsInfPath = thePrinterInfo.GetINFPath();
    wstring wsDir = wsInfPath.substr(0, wsInfPath.find_last_of(L'\\')), wsINFName = wsInfPath.substr(wsInfPath.find_last_of(L'\\') + 1);
    DRIVER_INFO_4 driverInfo = { 0 };
    memset(&driverInfo, 0, sizeof(DRIVER_INFO_3));
    wstring wsConfigFile = thePrinterInfo.GetConfigFile(), wsDataFile = thePrinterInfo.GetDataFile();
    driverInfo.cVersion = thePrinterInfo.GetPrinterClassVersionInt(); // Specify the version of the structure
    driverInfo.pName = const_cast<wchar_t*>(wsDriverName.c_str()); // Driver name
    driverInfo.pEnvironment = NULL; // Use the current environment
    driverInfo.pDriverPath = const_cast<wchar_t*>(L"UNIDRV.DLL"); // The path to the driver files (optional)
    driverInfo.pDataFile = const_cast<wchar_t*>(thePrinterInfo.GetGPDFile().c_str()); // NULL; // const_cast<wchar_t*>(wsInfPath.c_str()); // Path to the INF file
    driverInfo.pConfigFile = const_cast<wchar_t*>(wsConfigFile.empty() ? L"UNIDRVUI.DLL" : wsConfigFile.c_str());; // The path to the configuration file (optional)
    driverInfo.pHelpFile = const_cast<wchar_t*>(L"UNIDRV.HLP"); // The path to the help file (optional)
    driverInfo.pDependentFiles = NULL; // Array of dependent files (optional)
    driverInfo.pMonitorName = NULL; // Monitor name (optional)
    driverInfo.pDefaultDataType = NULL; // Default data type (optional)

    // Add the printer driver
    //DWORD level = driverInfo.cVersion;
    if (AddPrinterDriver(NULL, driverInfo.cVersion, reinterpret_cast<BYTE*>(&driverInfo)) == 0) {
        // Failed to add printer driver
        DWORD dwErr = GetLastError();
        return false;
    }

For context, this is an XPS mini driver (filter.dll).

If you need more information, I'll be happy to provide it. I'm desperate, Google, Reddit, and ChatGPT, are no help.

AddPrinterDriver returns FAILURE and dwErr == 2 or dwErr == 87, depending on whether driverInfo.pDataFile is NULL or points to a valid filename. Ultimately, my goal is to parse the inf, copy the files whether they need to go, and Add the Printer. I have some old Pascal code I'm using as a guide. I used ChatGPT to convert it to C++ and then I married it to my actualy code. It's literally just the AddPrinterDriver that is failing. Everything is working as expected

If someone knows of a better way to read an INF, prase it, copy the relevant files to the Driver Store, add the printer driver, and finally add the printer, let me know. MSDN used to be wonderful, but like everything else, it's not wonderful. And ChatGPT is literally spitting out nonsense that isn't real Windows API.

UPDATE:

HRESULT hResult = UploadPrinterDriverPackage(NULL, wsInfPath.c_str(), wsEnv.c_str(), UPDP_CHECK_DRIVERSTORE, NULL/*GetMainWnd()*/, wszDestInfPath, &cchDestInfPath);
if (hResult == S_OK)
{
    hResult = InstallPrinterDriverFromPackage(NULL, wszDestInfPath, wsDriverName.c_str(), wsEnv.c_str(), 0);
    if (hResult == S_OK)
    {

IS SUCCESSFUL!

However, a subsequent call to AddPrinterDriver is still failing FILE_NOT_FOUND (2)

basically, instead of if (RunWindowsPnpUtil(thePrinterInfo)), I am calling UploadPrinterDriverPackage and InstallPrinterDriverFromPackage. And that worked. Of course, the pnputil /a worked too, but this way return a destInfPath. Oh, help! I feel like I am so close, yet so far away still.

1

There are 1 best solutions below

1
Remy Lebeau On

You said in a comment:

AddPrinterDriver returns FAILURE and dwErr == 2 or dwErr == 87, depending on whether driverInfo.pDataFile is NULL or points to a valid filename.

One problem I see is that on this line:

driverInfo.pDataFile = const_cast<wchar_t*>(thePrinterInfo.GetGPDFile().c_str());

If GetGPDFile() returns a wstring by value rather than by reference, this will produce a dangling pointer causing Undefined Behavior. You should save the result of GetGPDFile() to a local variable first, like you do with your other strings, eg:

wstring wsGPDFile = thePrinterInfo.GetGPDFile();
driverInfo.pDataFile = const_cast<wchar_t*>(wsGPDFile.c_str());

On a side note, if you are compiling for C++17 or later:

  • std::wstring has a non-const data() method that you can use instead of const_cast, eg:

    driverInfo.pDataFile = wsGPDFile.data();
    
  • consider using std::filesystem::path instead of parsing file paths manually, eg:

    #include <filesystem>
    namespace fs = std::filesystem;
    
    fs::path fsInfPath = thePrinterInfo.GetINFPath();
    wstring wsDir = fsInfPath.parent_path();
    wstring wsINFName = fsInfPath.filename();