大部分扫描仪都支持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 (http://demo.dynamsoft.com/web-twain/)
- 本地测试程序:twacker (http://github.com/twain/twain-toolkit/tree/master/releases)
获取源码:
git clone http://github.com/twain/twain-samples.git
设置系统变量QTDIR:
在系统PATH中添加:C:Qt5.12.11msvc2017_64in。注意,如果你还添加了arm或者mingw,一定要保证msvc在最前面,不然通过windeployqt部署的时候,拿到的DLL是错误的。
用管理员权限启动Visual Studio。导入工程之后编译。编译好会生成TWAINDS_Sample32.ds或者TWAINDS_Sample64.ds动态链接库,对应的输出路径分别是C:Windows wain_32sample2或者C:Windows wain_64sample2。
现在用测试工具打开,应该可以看到扫描仪。点击扫描仪可以加载默认的图片。
调试程序运行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());
}
现在可以重新编译这个虚拟扫描仪,然后通过在线程序测试:
修改之前
修改之后
源码http://github.com/yushulx/windows-virtual-scanner
,