实现32/64位Windows虚拟扫描仪自定义图片加载

大部分扫描仪都支持TWAIN协议,为了方便开发扫描仪应用程序,TWAIN组织在GitHub上放了虚拟扫描仪的示例代码。这份代码只支持加载自带的TWAIN图标,ADF连续扫描也不起作用。为了更真实的模拟扫描不同的图片,我们可以对源码做些修改。

文档扫描流程

修改代码之前,我们可以先了解下扫描文档的流程。虚拟扫描仪就是在source这层。

在这里插入图片描述

开发测试环境

开发

  • Visual Studio 2017及以上(官方提供了比较早的工程)
  • Qt 5.12.11 msvc2017或者Qt 5.12.11 msvc2017_64。这里取决于你要编译32位还是64位的程序

测试

编译及调试

编译工程

  1. 获取源码:

    	 git clone https://github.com/twain/twain-samples.git
    
  2. 设置系统变量QTDIR

    在这里插入图片描述

  3. 在系统PATH中添加:C:\Qt\5.12.11\msvc2017_64\bin。注意,如果你还添加了arm或者mingw,一定要保证msvc在最前面,不然通过windeployqt部署的时候,拿到的DLL是错误的。

  4. 管理员权限启动Visual Studio。导入工程之后编译。编译好会生成TWAINDS_Sample32.ds或者TWAINDS_Sample64.ds动态链接库,对应的输出路径分别是C:\Windows\twain_32\sample2\或者C:\Windows\twain_64\sample2\

现在用测试工具打开,应该可以看到扫描仪。点击扫描仪可以加载默认的图片。

调试程序

  1. 运行twacker。

  2. 在Visual Studio中挂载进程:

    在这里插入图片描述

  3. 挂载之后,执行扫描就会跳到断点。 在这里插入图片描述

通过调试程序,我们可以了解源码的工作流程。接下来修改代码。

虚拟扫描仪加载自定义图片

通过调试发现,每次点击扫描,DLL都会被重新加载,所有的变量都会重置。要在内存中保留图片索引不行。解决方法是写一个配置文件。这里有另外一个问题就是windows目录下默认没有写权限,只有读权限。所以创建两个文件source.jsoninfo.json,一个放在windows目录下指定自定义图片的目录,一个放在图片目录中包含图片索引信息和一次最大扫描量(用于ADF)。

source.json:

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

info.json

{
    "index": 0,
    "maxcount": 10
}

CScanner_FreeImage.cppresetScanner()函数中,我们读取自定义的配置文件。通过索引设置一张当前图片,然后更新索引,写回到配置文件中,用于下一次的扫描:

  char szTWAIN_DS_DIR[PATH_MAX];
  GetModuleFileName(g_hinstance, szTWAIN_DS_DIR, PATH_MAX);
  // strip filename from path
  size_t x = strlen(szTWAIN_DS_DIR);
  while (x > 0)
  {
      if (PATH_SEPERATOR == szTWAIN_DS_DIR[x - 1])
      {
          szTWAIN_DS_DIR[x - 1] = 0;
          break;
      }
      --x;
  }
  char sourceConfig[PATH_MAX];
  SSNPRINTF(sourceConfig, sizeof(sourceConfig), PATH_MAX, "%s%csource.json", szTWAIN_DS_DIR, PATH_SEPERATOR);

  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 and max image count for ADF
          string infoPath = imageFolder + PATH_SEPERATOR + "info.json";
          ifstream infoStream(infoPath);
          json info;
          infoStream >> info;
          infoStream.close();

          int index = info["index"];
          m_nDocCount = m_nMaxDocCount = info["maxcount"];

          WIN32_FIND_DATAA data;
          HANDLE handle = FindFirstFileA((imageFolder + "\\*").c_str(), &data);

          if (handle == INVALID_HANDLE_VALUE)
              throw std::runtime_error("Invalid handle value! Please check your path...");

          while (FindNextFileA(handle, &data) != 0)
          {
              string filename = std::string(data.cFileName);
              string path = imageFolder + PATH_SEPERATOR + filename;
              string suffix = path.substr(path.length() - 4, 4);
              if (!suffix.compare(".jpg") || !suffix.compare(".png"))
              {
                  images.push_back(path);
              }
          }

          FindClose(handle);

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

              // Set a custom image
              memset(m_szSourceImagePath, 0, PATH_MAX);
              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();
          }
      }
  }

现在如果一张张扫描,已经可以正常工作了,不过ADF模式还不起作用。我们在acquireImage()函数中,针对ADF加入下面的代码:

if (images.size() > 0 && m_nPaperSource == SFI_PAPERSOURCE_ADF)
  {
      memset(m_szSourceImagePath, 0, PATH_MAX);
      SSNPRINTF(m_szSourceImagePath, sizeof(m_szSourceImagePath), PATH_MAX, images[m_nDocCount - 1].c_str());
  }

现在可以重新编译这个虚拟扫描仪,然后通过在线程序测试:

修改之前

在这里插入图片描述

修改之后

在这里插入图片描述

源码

https://github.com/yushulx/windows-virtual-scanner

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章