#include "scanner.h" #include #include "sane_option_trans.h" #include #include #include #include #include #include #include "sane_helper.h" #include "../../sdk/hginclude/utils.h" #define START_SCAN_IN_THREAD #if !defined(WIN32) && !defined(_WIN64) template 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 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 lock(cb_lock_); std::vector::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 lock(cb_lock_); std::vector::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 lock(cb_lock_); std::vector::iterator it = std::find(g_scanner_instances.begin(), g_scanner_instances.end(), invoker); if (it != g_scanner_instances.end()) g_scanner_instances.erase(it); } struct { const char* name; const char* title; }g_opts[] = { {SANE_STD_OPT_NAME_RESTORE , OPTION_TITLE_HFMRSZ} , {SANE_STD_OPT_NAME_HELP , OPTION_TITLE_BZ} , {SANE_STD_OPT_NAME_IS_MULTI_OUT , OPTION_TITLE_DLSC} , {SANE_STD_OPT_NAME_MULTI_OUT_TYPE , OPTION_TITLE_DLSCLX} , {SANE_STD_OPT_NAME_COLOR_MODE , OPTION_TITLE_YSMS} , {SANE_STD_OPT_NAME_BINARY_THRESHOLD , OPTION_TITLE_HBTXYZ} , {SANE_STD_OPT_NAME_REVERSE_01 , OPTION_TITLE_HBTXFSSC} , {SANE_STD_OPT_NAME_FILTER , OPTION_TITLE_HDHHBTX_CSYZQ} , {SANE_STD_OPT_NAME_RID_MULTIOUT_RED , OPTION_TITLE_24WCSTX_DLSCCH} , {SANE_STD_OPT_NAME_RID_ANSWER_SHEET_RED , OPTION_TITLE_24WCSTX_DTKCH} , {SANE_STD_OPT_NAME_ERASE_BACKGROUND , OPTION_TITLE_BJYC} , {SANE_STD_OPT_NAME_BKG_COLOR_RANGE , OPTION_TITLE_BJSCFDFW} , {SANE_STD_OPT_NAME_SHARPEN , OPTION_TITLE_RHYMH} , {SANE_STD_OPT_NAME_RID_MORR , OPTION_TITLE_QCMW} , {SANE_STD_OPT_NAME_RID_GRID , OPTION_TITLE_CWW} , {SANE_STD_OPT_NAME_ERROR_EXTENSION , OPTION_TITLE_CWKS} , {SANE_STD_OPT_NAME_NOISE_OPTIMIZE , OPTION_TITLE_HBTXZDYH} , {SANE_STD_OPT_NAME_NOISE_SIZE , OPTION_TITLE_ZDYHCC} , {SANE_STD_OPT_NAME_PAPER , OPTION_TITLE_ZZCC} , {SANE_STD_OPT_NAME_CUSTOM_AREA , OPTION_TITLE_ZDYSMQY} , {SANE_STD_OPT_NAME_CUSTOM_AREA_LEFT , OPTION_TITLE_SMQYZCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_RIGHT , OPTION_TITLE_SMQYYCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_TOP , OPTION_TITLE_SMQYSCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_BOTTOM , OPTION_TITLE_SMQYXCmm} , {SANE_STD_OPT_NAME_SIZE_CHECK , OPTION_TITLE_CCJC} , {SANE_STD_OPT_NAME_PAGE , OPTION_TITLE_SMYM} , {SANE_STD_OPT_NAME_DISCARD_BLANK_SENS , OPTION_TITLE_TGKBYLMD} , {SANE_STD_OPT_NAME_RESOLUTION , OPTION_TITLE_FBL} , {SANE_STD_OPT_NAME_TIME_TO_SLEEP , OPTION_TITLE_XMSJ} , {SANE_STD_OPT_NAME_IMAGE_QUALITY , OPTION_TITLE_HZ} , {SANE_STD_OPT_NAME_EXCHANGE ,OPTION_TITLE_JHZFM} , {SANE_STD_OPT_NAME_SPLIT ,OPTION_TITLE_TXCF } , {SANE_STD_OPT_NAME_ANTI_SKEW , OPTION_TITLE_ZDJP} , {SANE_STD_OPT_NAME_IS_CUSTOM_GAMMA , OPTION_TITLE_QYSDQX} , {SANE_STD_OPT_NAME_GAMMA , OPTION_TITLE_JMZ} , {SANE_STD_OPT_NAME_BRIGHTNESS , OPTION_TITLE_LDZ} , {SANE_STD_OPT_NAME_CONTRAST , OPTION_TITLE_DBD} , {SANE_STD_OPT_NAME_IS_PHOTO_MODE , OPTION_TITLE_ZPMS} , {SANE_STD_OPT_NAME_ERASE_BLACK_FRAME , OPTION_TITLE_XCHK} , {SANE_STD_OPT_NAME_DARK_SAMPLE , OPTION_TITLE_SSYZ} , {SANE_STD_OPT_NAME_THRESHOLD , OPTION_TITLE_YZ} , {SANE_STD_OPT_NAME_ANTI_NOISE_LEVEL , OPTION_TITLE_BJKZDJ} , {SANE_STD_OPT_NAME_MARGIN , OPTION_TITLE_BYSJ} , {SANE_STD_OPT_NAME_FILL_BKG_MODE , OPTION_TITLE_BJTCFS} , {SANE_STD_OPT_NAME_IS_ANTI_PERMEATE , OPTION_TITLE_FZST} , {SANE_STD_OPT_NAME_ANTI_PERMEATE_LEVEL , OPTION_TITLE_FZSTDJ} , {SANE_STD_OPT_NAME_RID_HOLE_L , OPTION_TITLE_CKYCZC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_L , OPTION_TITLE_ZCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_R , OPTION_TITLE_CKYCYC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_R , OPTION_TITLE_YCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_T , OPTION_TITLE_CKYCSC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_T , OPTION_TITLE_SCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_B , OPTION_TITLE_CKYCXC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_B , OPTION_TITLE_XCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_IS_FILL_COLOR , OPTION_TITLE_SCTC} , {SANE_STD_OPT_NAME_IS_ULTROSONIC_CHECK , OPTION_TITLE_CSBJC} , {SANE_STD_OPT_NAME_DOUBLE_FEED_HANDLE , OPTION_TITLE_SZTPCL} , {SANE_STD_OPT_NAME_IS_CHECK_STAPLE , OPTION_TITLE_ZDJC} , {SANE_STD_OPT_NAME_SCAN_MODE , OPTION_TITLE_SMZS} , {SANE_STD_OPT_NAME_SCAN_COUNT , OPTION_TITLE_SMSL} , {SANE_STD_OPT_NAME_TEXT_DIRECTION , OPTION_TITLE_WGFX} , {SANE_STD_OPT_NAME_IS_ROTATE_BKG_180 , OPTION_TITLE_BMXZ180} , {SANE_STD_OPT_NAME_IS_CHECK_DOG_EAR , OPTION_TITLE_ZJJC} , {SANE_STD_OPT_NAME_DOG_EAR_SIZE , OPTION_TITLE_ZJDX} , {SANE_STD_OPT_NAME_IS_CHECK_ASKEW , OPTION_TITLE_WXJC} , {SANE_STD_OPT_NAME_ASKEW_RANGE , OPTION_TITLE_WXRRD} , {SANE_STD_OPT_NAME_FEED_STRENGTH , OPTION_TITLE_FZQD} , {SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH , OPTION_TITLE_ZDFZQD} , {SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE , OPTION_TITLE_JZSBL} , {SANE_STD_OPT_NAME_WAIT_TO_SCAN , OPTION_TITLE_DZSM} , {SANE_STD_OPT_NAME_FOLD_TYPE , OPTION_TITLE_DZMS} , {SANE_STD_OPT_NAME_COLOR_CORRECTION , OPTION_TITLE_SPJZ} }, g_discard[] = { {SANE_STD_OPT_NAME_REVERSE_01 , "\351\273\221\347\231\275\345\233\276\345\203\217\345\217\215\350\211\262\350\276\223\345\207\272\357\274\210\346\255\243\345\270\270\351\242\234\350\211\262\344\270\272\357\274\2320-\351\273\221\350\211\262\357\274\2331-\347\231\275\350\211\262\357\274\211"} // 黑白图像反色输出(正常颜色为:0-黑色;1-白色) , {SANE_STD_OPT_NAME_FILTER , "\347\201\260\345\272\246\346\210\226\351\273\221\347\231\275\345\233\276\345\203\217 - \351\231\244\350\211\262"} // 灰度或黑白图像 - 除色 , {SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH , "\350\207\252\345\212\250\346\220\223\347\272\270\345\274\272\345\272\246"} // 自动搓纸强度 , {SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE , "\346\220\223\347\272\270\351\230\210\345\200\274"} // " 搓纸阈值" }; const char* option_title_2_name(const char* title) { while (*title == ' ') title++; for (size_t i = 0; i < _countof(g_discard); ++i) { if (strcmp(title, g_discard[i].title) == 0) return g_discard[i].name; } for (size_t i = 0; i < _countof(g_opts); ++i) { if (strcmp(title, g_opts[i].title) == 0) return g_opts[i].name; } return ""; } const char* option_name_2_title(const char* name) { for (size_t i = 0; i < _countof(g_opts); ++i) { if (strcmp(name, g_opts[i].name) == 0) return g_opts[i].title; } return ""; } 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& 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; // 应用设备的当前配�? int (*show_setting_ui)(SANE_Handle device, HWND parent, LPSANEAPI api, const char* devname, bool with_scan, std::function callback) = nullptr; int (*show_progress_ui)(HWND parent, std::function callback, std::function* 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(SCANNER_DRIVER_PART_NAME)); 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; } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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(); 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(); } void scanner::get_scanner_name(const char* model, std::vector& 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; } 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(); } else if (uev == SANE_EVENT_UI_CLOSE_CANCEL) { // scan cancelled 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(); } 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& scanners) { if (scanners.empty()) return ""; std::vector 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 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; // transport_config_file(); 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) { 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; } } 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_); 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 ... if (ret == SANE_STATUS_NO_DOCS && prev_start_result_ == SANE_STATUS_GOOD) ret = sane_helper_->invoke_sane_start(handle_); if (ret == SANE_STATUS_GOOD) { /*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; ui_notify(SANE_EVENT_SCAN_FINISHED, (void *)sane_helper_->invoke_sane_strstatus((SANE_Status)ret), ret); } else #endif { 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, long, add_ref(void)) { return refer::add_ref(); } COM_API_IMPLEMENT(scanner, long, 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)); #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) { // MSG msg = { 0 }; // if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) // { // TranslateMessage(&msg); // DispatchMessageW(&msg); // } // else Sleep(elapse); count = images_.count(); //int ev = get_event(); // //if (ev == SANE_EVENT_SCAN_FINISHED) //{ // ui_hide(); // break; //} //else if (ev == SANE_EVENT_UI_CLOSE_CANCEL) //{ // stop(); // ui_hide(); // break; //} //else if (ev == SANE_EVENT_UI_CLOSE_NORMAL) //{ // ui_hide(); // break; //} 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(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)) { if (get_option_value_with_parent(sn, setval, param)) return true; const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, sn); 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*)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(*(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*)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(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(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); { 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()); } 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(bool(* cb)(uint32_t id, void* param), void* param)) { for (auto& v : sane_opts_) { if (!cb(v.first, param)) 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)) { bIndicator = false; is_bIndicator = bIndicator; ui_notify = std::function(); 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(); } COM_API_IMPLEMENT(scanner, bool, ui_is_ok(void)) { return callback::show_setting_ui != nullptr; } // called from device-layer ... int scanner::last_err(void) { return err_; } int scanner::handle_device_event(int ev_code, void* data, unsigned int* len) { if (ev_code == SANE_EVENT_WORKING) { 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((SANE_Image_Statu)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_); { 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; if (ui_notify) ui_notify(ev_code, data, *len); else { if (*len) { if (callback::show_messagebox_ui) { callback::show_messagebox_ui(app_wnd_, ev_code, (void*)data, 0); } } on_ui_event(ev_code, (void*)ev_code); } // 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((std::string(OEM_SHORT_NAME_E) + "sane").c_str())) { 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(SCANNER_DRIVER_PART_NAME)); size_t pos = lang.rfind(PATH_SEPARATOR[0]); if(pos++ == std::string::npos) pos = 0; lang.erase(pos); lang += LANG_DLL_NAME; 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 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(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; } }