How to Build Windows Virtual Scanner and Feed Custom Images
Tags: twain, document, scanner, scanning
Published: 2021-11-16 06:56:51

When building a TWAIN-compatible application and there is no physical scanner available, you can use a virtual scanner application for development and testing. TWAIN working group has released a [TWAIN DS sample](https://github.com/twain/twain-samples) on GitHub. In this article, I will detail how to set up the development environment, as well as how to substitute the default TWAIN logo with custom images.

## Document Acquisition Process
Before getting started, let's take a glimpse of the [workflow](https://twain.org/wp-content/uploads/2016/03/TWAIN-2.2-Spec.pdf) of the document acquisition process.

![TWAIN data source](https://www.dynamsoft.com/codepool/img/2021/11/twain-data-source.jpg)

We are going to do something in the source layer.

## Pre-requisites
- Visual Studio 2017 or above
- Qt 5.12.11 msvc2017
- Test tools:
    - [Dynamic Web TWAIN online demo](https://demo.dynamsoft.com/web-twain/)
    - [twacker](https://github.com/twain/twain-toolkit/tree/master/releases)
  
## How to Build and Debug the TWAIN DS Sample

**Build**
1. Get the source code:
    
    ```bash
    git clone https://github.com/twain/twain-samples.git
    ```

2. Install Qt and add system variable `QTDIR`: msvc2017 (x86) or msvc2017_64 (x64).

    ![Qt system variable](https://www.dynamsoft.com/codepool/img/2021/11/qt-system-variable.jpg)

3. If you have installed multiple Qt kits, such as MinGW, ARM64, MSVC, you have to move MSVC ahead of others in `PATH` to avoid deploying incompatible DLL files with `windeployqt.exe`  

4. Start Visual Studio as administrator because the output directory is `C:\Windows\twain_32\sample2\`. Open the project in Visual Studio and update linking options according to your OS environment.
5. Build the project to generate a `TWAINDS_Sample32.ds` file, which is a dynamic library.

**Debug**
1. Run twacker (32-bit/64-bit) application.
2. In Visual Studio, set a breakpoint in `DS_Entry()` and then go to `Debug > Attach to Process` to find the twacker process.
    
    ![attach TWAIN DS to twacker](https://www.dynamsoft.com/codepool/img/2021/11/debug-twain-ds.jpg)

3. Click the `Attach` button and then select the virtual scanner to trigger debugging.  
    
    ![Debug TWAIN virtual scanner](https://www.dynamsoft.com/codepool/img/2021/11/visual-studio-debug-twain-ds.jpg)


## Feeding Windows Virtual Scanner with Custom Images
I am tired of using the default TWAIN logo as the scanned image, so I am going to modify the source code of the TWAIN DS sample to load custom images.

The `DS_Entry()` function is the entry point of the TWAIN data source in `CTWAINDS_Sample1.cpp`. We can set a breakpoint in this function to see the stack calls.

After debugging the code, I found an appropriate place for the custom image loading code is in the constructor of `CScanner_FreeImage` class.

When triggering the acquire image event, the `TWAINDS_Sample32.ds` file will be reloaded to the memory, which means all variables will be reset. Therefore, we use a JSON file to read and write the current image index in order to load different images by clicking the `Acquire Image` button.

Note: the `C:\Windows\twain_32\sample2\` directory is not writable without administrator privileges. Thus, we create a read-only file `source.json` that contains the image set folder and put an `info.json` file in the image set folder to store the image index. For example:

**source.json**

```json
{
    "folder": "C:/Users/admin/Pictures/barcode"
}
```

**info.json**

```json
{
    "index": 0
}
```

The code is as follows:

```cpp
#include "json.hpp"
using json = nlohmann::json;

#include <filesystem>
#include <fstream>
namespace fs = std::experimental::filesystem;

CScanner_FreeImage::CScanner_FreeImage()
{
    ...

    char sourceConfig[PATH_MAX];
    SSNPRINTF(sourceConfig, sizeof(sourceConfig), PATH_MAX, "%s%csource.json", szTWAIN_DS_DIR, PATH_SEPERATOR);
    vector<string> images;

    if (FILE_EXISTS(sourceConfig))
    {
        // Read the image folder from source.json
        ifstream stream(sourceConfig);
        json  source;
        stream >> source;
        stream.close();

        string imageFolder = source["folder"];

        if (FILE_EXISTS(imageFolder.c_str()))
        { 
            // Get the image index
            string infoPath = imageFolder + PATH_SEPERATOR + "info.json";
            ifstream infoStream(infoPath);
            json info;
            infoStream >> info;
            infoStream.close();

            int index = info["index"];

            for (const auto& entry : fs::directory_iterator(imageFolder))
            {
                std::string path{ entry.path().u8string() };
                string suffix = path.substr(path.length() - 4, 4);
                // Get JPEG or PNG files
                if (!suffix.compare(".jpg") || !suffix.compare(".png"))
                {
                    images.push_back(path);
                }
            }

            if (images.size() > 0)
            {
                if (index >= images.size()) index = 0;

                // Set a custom image
                SSNPRINTF(m_szSourceImagePath, sizeof(m_szSourceImagePath), PATH_MAX, images[index].c_str());

                // Save image index to info.json
                index += 1;
                info["index"] = index;
                std::ofstream stream(infoPath);
                stream << info << std::endl;
                stream.close();
            }
        }
    }

    // If there's no config file for custom image set, use the default image
    if (images.size() == 0)
    {
        SSNPRINTF(m_szSourceImagePath, sizeof(m_szSourceImagePath), PATH_MAX, "%s%cTWAIN_logo.png", szTWAIN_DS_DIR, PATH_SEPERATOR);
    }
    ...
}

```

You can download `json.hpp` from https://github.com/nlohmann/json.

Now we can re-build the project and test it with [Dynamic Web TWAIN online demo](https://demo.dynamsoft.com/web-twain/).


**Before**

![scan images from default virtual scanner](https://www.dynamsoft.com/codepool/img/2021/11/scan-images-from-virtual-scanner-default.jpg)

**After**

![scan images from modified virtual scanner](https://www.dynamsoft.com/codepool/img/2021/11/scan-images-from-virtual-scanner.jpg)

## Source Code

[https://github.com/yushulx/windows-virtual-scanner](https://github.com/yushulx/windows-virtual-scanner)