code_device/twain/ds/scanner.cpp

1566 lines
42 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "scanner.h"
#include <sane/sane_option_definitions.h>
#include "sane_option_trans.h"
#include <chrono>
#include <mutex>
#include <string.h>
#include <functional>
#include <twain_user/twainui.h>
#include <huagao/brand.h>
#include "sane_helper.h"
#include <base/utils.h>
#define START_SCAN_IN_THREAD
#pragma comment(lib, "user32.lib")
#if !defined(WIN32) && !defined(_WIN64)
template<typename ... Args>
int sprintf_s(char* buf, size_t len, const char* fmt, Args ... args)
{
return sprintf(buf, fmt, args ...);
}
std::string g_scanner_path = ""; // hg_log::get_module_full_path((std::string(GET_BACKEND_NAME) + ".so").c_str());
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// callback
sane_helper* sane_helper_ = nullptr;
namespace callback
{
static std::mutex cb_lock_;
typedef struct _scanner_inst
{
SANE_Handle dev;
scanner* invoker;
bool operator==(const SANE_Handle& h)
{
return dev == h;
}
bool operator==(const scanner* obj)
{
return invoker == obj;
}
}SCNINST;
std::vector<SCNINST> g_scanner_instances;
int sane_event_callback( // 注册回调的对象,需要保证该回调是多线程安全的
SANE_Handle hdev // 产生事件的设备句柄
, int code // 回调事件代码
, void* data // 回调事件数据,根据事件代码有所不同,参照具体事件定义
, unsigned int* len // 数据长度字节或者event_data的缓冲区长度详细请看相应的事件代码
, void* param // 用户自定义数据与调用sane_init_ex传入时的保持一致
) // 返回值依不同的事件代码而定通常为“0”
{
std::lock_guard<std::mutex> lock(cb_lock_);
std::vector<SCNINST>::iterator it = std::find(g_scanner_instances.begin(), g_scanner_instances.end(), hdev);
if (it != g_scanner_instances.end())
return it->invoker->handle_device_event(code, data, len);
else if(code != SANE_EVENT_SCANNER_CLOSED)
{
char msg[218] = { 0 };
sprintf_s(msg, _countof(msg) - 1, "Lost device(0x%08X) when event(%u) occurs!\r\n", hdev, code);
utils::log_info(msg, (log_level)1);
return 0;
}
}
void reg_callback(SANE_Handle dev, scanner* invoker)
{
std::lock_guard<std::mutex> lock(cb_lock_);
std::vector<SCNINST>::iterator it = std::find(g_scanner_instances.begin(), g_scanner_instances.end(), dev);
if (it == g_scanner_instances.end())
{
SCNINST inst;
inst.dev = dev;
inst.invoker = invoker;
g_scanner_instances.push_back(inst);
}
else
it->invoker = invoker;
}
void unreg_callback(scanner* invoker)
{
std::lock_guard<std::mutex> lock(cb_lock_);
std::vector<SCNINST>::iterator it = std::find(g_scanner_instances.begin(), g_scanner_instances.end(), invoker);
if (it != g_scanner_instances.end())
g_scanner_instances.erase(it);
}
static HWND find_main_wnd(void)
{
return nullptr;
}
static std::string option_value_2_string(SANE_Value_Type type, void* value)
{
if(type == SANE_TYPE_BOOL)
return *(SANE_Bool*)value == SANE_TRUE ? "true" : "false";
if(type == SANE_TYPE_INT)
return std::to_string(*(SANE_Int*)value);
if(type == SANE_TYPE_FIXED)
return std::to_string(SANE_UNFIX(*(SANE_Int*)value));
if(type == SANE_TYPE_STRING)
return (char*)value;
return "Unknown";
}
// UI ...
//
// events code, see SANE_Event
//
// callback events: SANE_EVENT_UI_CLOSE_CANCEL/SANE_EVENT_UI_CLOSE_NORMAL/SANE_EVENT_UI_SCAN_COMMAND/SANE_EVENT_UI_CLOSE_SETTING
//
// notify events: SANE_EVENT_WORKING - void*: unused, be nullptr, flag - unused, be 0
// SANE_EVENT_SCAN_FINISHED - void*: (utf8*)message, flag - error code (0 is success)
// SANE_EVENT_USB_DATA_RECEIVED- void* unused, be nullptr, flag - unused, be 0
// SANE_EVENT_IMAGE_OK - void* unused, be nullptr, flag - unused, be 0
static HMODULE hui = nullptr;
int (*choose_scanner)(const std::vector<DEVQUEUI>& devs) = nullptr; // blocked. return selected DEVQUE::id or -1 if user cancelled
char* (*apply_current_config)(const char* dev_name, SANE_Handle device, LPSANEAPI api) = nullptr; // 应用设备的当前配<E5898D>?
int (*show_setting_ui)(SANE_Handle device, HWND parent, LPSANEAPI api, const char* devname, bool with_scan, std::function<void(ui_result)> callback) = nullptr;
int (*show_progress_ui)(HWND parent, std::function<void(ui_result)> callback, std::function<void(int/*event*/, void* /*msg*/, int/*flag*/)>* notify) = nullptr;
int (*show_messagebox_ui)(HWND parent, int event, void* msg, int flag) = nullptr;
int (*close_ui)(int) = nullptr;
void (*twain_ui_free)(void* buf) = nullptr;
static void init_ui(void)
{
std::string root(utils::get_module_full_path(MODULE_NAME_SCANNER));
size_t pos = root.rfind(PATH_SEPARATOR[0]);
if(pos != std::string::npos)
root.erase(pos);
root = utils::find_file(root.c_str(), "*twainui*", false);
utils::to_log(LOG_LEVEL_DEBUG, "TwainUI component: %s\n", root.c_str());
// #if defined(WIN32) || defined(_WIN64)
// root += OEM_SHORT_NAME_E;
// #else
// std::string vnd(OEM_SHORT_NAME_E);
//
// std::transform(vnd.begin(), vnd.end(), vnd.begin(), toupper);
// root += "lib" + vnd;
// #endif
// root += "TwainUI.";
// root += DLL_EXTESION;
if (hui)
FreeLibrary(hui);
// hui = LoadLibraryExA(root.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
hui = utils::load_dll(root.c_str(), LOAD_WITH_ALTERED_SEARCH_PATH);
if (!hui)
{
std::string info("Load '" + root);
info += "' failed: " + std::string(strerror(GetLastError())) + "\r\n";
utils::log_info(info.c_str(), LOG_LEVEL_DEBUG);
}
else
{
#define GET_API(api) \
proc = (FARPROC*)&api; \
*proc = GetProcAddress(hui, #api); \
if(*proc == nullptr) \
utils::to_log(LOG_LEVEL_FATAL, "TWAINUI - function '%s' not found!\n", #api);
FARPROC* proc = nullptr;
GET_API(choose_scanner);
GET_API(apply_current_config);
GET_API(show_setting_ui);
GET_API(show_progress_ui);
GET_API(show_messagebox_ui);
GET_API(close_ui);
GET_API(twain_ui_free);
}
}
static void unint_ui(void)
{
if (close_ui)
close_ui(UI_UNLOAD_MODULE);
choose_scanner = nullptr;
apply_current_config = nullptr;
show_setting_ui = nullptr;
show_progress_ui = nullptr;
show_messagebox_ui = nullptr;
close_ui = nullptr;
twain_ui_free = nullptr;
if (hui)
{
FreeLibrary(hui);
hui = nullptr;
}
}
static SANE_Bool get_invisible_fix_id(int id, void* param)
{
((std::vector<int>*)param)->push_back(id);
return SANE_TRUE;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class scanner
scanner::scanner(const char* model) : handle_(nullptr), model_(model), ex_id_(EXTENSION_ID_BASE), prev_start_result_(SCANNER_ERR_NOT_START)
, dpi_(200), tmp_path_(""), img_ind_(0), err_(SCANNER_ERR_NOT_OPEN)
, scanner_name_(""), is_ui_wait_img_(false), is_scanning_(false)
, scanner_ev_handler_(nullptr), evh_param_(nullptr), app_wnd_(nullptr), user_cancel_(false)
, max_img_mem_(1 * 1024), twain_set_(false), ev_cnt_(0), is_bIndicator(false), is_show_setting_(false)
{
ui_notify = std::function<void(int, void*, int)>();
sane_api_.sane_cancel_api = sane_helper_->invoke_sane_cancel;
sane_api_.sane_close_api = sane_helper_->invoke_sane_close;
sane_api_.sane_control_option_api = sane_helper_->invoke_sane_control_option;
sane_api_.sane_get_devices_api = sane_helper_->invoke_sane_get_devices;
sane_api_.sane_get_option_descriptor_api = sane_helper_->invoke_sane_get_option_descriptor;
sane_api_.sane_get_parameters_api = sane_helper_->invoke_sane_get_parameters;
sane_api_.sane_get_select_fd_api = sane_helper_->invoke_sane_get_select_fd;
sane_api_.sane_io_control_api = nullptr;
sane_api_.sane_open_api = sane_helper_->invoke_sane_open;
sane_api_.sane_read_api = sane_helper_->invoke_sane_read;
sane_api_.sane_set_io_mode_api = sane_helper_->invoke_sane_set_io_mode;
sane_api_.sane_start_api = sane_helper_->invoke_sane_start;
sane_api_.sane_strstatus_api = sane_helper_->invoke_sane_strstatus;
tmp_path_ = utils::get_local_data_path() + PATH_SEPARATOR;
cfg_path_ = tmp_path_ + "config";
utils::create_folder(cfg_path_.c_str());
cfg_path_ += PATH_SEPARATOR;
tmp_path_ += "imgs";
utils::create_folder(tmp_path_.c_str());
tmp_path_ += PATH_SEPARATOR;
img_fmt_.img_format = SANE_IMAGE_TYPE_BMP;
img_fmt_.compress.compression = SANE_COMPRESSION_NONE;
int mem_limit = GetPrivateProfileIntA("mem", "max_img", 0, (cfg_path_ + "debug.cfg").c_str());
if (mem_limit > 0)
{
// this value is same as driver memory limit ...
max_img_mem_ = mem_limit;
}
}
scanner::~scanner()
{
close();
if (thread_starting_.get() && thread_starting_->joinable())
thread_starting_->join();
thread_starting_.reset();
if (thread_stop_.get() && thread_stop_->joinable())
thread_stop_->join();
thread_stop_.reset();
}
void scanner::get_scanner_name(const char* model, std::vector<std::string>& names)
{
const SANE_Device **devs = nullptr;
int count = 0;
names.clear();
if (sane_helper_->invoke_sane_get_devices(&devs, SANE_TRUE) == SANE_STATUS_GOOD)
{
for(; devs[count]; ++count)
{
if (strcmp(devs[count]->model, model) == 0)
names.push_back(devs[count]->name);
}
}
}
value_type scanner::from_sane_type(SANE_Value_Type type)
{
if (type == SANE_TYPE_BOOL)
return VAL_TYPE_BOOL;
else if (type == SANE_TYPE_INT)
return VAL_TYPE_INT;
else if (type == SANE_TYPE_FIXED)
return VAL_TYPE_FLOAT;
else if (type == SANE_TYPE_STRING)
return VAL_TYPE_STR;
else
return VAL_TYPE_NONE;
}
value_limit scanner::from_sane_constraint(SANE_Constraint_Type type)
{
if (type == SANE_CONSTRAINT_RANGE)
return VAL_LIMIT_RANGE;
else if (type == SANE_CONSTRAINT_STRING_LIST || type == SANE_CONSTRAINT_WORD_LIST)
return VAL_LIMIT_ENUM;
else
return VAL_LIMIT_NONE;
}
int scanner::to_int(SANE_Int v)
{
return v;
}
double scanner::to_double(SANE_Fixed v)
{
return (double)SANE_UNFIX(v);
}
void scanner::ui_callback(int uev, void* sender, void* param)
{
((scanner*)param)->on_ui_event(uev, sender);
}
bool scanner::is_option_float(int sn, void* param)
{
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor((SANE_Handle)param, sn);
if (desc)
return desc->type == SANE_TYPE_FIXED;
else
return false;
}
void scanner::scan_done(void)
{
std::string msg(scan_msg_);
while (images_.count())
std::this_thread::sleep_for(std::chrono::milliseconds(5));
if (ui_notify)
ui_notify(SANE_EVENT_SCAN_FINISHED, &msg[0], err_);
else
is_scanning_ = false;
//else
//{
// if (err_)
// {
// if (callback::show_messagebox_ui)
// {
// callback::show_messagebox_ui(app_wnd_, SANE_EVENT_SCAN_FINISHED, (void*)&msg[0], 0);
// }
// else // windows message box ...
// {
// std::wstring text(local_trans::a2u(msg.c_str(), CP_UTF8));
// if (!IsWindow(app_wnd_))
// callback::bring_message_box_topmost(local_trans::lang_trans_between_hz936(CONST_STRING_ERROR).c_str());
// MessageBoxW(app_wnd_, text.c_str(), local_trans::lang_trans_between_hz936(CONST_STRING_ERROR).c_str(), MB_OK);
// }
// }
// on_ui_event(SANE_EVENT_SCAN_FINISHED, (void*)SANE_EVENT_SCAN_FINISHED);
//}
// is_scanning_ = false;
}
int scanner::transfer_id(int id)
{
if (id > SANE_OPT_ID_BASE)
{
if (sane_opts_.count((sane_option_id)id) == 0)
id = -1;
else
id = sane_opts_[(sane_option_id)id].opt_sn;
}
return id;
}
void scanner::apply_config(void)
{
if (callback::apply_current_config)
{
char* cfg = callback::apply_current_config(scanner_name_.c_str(), handle_, &sane_api_);
if (cfg && callback::twain_ui_free)
callback::twain_ui_free(cfg);
}
}
void scanner::on_ui_event(int uev, void* sender)
{
if (uev == SANE_EVENT_UI_CLOSE_SETTING)
{
// setting UI closed ...
is_scanning_ = is_show_setting_ = false;
}
else if (uev == SANE_EVENT_UI_CLOSE_NORMAL)
{
// scan complete
// FIX on 2023-05-30: restore scan finished status, whether close UI is up to APP
//is_scanning_ = is_show_setting_;
is_scanning_ = false;
ui_notify = std::function<void(int, void*, int)>();
}
else if (uev == SANE_EVENT_UI_CLOSE_CANCEL)
{
// scan cancelled
if (thread_stop_.get() && thread_stop_->joinable())
thread_stop_->join();
thread_stop_.reset(new std::thread(&scanner::stop, this));
return; // notify in SCAN_FINISHED
//stop(); // nothing to do, the finishing work do in SANE_EVENT_SCAN_FINISHED
}
else if (uev == SANE_EVENT_SCAN_FINISHED)
{
// FIX on 2023-05-30: restore scan finished status, whether close UI is up to APP
//is_scanning_ = is_show_setting_;
is_scanning_ = false;
ui_notify = std::function<void(int, void*, int)>();
}
int( * h)(int, void*) = scanner_ev_handler_;
if (h)
{
h(uev, evh_param_);
}
return;
events_.save(uev, sizeof(uev));
}
std::string scanner::choose_scanner(const std::vector<std::string>& scanners)
{
if (scanners.empty())
return "";
std::vector<DEVQUEUI> devs;
std::string sel("");
int id = 1;
for (size_t i = 0; i < scanners.size(); ++i)
{
SANE_Handle h = nullptr;
int ret = sane_helper_->invoke_sane_open(scanners[i].c_str(), &h);
if (h)
{
char sn[256] = {0};
SANE_Int len = sizeof(sn) - 1;
sane_helper_->invoke_sane_control_option(h, SANE_OPT_ID_DEVICE_SERIAL_NO, SANE_ACTION_GET_VALUE, sn, &len);
if (sn[0])
{
DEVQUEUI dev;
dev.id = id++;
dev.name = scanners[i];
dev.sn = sn;
devs.push_back(dev);
}
sane_helper_->invoke_sane_close(h);
}
}
if (devs.size() == 0)
sel = scanners[0];
else if (devs.size() == 1)
sel = devs[0].name;
else if (callback::choose_scanner)
{
id = callback::choose_scanner(devs);
if (id != -1)
{
for (auto& v : devs)
{
if (v.id == id)
{
sel = v.name;
break;
}
}
}
}
else
{
//dlg_choose_dev dlg(nullptr, devs);
//dlg.show(true, true);
//sel = dlg.get_selected_device();
}
return sel;
}
int scanner::open(void)
{
int ret = close();
std::vector<std::string> que;
std::string name("");
scanner::get_scanner_name(model_.c_str(), que);
init_options_id();
scanner_name_ = "";
if (que.empty())
return SCANNER_ERR_DEVICE_NOT_FOUND;
if (que.size() == 1)
name = que[0];
else
{
name = choose_scanner(que);
if (name.empty())
return SCANNER_ERR_USER_CANCELED;
}
ret = sane_helper_->invoke_sane_open(name.c_str(), &handle_);
if (ret == SANE_STATUS_GOOD)
{
size_t pid = -1;
callback::reg_callback(handle_, this);
scanner_name_ = name;
pid = scanner_name_.find(" - ");
if (pid == -1)
pid = scanner_name_.length();
ret = init_options_id();
apply_config();
img_fmt_.compress.compression = SANE_CompressionType::SANE_COMPRESSION_NONE;
img_fmt_.compress.detail = 0;
img_fmt_.img_format = SANE_IMAGE_TYPE_BMP;
img_fmt_.detail = 0;
// check roller life ...
SANE_Int cnt = 0, life = 0, after = 0;
if (sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_ROLLER_COUNT, SANE_ACTION_GET_VALUE, &cnt, &after) == SANE_STATUS_GOOD &&
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_ROLLER_LIFE, SANE_ACTION_GET_VALUE, &life, &after) == SANE_STATUS_GOOD)
{
if (cnt >= life)
{
if (callback::show_messagebox_ui)
{
// 纸轮搓纸次数已超过设计使用范围,扫描过程中搓纸失败、歪斜、搓多张等异常频次可能会明显增多,请注意及时清洁、并联系设备供应商购买替换纸轮!
std::string roller_msg(local_trans::lang_trans_between_hz936("\347\272\270\350\275\256\346\220\223\347\272\270\346\254\241\346\225\260\345\267\262\350\266\205\350\277\207\350\256\276\350\256\241\344\275\277\347\224\250\350\214\203\345\233\264\357\274\214\346\211\253\346\217\217\350\277\207\347\250\213\344\270\255\346\220\223\347\272\270\345\244\261\350\264\245\343\200\201\346\255\252\346\226\234\343\200\201\346\220\223\345\244\232\345\274\240\347\255\211\345\274\202\345\270\270\351\242\221\346\254\241\345\217\257\350\203\275\344\274\232\346\230\216\346\230\276\345\242\236\345\244\232\357\274\214\350\257\267\346\263\250\346\204\217\345\217\212\346\227\266\346\270\205\346\264\201\343\200\201\345\271\266\350\201\224\347\263\273\350\256\276\345\244\207\344\276\233\345\272\224\345\225\206\350\264\255\344\271\260\346\233\277\346\215\242\347\272\270\350\275\256\357\274\201"));
app_wnd_ = callback::find_main_wnd();
callback::show_messagebox_ui(app_wnd_, ret, (void*)&roller_msg[0], 0);
}
}
}
}
else
{
if (callback::show_messagebox_ui)
{
app_wnd_ = callback::find_main_wnd();
callback::show_messagebox_ui(app_wnd_, ret, (void*)sane_helper_->invoke_sane_strstatus((SANE_Status)ret), 0);
}
}
return ret;
}
int scanner::close(void)
{
images_.clear();
if (done_.get() && done_->joinable())
done_->join();
scanner_ev_handler_ = nullptr;
ui_hide();
callback::unreg_callback(this);
if (handle_)
{
sane_helper_->invoke_sane_close(handle_);
}
handle_ = nullptr;
ex_id_ = EXTENSION_ID_BASE;
return SCANNER_ERR_OK;
}
int scanner::init_options_id(void)
{
SANE_Int op_id = 0, fix_id = 0;
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = nullptr;
sane_opts_.clear();
while ((desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, ++op_id)))
{
fix_id = -1;
if (sane_helper_->invoke_sane_control_option(handle_, op_id, (SANE_Action)SANE_ACTION_GET_FIX_ID, &fix_id, nullptr)
== SANE_STATUS_GOOD)
{
SANEOPT opt;
opt.opt_sn = op_id;
if (desc->type == SANE_TYPE_BOOL) // bool to enumeration constraint
opt.limit = VAL_LIMIT_ENUM;
else
opt.limit = scanner::from_sane_constraint(desc->constraint_type);
opt.type = scanner::from_sane_type(desc->type);
opt.size = desc->size;
opt.cap = desc->cap;
sane_opts_[fix_id] = opt;
}
}
// invisible option in SANE
if(0)
{
std::vector<int> inv;
struct _fix_id_cb fcb;
fcb.cb = callback::get_invisible_fix_id;
fcb.param = &inv;
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_BASE, (SANE_Action)SANE_ACTION_ENUM_INVISIBLE_FIX_ID, &fcb, nullptr);
for (auto& v : inv)
{
desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, v);
if (desc)
{
SANEOPT opt;
opt.opt_sn = v;
if (desc->type == SANE_TYPE_BOOL) // bool to enumeration constraint
opt.limit = VAL_LIMIT_ENUM;
else
opt.limit = scanner::from_sane_constraint(desc->constraint_type);
opt.type = scanner::from_sane_type(desc->type);
opt.size = desc->size;
opt.cap = desc->cap;
sane_opts_[v] = opt;
}
}
}
return ret;
}
char* scanner::get_opt_value(int id, int size, bool def_val)
{
char *buf = new char[size + 4];
SANE_Action act = def_val ? (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE : SANE_ACTION_GET_VALUE;
memset(buf, 0, size + 4);
if(sane_helper_->invoke_sane_control_option(handle_, id, act, buf, nullptr))
{
delete[] buf;
buf = nullptr;
}
return buf;
}
bool scanner::get_opt_value(int id, void* buf, bool def_val)
{
SANE_Action act = def_val ? (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE : SANE_ACTION_GET_VALUE;
return sane_helper_->invoke_sane_control_option(handle_, id, act, buf, nullptr) == SANE_STATUS_GOOD;
}
int scanner::control_read_string(int io_code, std::string& val)
{
char buf[256] = {0};
int ret = SCANNER_ERR_OK, len = sizeof(buf) - 1;
if(io_code == IO_CTRL_CODE_GET_SERIAL)
io_code = SANE_OPT_ID_DEVICE_SERIAL_NO;
else
io_code = -1;
if(io_code == -1)
{
ret = SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
else
{
ret = sane_helper_->invoke_sane_control_option(handle_, io_code, SANE_ACTION_GET_VALUE, buf, &len);
val = buf;
}
return ret;
}
bool scanner::get_option_value_with_parent(int sn, set_opt_value setv, void* param) // return true if handled
{
bool handled = true;
if (sn == SANE_OPT_ID_SCAN_COUNT)
{
const SANE_Option_Descriptor* parent = sane_helper_->invoke_sane_get_option_descriptor(handle_, SANE_OPT_ID_SCAN_MODE);
char* buf = get_opt_value(SANE_OPT_ID_SCAN_MODE, parent->size, false);
if (buf)
{
handled = strcmp(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), buf) == 0;
if (handled)
{
int count = -1;
value_role role = VAL_ROLE_CURRENT;
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_SCAN_MODE, (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE, buf, nullptr);
if (strcmp(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), buf) == 0)
role = value_role(role | VAL_ROLE_DEFAULT);
setv(&count, role, VAL_LIMIT_NONE, param);
}
delete[] buf;
}
}
else
handled = false;
return handled;
}
bool scanner::set_option_value_with_parent(int sn, void* data, int* err) // return true if handled sn
{
bool handled = false;
if (sn == SANE_OPT_ID_SCAN_COUNT)
{
const SANE_Option_Descriptor* parent = sane_helper_->invoke_sane_get_option_descriptor(handle_, SANE_OPT_ID_SCAN_MODE);
char* val = get_opt_value(SANE_OPT_ID_SCAN_MODE, parent->size, false);
SANE_Int after = 0;
if(val)
{
if (strcmp(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), val) == 0)
{
if (*(int*)data != -1)
{
strcpy(val, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_SMZDZS, true, nullptr));
*err = sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_SCAN_MODE, SANE_ACTION_SET_VALUE, val, &after);
}
}
else if (*(int*)data == -1)
{
strcpy(val, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr));
*err = sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_SCAN_MODE, SANE_ACTION_SET_VALUE, val, &after);
}
delete[] val;
}
}
return handled;
}
int scanner::set_option_value(int sn, SANE_Value_Type type, int size, void* data)
{
char* buf = nullptr;
SANE_Bool sb = SANE_FALSE;
SANE_Int si = 0, after = 0;
SANE_Fixed sf = 0;
int ret = SCANNER_ERR_OK;
void* val = data;
if (type == SANE_TYPE_BOOL)
{
sb = *(bool*)data ? SANE_TRUE : SANE_FALSE;
val = &sb;
}
else if (type == SANE_TYPE_INT)
{
si = *(int*)data;
val = &si;
}
else if (type == SANE_TYPE_FIXED)
{
sf = SANE_FIX(*(float*)data);
val = &sf;
}
else
{
buf = new char[size + 4];
memset(buf, 0, size + 4);
//strcpy(buf, ((std::string*)data)->c_str());
strcpy(buf, (char*)data);
val = buf;
}
ret = sane_helper_->invoke_sane_control_option(handle_, sn, SANE_ACTION_SET_VALUE, val, &after);
if (type == SANE_TYPE_BOOL)
{
*(bool*)data = sb == SANE_TRUE;
}
else if (type == SANE_TYPE_INT)
{
*(int*)data = si;
}
else if (type == SANE_TYPE_FIXED)
{
*(float*)data = (float)SANE_UNFIX(sf);
}
else if(buf)
{
strcpy((char*)val, buf);
delete[] buf;
}
return ret;
}
int scanner::thread_start(void)
{
SANE_Int dpi = 0;
int ret = 0;
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_RESOLUTION, SANE_ACTION_GET_VALUE, &dpi, nullptr);
dpi_ = dpi;
utils::to_log(LOG_LEVEL_DEBUG, "start scanning with DPI %d\r\n", dpi_);
scan_working_ = false;
ret = sane_helper_->invoke_sane_start(handle_);
// the third-APPs in linux will call 'stop' after every start, but it not happens in windows-apps, so we handle this as following ...
// wait until at least ONE picture returned, so we need not this - 2023-11-07
//if (ret == SANE_STATUS_NO_DOCS && prev_start_result_ == SANE_STATUS_GOOD)
// ret = sane_helper_->invoke_sane_start(handle_);
if (ret == SANE_STATUS_GOOD || ret == SCANNER_ERR_DEVICE_DOUBLE_FEEDING)
{
/*if (indicator_.get() && !IsWindowVisible(indicator_->hwnd()))
indicator_->show(true);*/
// unsigned int l = sizeof(img_fmt_);
// SANE_CompressionType cmprsn = img_fmt_.compress.compression;
// if (hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_GET_FINAL_IMAGE_FORMAT, &img_fmt_, &l))
// img_fmt_.img_format = SANE_IMAGE_TYPE_BMP;
// img_fmt_.compress.compression = cmprsn;
}
//else if (indicator_.get())
//{
// indicator_->notify_scan_over(sane_helper_->invoke_sane_strstatus((SANE_Status)ret), true);
//}
else
{
// display error message on progress UI, may be closed immediately by APP, so we hide progress UI and call message_box ...
//
#ifdef START_SCAN_IN_THREAD
if (callback::show_progress_ui && is_bIndicator && ui_notify)
{
int ev = SANE_EVENT_WORKING;
if (!scan_working_) // scan working thread would trigger the finish event, so here check this flag
{
ui_notify(SANE_EVENT_SCAN_FINISHED, (void*)sane_helper_->invoke_sane_strstatus((SANE_Status)ret), ret);
// block ?
// while (!is_scanning_)
// {
//#ifdef WIN32
// MSG msg = { 0 };
// PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE);
//#else
// std::this_thread::sleep_for(std::chrono::milliseconds(3));
//#endif
// }
}
}
else
#endif
if (!scan_working_)
{
if (callback::close_ui)
callback::close_ui(UI_INDICATOR);
if (callback::show_messagebox_ui)
{
callback::show_messagebox_ui(app_wnd_, ret, (void*)sane_helper_->invoke_sane_strstatus((SANE_Status)ret), 0);
}
// else
// {
// std::string msg(local_trans::a2u(sane_helper_->invoke_sane_strstatus((SANE_Status)ret), CP_UTF8));
// //if (indicator_.get())
// // indicator_->show(false);
// if (!IsWindow(app_wnd_))
// callback::bring_message_box_topmost(local_trans::lang_trans_between_hz936(CONST_STRING_START_FAILED).c_str());
// MessageBoxW(app_wnd_, msg.c_str(), local_trans::lang_trans_between_hz936(CONST_STRING_START_FAILED).c_str(), MB_OK | MB_ICONERROR);
// }
is_scanning_ = false;
}
}
prev_start_result_ = ret;
return ret;
}
// IRef
COM_API_IMPLEMENT(scanner, int32_t, add_ref(void))
{
return refer::add_ref();
}
COM_API_IMPLEMENT(scanner, int32_t, release(void))
{
return refer::release();
}
// ISaneInvoker
COM_API_IMPLEMENT(scanner, int, start(void))
{
int ret = SANE_STATUS_GOOD;
ev_cnt_ = 0;
events_.clear();
images_.clear();
scan_msg_ = "OK";
scan_err_ = false;
user_cancel_ = false;
fetch_imgs_ = 0;
is_scanning_ = true;
app_wnd_ = callback::find_main_wnd();
if (thread_starting_.get() && thread_starting_->joinable())
thread_starting_->join();
#ifdef START_SCAN_IN_THREAD
thread_starting_.reset(new std::thread(&scanner::thread_start, this));
get_scanned_images(-1); // block until image arrived or scan finished
ret = err_;
#else
ret = thread_start();
#endif
return ret;
}
COM_API_IMPLEMENT(scanner, int, stop(void))
{
user_cancel_ = true;
sane_helper_->invoke_sane_cancel(handle_);
return SANE_STATUS_GOOD;
}
COM_API_IMPLEMENT(scanner, int, get_event(void))
{
return events_.take();
}
COM_API_IMPLEMENT(scanner, void, set_event_callback(int(* handle_ev)(int, void*), void* para))
{
scanner_ev_handler_ = handle_ev;
evh_param_ = para;
}
COM_API_IMPLEMENT(scanner, bool, wait_image(DWORD milliseconds))
{
int count = get_scanned_images(milliseconds);
return count > 0;
}
COM_API_IMPLEMENT(scanner, int, get_scanned_images(DWORD milliseconds))
{
size_t count = images_.count();
DWORD elapse = 2;
is_ui_wait_img_ = true;
while (is_scanning_ && count == 0 && milliseconds)
{
#if OS_WIN
MSG msg = { 0 };
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
else
#endif
Sleep(elapse);
count = images_.count();
if (milliseconds != -1)
{
if (milliseconds <= elapse)
break;
milliseconds -= elapse;
}
}
is_ui_wait_img_ = false;
count = images_.count();
{
char msg[128] = { 0 };
sprintf_s(msg, _countof(msg) - 1, "Wait image count = %d\r\n", count);
utils::log_info(msg, (log_level)1);
}
return count;
}
COM_API_IMPLEMENT(scanner, IScanImg*, take_first_image(twain_xfer xfer))
{
scanned_img* img = images_.take(false);
if (img)
{
img->prepare_data_for_transfer(xfer);
img->add_ref();
utils::to_log(LOG_LEVEL_DEBUG, "Begin transferring image %d of %p\r\n", fetch_imgs_ + 1, img);
}
return dynamic_cast<IScanImg*>(img);
}
COM_API_IMPLEMENT(scanner, bool, get_first_image_header(SANE_Parameters* header, size_t* bytes, int* dpi))
{
return images_.get_header(header, bytes, dpi);
}
COM_API_IMPLEMENT(scanner, bool, discard_first_image(void))
{
scanned_img* img = images_.take();
if (img)
{
img->release();
return true;
}
else
{
return false;
}
}
COM_API_IMPLEMENT(scanner, bool, is_online(void))
{
std::string sn("");
return handle_ && control_read_string(IO_CTRL_CODE_GET_SERIAL, sn) != SCANNER_ERR_DEVICE_NOT_FOUND;
}
COM_API_IMPLEMENT(scanner, bool, is_paper_on(void))
{
SANE_Bool on = SANE_FALSE;
unsigned int len = sizeof(on);
if (get_opt_value(SANE_OPT_ID_PAPER_ON, &on, false) == SANE_STATUS_GOOD)
return on == SANE_TRUE;
else
return false;
}
COM_API_IMPLEMENT(scanner, int, last_error(void))
{
return err_;
}
COM_API_IMPLEMENT(scanner, int, image_fetched(IScanImg* tx))
{
fetch_imgs_++;
if (ui_notify)
ui_notify(SANE_EVENT_IMG_UPLOADED, nullptr, fetch_imgs_);
utils::to_log(LOG_LEVEL_DEBUG, "Transferring image %d of %p finished.\r\n", fetch_imgs_, tx);
return 0;
}
COM_API_IMPLEMENT(scanner, bool, get_option_info(int sn, value_type* type, value_limit* limit, int* bytes, bool* readonly))
{
if (sane_opts_.count(sn))
{
if (type)
*type = sane_opts_[sn].type;
if (limit)
*limit = sane_opts_[sn].limit;
if (bytes)
*bytes = sane_opts_[sn].size;
if (readonly)
*readonly = IS_CAP_READONLY(sane_opts_[sn].cap);
return true;
}
else
{
return false;
}
}
COM_API_IMPLEMENT(scanner, bool, get_value(int sn, set_opt_value setval, void* param))
{
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, sn);
if (desc)
utils::to_log(LOG_LEVEL_DEBUG, "get_value(0x%04X - %s) ...\r\n", sn, desc->name);
else
{
utils::to_log(LOG_LEVEL_WARNING, "get_value(0x%04X, ...), but the option is not found !!!\r\n", sn);
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
if (get_option_value_with_parent(sn, setval, param))
return true;
char* init = get_opt_value(sn, desc->size, true),
* now = get_opt_value(sn, desc->size, false);
int ret = SANE_STATUS_GOOD;
if (desc->type == SANE_TYPE_BOOL)
{
bool val = false;
int role = VAL_ROLE_NONE;
if (*(SANE_Bool*)init == SANE_FALSE)
role |= VAL_ROLE_DEFAULT;
if (*(SANE_Bool*)now == SANE_FALSE)
role |= VAL_ROLE_CURRENT;
setval(&val, (value_role)role, VAL_LIMIT_ENUM, param);
val = true;
role = VAL_ROLE_NONE;
if (*(SANE_Bool*)init == SANE_TRUE)
role |= VAL_ROLE_DEFAULT;
if (*(SANE_Bool*)now == SANE_TRUE)
role |= VAL_ROLE_CURRENT;
setval(&val, (value_role)role, VAL_LIMIT_ENUM, param);
}
else if (desc->type == SANE_TYPE_INT)
{
do
{
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
{
set_value_range<SANE_Int, int>(*(SANE_Int*)now, *(SANE_Int*)init, desc->constraint.range->min, desc->constraint.range->max, desc->constraint.range->quant, setval, param, scanner::to_int);
}
else if (desc->constraint_type == SANE_CONSTRAINT_WORD_LIST)
{
const SANE_Word* v = desc->constraint.word_list;
for (int i = 0; i < v[0]; ++i)
{
value_role role = VAL_ROLE_NONE;
if (v[i + 1] == *(SANE_Int*)now)
role = value_role(role | VAL_ROLE_CURRENT);
if (v[i + 1] == *(SANE_Int*)init)
role = value_role(role | VAL_ROLE_DEFAULT);
int val = v[i + 1];
if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
}
}
else
set_cur_and_def_value<int>(*(SANE_Int*)now, *(SANE_Int*)init, setval, param);
}while (0);
}
else if (desc->type == SANE_TYPE_FIXED)
{
do
{
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
{
set_value_range<SANE_Fixed, double>(*(SANE_Fixed*)now, *(SANE_Fixed*)init, desc->constraint.range->min, desc->constraint.range->max, desc->constraint.range->quant, setval, param, scanner::to_double);
}
else if (desc->constraint_type == SANE_CONSTRAINT_WORD_LIST)
{
const SANE_Word* v = desc->constraint.word_list;
for (int i = 0; i < v[0]; ++i)
{
value_role role = VAL_ROLE_NONE;
if (v[i + 1] == *(SANE_Fixed*)now)
role = value_role(role | VAL_ROLE_CURRENT);
if (v[i + 1] == *(SANE_Fixed*)init)
role = value_role(role | VAL_ROLE_DEFAULT);
double val = SANE_UNFIX(v[i + 1]);
if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
}
}
else
set_cur_and_def_value<double>(SANE_UNFIX(*(SANE_Int*)now), SANE_UNFIX(*(SANE_Int*)init), setval, param);
} while (0);
}
else if (desc->type == SANE_TYPE_STRING)
{
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
if (strcmp(desc->constraint.string_list[i], now) == 0)
role = value_role(role | VAL_ROLE_CURRENT);
if (strcmp(desc->constraint.string_list[i], init) == 0)
role = value_role(role | VAL_ROLE_DEFAULT);
std::string val(desc->constraint.string_list[i]);
if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
}
}
else
set_cur_and_def_value<std::string>(now, init, setval, param);
} while (0);
}
else
{
ret = SANE_STATUS_INVAL;
}
delete[] init;
delete[] now;
return ret == SANE_STATUS_GOOD;
}
COM_API_IMPLEMENT(scanner, bool, get_value(int sn, void* data, int* len))
{
return sane_helper_->invoke_sane_control_option(handle_, sn, SANE_ACTION_GET_VALUE, data, nullptr) == SANE_STATUS_GOOD;
}
COM_API_IMPLEMENT(scanner, int, set_value(int sn, void* val))
{
int ret = SANE_STATUS_INVAL;
SANE_Int after = 0;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, sn);
if (desc)
utils::to_log(LOG_LEVEL_DEBUG, "set_value(0x%04X - %s, '%s') ...\r\n", sn, desc->name, callback::option_value_2_string(desc->type, val).c_str());
else
{
utils::to_log(LOG_LEVEL_WARNING, "set_value(0x%04X, ...), but the option is not found !!!\r\n", sn);
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
twain_set_ = true;
if (!set_option_value_with_parent(sn, val, &ret))
ret = set_option_value(sn, desc->type, desc->size, val);
return ret;
}
COM_API_IMPLEMENT(scanner, int, convert_image(SANE_ImageFormatConvert* conv))
{
return sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_TRANSFORM_IMAGE_FORMAT, SANE_ACTION_SET_VALUE, conv, nullptr);
}
COM_API_IMPLEMENT(scanner, int, get_fixed_ids(SANE_Bool(* cb)(int id, void* param), void* param))
{
bool stopped = false;
for (auto& v : sane_opts_)
{
if (!cb(v.first, param))
{
stopped = true;
break;
}
}
return 0;
}
COM_API_IMPLEMENT(scanner, void, twain_set_transfer(twain_xfer xfer))
{
xfer_ = xfer;
}
COM_API_IMPLEMENT(scanner, void, twain_set_compression(SANE_CompressionType compression, void* detail))
{
img_fmt_.compress.compression = compression;
img_fmt_.compress.detail = detail;
}
COM_API_IMPLEMENT(scanner, int, twain_get_config(char* buf, size_t* len))
{
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
COM_API_IMPLEMENT(scanner, int, twain_set_config(char* buf, size_t len))
{
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
// ui ...
COM_API_IMPLEMENT(scanner, bool, ui_show_setting(HWND parent, bool with_scan, bool indicator))
{
bool ret = false;
utils::to_log(LOG_LEVEL_ALL, "ui_show_setting(%p, '%s', '%s'), api = %p\r\n", parent, with_scan ? "with scan" : "only ui", indicator ? "has indicator" : "no indicator", callback::show_setting_ui);
is_show_ui_ = with_scan;
is_show_setting_ = true;
events_.clear();
if (callback::show_setting_ui)
{
if (with_scan)
{
images_.clear();
scan_msg_ = "OK";
scan_err_ = false;
}
auto ui = [this](ui_result res)
{
int uev = SANE_EVENT_SCAN_FINISHED;
switch (res)
{
case UI_RESULT_FAILED:
break;
case UI_RESULT_OK:
break;
case UI_RESULT_CLOSE_NORMAL:
is_show_ui_ = false;
uev = SANE_EVENT_UI_CLOSE_NORMAL;
on_ui_event(uev, (void*)uev);
break;
case UI_RESULT_CLOSE_CANCEL:
is_show_ui_ = false;
uev = SANE_EVENT_UI_CLOSE_CANCEL;
on_ui_event(uev, (void*)uev);
break;
case UI_RESULT_CLOSE_SETTING:
is_show_setting_ = false;
uev = SANE_EVENT_UI_CLOSE_SETTING;
on_ui_event(uev, (void*)uev);
break;
case UI_RESULT_START_SCAN:
on_ui_event(SANE_EVENT_UI_SCAN_COMMAND, nullptr);
break;
default:
break;
}
};
ret = callback::show_setting_ui(handle_, parent, &sane_api_, scanner_name_.c_str(), with_scan, ui) == ui_result::UI_RESULT_OK;
}
return ret;
}
COM_API_IMPLEMENT(scanner, bool, ui_show_progress(HWND parent, bool bIndicator))
{
is_bIndicator = bIndicator;
ui_notify = std::function<void(int, void*, int)>();
auto ui_process = [this](ui_result res)
{
int uev = SANE_EVENT_SCAN_FINISHED;
switch (res)
{
case UI_RESULT_CLOSE_NORMAL:
uev = SANE_EVENT_UI_CLOSE_NORMAL;
//if (!is_show_ui_)
{
on_ui_event(uev, (void*)uev);
}
break;
case UI_RESULT_CLOSE_CANCEL:
uev = SANE_EVENT_UI_CLOSE_CANCEL;
//if (!is_show_ui_)
{
on_ui_event(uev, (void*)uev);
}
//else
// stop();
break;
default:
break;
}
};
if (callback::show_progress_ui && bIndicator)
{
callback::show_progress_ui(parent, ui_process,&ui_notify);
}
return true;
}
COM_API_IMPLEMENT(scanner, void, ui_hide(void))
{
if (callback::close_ui)
callback::close_ui(UI_INDICATOR | UI_SETTING | UI_MSG_BOX);
is_show_setting_ = false;
ui_notify = std::function<void(int, void*, int)>();
}
COM_API_IMPLEMENT(scanner, bool, ui_is_ok(void))
{
return callback::show_setting_ui != nullptr;
}
// called from device-layer ...
int scanner::handle_device_event(int ev_code, void* data, unsigned int* len)
{
if (ev_code == SANE_EVENT_WORKING)
{
scan_working_ = true;
img_ind_ = 0;
if (callback::show_progress_ui && is_bIndicator)
ui_notify(ev_code, data, *len);
on_ui_event(ev_code, (void*)ev_code);
utils::log_info("Scanning ...\r\n", LOG_LEVEL_DEBUG);
}
else if (ev_code == SANE_EVENT_IMAGE_OK)
{
SANE_Image* simg = (SANE_Image*)data;
scanned_img* img = nullptr;
char name[40] = { 0 };
sprintf_s(name, _countof(name) - 1, "img_%05u.bmp", ++img_ind_);
img = new scanned_img(handle_, simg->header, simg->data, simg->bytes, simg->flag.dpi, (tmp_path_ + name).c_str(), xfer_, &img_fmt_);
if (img->bytes() /*>= simg->bytes*/)
{
size_t bytes = 0;
int times = 0;
images_.count(&bytes);
img->set_image_status(simg->flag.statu);
bytes /= 1024 * 1024;
while (bytes > max_img_mem_ && !user_cancel_ && times++ < 20) // memory control
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
images_.count(&bytes);
bytes /= 1024 * 1024;
if (times == 1)
utils::log_info("Memory usage upto limit! wait up to 100 ms ...\r\n", LOG_LEVEL_DEBUG);
}
images_.save(img, img->bytes());
}
else
{
img->release();
}
if (ui_notify)
ui_notify(ev_code, data, img_ind_);
// notifyXferReady 改为有图片才通知防止部分APP在imgGetInfo中返回错误不能退出的问题 - 22023-10-25
if (img_ind_ == 1)
on_ui_event(SANE_EVENT_TWAIN_XFER_READY, nullptr);
{
char msg[128] = { 0 };
sprintf_s(msg, _countof(msg) - 1, "New image(%u) received with %u bytes\r\n", img_ind_, simg->bytes);
utils::log_info(msg, (log_level)1);
}
}
else if (ev_code == SANE_EVENT_USB_DATA_RECEIVED)
{
//if (indicator_.get())
// indicator_->notify_data_arrived(false);
//else if (ui_notify)
// ui_notify(ev_code, data, 0);
}
else if (ev_code == SANE_EVENT_SCAN_FINISHED)
{
err_ = *len;
scan_msg_ = data ? (char*)data : "OK";
if (done_.get() && done_->joinable())
done_->join();
done_.reset(new std::thread(&scanner::scan_done, this));
// is_scanning_ = false;
{
char msg[128] = { 0 };
sprintf_s(msg, _countof(msg) - 1, "Scan finished with error: %u\r\n", *len);
utils::log_info(msg, (log_level)1);
}
}
//else if (ev_code == SANE_EVENT_ERROR) // 屏蔽,在停止扫描时展示信息 - 2023-05-30
//{
// if (callback::show_messagebox_ui && *len)
// {
// callback::show_messagebox_ui(app_wnd_, ev_code, (void*)data, 0);
// }
// else if (*len) //错误弹出
// {
// std::string msg(local_trans::a2u((char*)data, CP_UTF8));
// if (!IsWindow(app_wnd_))
// callback::bring_message_box_topmost(local_trans::lang_trans_between_hz936(CONST_STRING_ERROR).c_str());
// MessageBoxW(app_wnd_, msg.c_str(), local_trans::lang_trans_between_hz936(CONST_STRING_ERROR).c_str(), MB_OK);
// }
//// on_ui_event(ev_code, (void*)ev_code);
//}
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// exports
extern "C"
{
int initialize_sane(void* reserve)
{
SANE_Int ver = 0;
utils::init_log(LOG_TYPE_FILE, LOG_LEVEL_ALL, "_twain");
sane_helper_ = new sane_helper();
if(!sane_helper_->load_sane(OEM_SHORT_NAME_E))
{
delete sane_helper_;
sane_helper_ = nullptr;
return SCANNER_ERR_NOT_OPEN;
}
callback::init_ui();
if (sane_helper_->invoke_sane_init_ex(&ver, callback::sane_event_callback, nullptr) == SANE_STATUS_GOOD)
{
std::string lang(utils::get_module_full_path(MODULE_NAME_SCANNER));
size_t pos = lang.rfind(PATH_SEPARATOR[0]);
if(pos++ == std::string::npos)
pos = 0;
lang.erase(pos);
lang += MODULE_NAME_LANG;
pos = local_trans::load_lang_pak(lang.c_str());
utils::to_log(LOG_LEVEL_WARNING, "load language component (%s) = %s\n", lang.c_str(), strerror(pos));
return SANE_STATUS_GOOD;
}
else
return SCANNER_ERR_LANG_PAK_LOST;
}
bool is_scanner_online(const char* model)
{
std::vector<std::string> que;
scanner::get_scanner_name(model, que);
return !que.empty();
}
ISaneInvoker* open_scanner(const char* model, int* err)
{
if (!sane_helper_)
{
int rv = initialize_sane(nullptr);
if (rv)
{
if (err)
*err = rv;
return nullptr;
}
}
scanner* s = new scanner(model);
int ret = s->open(), cnt = 0;
while (ret)
{
if (ret == SCANNER_ERR_OPENED_BY_OTHER_PROCESS)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(5));
if (cnt++ > 100)
break;
ret = s->open();
}
if (ret && ret != SCANNER_ERR_OPENED_BY_OTHER_PROCESS) // error SCANNER_ERR_OPENED_BY_OTHER_PROCESS has tipped already
{
HWND parent = callback::find_main_wnd();
std::string msg(local_trans::lang_trans_between_hz936("\xE6\x89\x93\xE5\xBC\x80\xE6\x89\xAB\xE6\x8F\x8F\xE4\xBB\xAA\xE5\xA4\xB1\xE8\xB4\xA5\xE3\x80\x82", true, nullptr)),
title(local_trans::lang_trans_between_hz936("\xE9\x94\x99\xE8\xAF\xAF", true, nullptr));
if (callback::show_messagebox_ui)
callback::show_messagebox_ui(parent, 0, &msg[0], ret);
}
if (err)
*err = ret;
return s ? dynamic_cast<ISaneInvoker*>(s) : nullptr;
}
int uninitialize_sane(void* reserve)
{
if(sane_helper_ && sane_helper_->invoke_sane_exit)
sane_helper_->invoke_sane_exit();
callback::unint_ui();
if(sane_helper_)
delete sane_helper_;
sane_helper_ = nullptr;
utils::uninit();
return 0;
}
}