實現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

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