大部分掃描儀都支持TWAIN協議,爲了方便開發掃描儀應用程序,TWAIN組織在GitHub上放了虛擬掃描儀的示例代碼。這份代碼只支持加載自帶的TWAIN圖標,ADF連續掃描也不起作用。爲了更真實的模擬掃描不同的圖片,我們可以對源碼做些修改。
文檔掃描流程
修改代碼之前,我們可以先了解下掃描文檔的流程。虛擬掃描儀就是在source這層。
開發測試環境
開發
- Visual Studio 2017及以上(官方提供了比較早的工程)
- Qt 5.12.11 msvc2017或者Qt 5.12.11 msvc2017_64。這裏取決於你要編譯32位還是64位的程序
測試
- 在線測試程序:Dynamic Web TWAIN online demo
- 本地測試程序:twacker
編譯及調試
編譯工程
-
獲取源碼:
git clone https://github.com/twain/twain-samples.git
-
設置系統變量
QTDIR
: -
在系統
PATH
中添加:C:\Qt\5.12.11\msvc2017_64\bin
。注意,如果你還添加了arm
或者mingw
,一定要保證msvc
在最前面,不然通過windeployqt
部署的時候,拿到的DLL
是錯誤的。 -
用
管理員權限
啓動Visual Studio。導入工程之後編譯。編譯好會生成TWAINDS_Sample32.ds
或者TWAINDS_Sample64.ds
動態鏈接庫,對應的輸出路徑分別是C:\Windows\twain_32\sample2\
或者C:\Windows\twain_64\sample2\
。
現在用測試工具打開,應該可以看到掃描儀。點擊掃描儀可以加載默認的圖片。
調試程序
-
運行twacker。
-
在Visual Studio中掛載進程:
-
掛載之後,執行掃描就會跳到斷點。
通過調試程序,我們可以瞭解源碼的工作流程。接下來修改代碼。
虛擬掃描儀加載自定義圖片
通過調試發現,每次點擊掃描,DLL都會被重新加載,所有的變量都會重置。要在內存中保留圖片索引不行。解決方法是寫一個配置文件。這裏有另外一個問題就是windows目錄下默認沒有寫權限,只有讀權限。所以創建兩個文件source.json
,info.json
,一個放在windows目錄下指定自定義圖片的目錄,一個放在圖片目錄中包含圖片索引信息和一次最大掃描量(用於ADF)。
source.json:
{
"folder": "C:/Users/admin/Pictures/barcode"
}
info.json
{
"index": 0,
"maxcount": 10
}
在CScanner_FreeImage.cpp
的resetScanner()
函數中,我們讀取自定義的配置文件。通過索引設置一張當前圖片,然後更新索引,寫回到配置文件中,用於下一次的掃描:
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());
}
現在可以重新編譯這個虛擬掃描儀,然後通過在線程序測試:
修改之前
修改之後