yewanji 2023-05-22 16:49 采纳率: 36.3%
浏览 132
已结题

c++ 控制台程序 如何使用twain 实现调用扫描仪进行扫描

问题是这样的,现在目前github上使用twain调用扫描仪一直都是基于mfc工程,我是参考了这篇文章,并且把资源下载到本地,改造成控制台程序https://blog.csdn.net/a1101000099/article/details/38561365

然后,在构造回调函数的时候,我们之间出现了分歧,MFC是通过 CDialogEx::PreTranslateMessage(MSG* pMsg) 这个回调函数来处理twain回调的(这里的msg变量是mfc带的)

if(SourceEnabled())
    {
    TW_EVENT twEvent;
        twEvent.pEvent = (TW_MEMREF)&msg;
        twEvent.TWMessage = MSG_NULL;
    
        CallTwainProc(&m_AppId,&m_Source,DG_CONTROL,DAT_EVENT,MSG_PROCESSEVENT,(TW_MEMREF)&twEvent);
        if(GetRC() != TWRC_NOTDSEVENT)
        {
            TranslateMessage(twEvent);
        }
        return FALSE;

    }
    return FALSE;

但是我控制台程序,无法构造这个msg对象,于是我用了如下的方法,间隔2s,去获取一下twain的最新状态(听说可以直接注册twain回调函数,但是试过好几次,都失败了)

    TW_EVENT twEvent;
            MSG windowsMsg;
            windowsMsg.message = WM_USER;     // 模拟的消息类型
            windowsMsg.wParam = 0;            // 模拟的消息参数
            windowsMsg.lParam = 0;            // 模拟的消息参数
            windowsMsg.hwnd = GetConsoleWindow(); // 控制台窗口句柄,获取当前控制台窗口的句柄
            twEvent.pEvent = &windowsMsg;
            twEvent.TWMessage = MSG_NULL;
            while (true)
            {
                //间隔2s
                std::this_thread::sleep_for(std::chrono::seconds(2));
                CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, (TW_MEMREF)&twEvent);
                TranslateMessage(twEvent);
            }
            

重点来了,上面的CallTwainProc 返回的状态码一直是5,我查了下,这个状态码是失败的意思,也就是说,当前的问题是无法正常处理twain回调,也就无法从扫描仪拿到图片

附上我的TwainCpp.cpp完整代码

#define _CRT_SECURE_NO_WARNINGS
#include "twaincpp.h"
#include <cstddef>
#include "Base64Util.h"
#include "ImageUtil.h"
#include <chrono>
#include <thread>
/*
Constructor:
    Parameters : HWND
                Window to subclass

*/
CTwain::CTwain(HWND hWnd)
{
    m_hTwainDLL = NULL;
    m_pDSMProc = NULL;
    m_bSourceSelected = FALSE;
    m_bDSOpen = m_bDSMOpen = FALSE;
    m_bSourceEnabled = FALSE;
    m_bModalUI = TRUE;
    m_nImageCount = TWCPP_ANYCOUNT;
    if (hWnd)
    {
        InitTwain(hWnd);
    }
}

CTwain::~CTwain()
{
    ReleaseTwain();
}

/*
Initializes TWAIN interface . Is already called from the constructor.
It should be called again if ReleaseTwain is called.

  hWnd is the window which has to subclassed in order to recieve
  Twain messaged. Normally - this would be your main application window.

*/
BOOL CTwain::InitTwain(HWND hWnd)
{
    char libName[512];
    if (IsValidDriver())
    {
        return TRUE;
    }
    memset(&m_AppId, 0, sizeof(m_AppId));
    if (!IsWindow(hWnd))
    {
        return FALSE;
    }
    m_hMessageWnd = hWnd;
    strcpy(libName, "twain_32.dll");
    m_hTwainDLL = LoadLibraryA(libName);
    if (m_hTwainDLL != NULL)
    {
        if (!(m_pDSMProc = (DSMENTRYPROC)GetProcAddress(m_hTwainDLL, MAKEINTRESOURCEA(1))))
        {
            FreeLibrary(m_hTwainDLL);
            m_hTwainDLL = NULL;
        }
    }
    if (IsValidDriver())
    {
        GetIdentity();
        m_bDSMOpen = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, (TW_MEMREF)&m_hMessageWnd);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

/*
Releases the twain interface . Need not be called unless you
want to specifically shut it down.
*/
void CTwain::ReleaseTwain()
{
    if (IsValidDriver())
    {
        CloseDSM();
        FreeLibrary(m_hTwainDLL);
        m_hTwainDLL = NULL;
        m_pDSMProc = NULL;
    }
}

/*
Returns true if a valid driver has been loaded
*/
BOOL CTwain::IsValidDriver() const
{
    return (m_hTwainDLL && m_pDSMProc);
}

/*
Entry point into Twain. For a complete description of this
routine  please refer to the Twain specification 1.8
*/
BOOL CTwain::CallTwainProc(pTW_IDENTITY pOrigin, pTW_IDENTITY pDest,
    TW_UINT32 DG, TW_UINT16 DAT, TW_UINT16 MSG,
    TW_MEMREF pData)
{
    if (IsValidDriver())
    {
        USHORT ret_val;
        ret_val = (*m_pDSMProc)(pOrigin, pDest, DG, DAT, MSG, pData);
        m_returnCode = ret_val;
        if (ret_val != TWRC_SUCCESS)
        {
            (*m_pDSMProc)(pOrigin, pDest, DG_CONTROL, DAT_STATUS, MSG_GET, &m_Status);
        }
        return (ret_val == TWRC_SUCCESS);
    }
    else
    {
        m_returnCode = TWRC_FAILURE;
        return FALSE;
    }
}

/*
This function should ideally be overridden in the derived class . If only a
few fields need to be updated , call CTawin::GetIdentity first in your
derived class
*/
void CTwain::GetIdentity()
{
    // Expects all the fields in m_AppId to be set except for the id field.
    m_AppId.Id = 0; // Initialize to 0 (Source Manager
    // will assign real value)
    m_AppId.Version.MajorNum = 3; //Your app's version number
    m_AppId.Version.MinorNum = 8;
    m_AppId.Version.Language = TWLG_USA;
    m_AppId.Version.Country = TWCY_USA;
    strcpy(m_AppId.Version.Info, "3.8");
    m_AppId.ProtocolMajor = TWON_PROTOCOLMAJOR;
    m_AppId.ProtocolMinor = TWON_PROTOCOLMINOR;
    m_AppId.SupportedGroups = DG_IMAGE | DG_CONTROL;
    strcpy(m_AppId.Manufacturer, "MICSS");
    strcpy(m_AppId.ProductFamily, "Generic");
    strcpy(m_AppId.ProductName, "MyTwain");

}


/*
Called to display a dialog box to select the Twain source to use.
This can be overridden if a list of all sources is available
to the application. These sources can be enumerated by Twain.
it is not yet supportted by CTwain.
*/
BOOL CTwain::SelectSource()
{
    memset(&m_Source, 0, sizeof(m_Source));
    if (!SourceSelected())
    {
        SelectDefaultSource();
    }
    if (CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, &m_Source))
    {
        m_bSourceSelected = TRUE;
    }
    return m_bSourceSelected;
}

/*
Called to select the default source
*/
BOOL CTwain::SelectDefaultSource()
{
    m_bSourceSelected = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, &m_Source);
    return m_bSourceSelected;
}

/*
Closes the Data Source
*/
void CTwain::CloseDS()
{
    if (DSOpen())
    {
        DisableSource();
        CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, (TW_MEMREF)&m_Source);
        m_bDSOpen = FALSE;
    }
}

/*
Closes the Data Source Manager
*/
void CTwain::CloseDSM()
{
    if (DSMOpen())
    {
        CloseDS();
        CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, (TW_MEMREF)&m_hMessageWnd);
        m_bDSMOpen = FALSE;
    }
}

/*
Returns true if the Data Source Manager is Open
*/
BOOL CTwain::DSMOpen() const
{
    return IsValidDriver() && m_bDSMOpen;
}

/*
Returns true if the Data Source is Open
*/
BOOL CTwain::DSOpen() const
{
    return IsValidDriver() && DSMOpen() && m_bDSOpen;
}

/*
Opens a Data Source supplied as the input parameter
*/
BOOL CTwain::OpenSource(TW_IDENTITY* pSource)
{
    if (pSource)
    {
        m_Source = *pSource;
    }
    if (DSMOpen())
    {
        m_bDSOpen = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, (TW_MEMREF)&m_Source);
    }
    return DSOpen();
}

/*
Should be called from the main message loop of the application. Can always be called,
it will not process the message unless a scan is in progress.
*/
BOOL CTwain::ProcessMessage(MSG msg)
{
    if (SourceEnabled())
    {
        TW_EVENT twEvent;
        twEvent.pEvent = (TW_MEMREF)&msg;
        twEvent.TWMessage = MSG_NULL;

        CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, (TW_MEMREF)&twEvent);
        if (GetRC() != TWRC_NOTDSEVENT)
        {
            TranslateMessage(twEvent);
        }
        return FALSE;

    }
    return FALSE;
}

/*
Queries the capability of the Twain Data Source
*/
BOOL CTwain::GetCapability(TW_CAPABILITY& twCap, TW_UINT16 cap, TW_UINT16 conType)
{
    if (DSOpen())
    {
        twCap.Cap = cap;
        twCap.ConType = conType;
        twCap.hContainer = NULL;

        if (CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, (TW_MEMREF)&twCap))
        {
            return TRUE;
        }
    }
    return FALSE;
}

/*
Queries the capability of the Twain Data Source
*/
BOOL CTwain::GetCapability(TW_UINT16 cap, TW_UINT32& value)
{
    TW_CAPABILITY twCap;
    if (GetCapability(twCap, cap))
    {
        pTW_ONEVALUE pVal;
        pVal = (pTW_ONEVALUE)GlobalLock(twCap.hContainer);
        if (pVal)
        {
            value = pVal->Item;
            GlobalUnlock(pVal);
            GlobalFree(twCap.hContainer);
            return TRUE;
        }
    }
    return FALSE;
}


/*
Sets the capability of the Twain Data Source
*/
BOOL CTwain::SetCapability(TW_UINT16 cap, TW_UINT16 value, BOOL sign)
{
    if (DSOpen())
    {
        TW_CAPABILITY twCap;
        pTW_ONEVALUE pVal;
        BOOL ret_value = FALSE;

        twCap.Cap = cap;
        twCap.ConType = TWON_ONEVALUE;

        twCap.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE));
        if (twCap.hContainer)
        {
            pVal = (pTW_ONEVALUE)GlobalLock(twCap.hContainer);
            pVal->ItemType = sign ? TWTY_INT16 : TWTY_UINT16;
            pVal->Item = (TW_UINT32)value;
            GlobalUnlock(twCap.hContainer);
            ret_value = SetCapability(twCap);
            GlobalFree(twCap.hContainer);
        }
        return ret_value;
    }
    return FALSE;
}

BOOL CTwain::SetResolution(TW_UINT16 cap, TW_UINT32 value)
{
    if (DSOpen())
    {
        TW_CAPABILITY twCap;
        pTW_ONEVALUE pVal;
        BOOL ret_value = FALSE;

        twCap.Cap = cap;
        twCap.ConType = TWON_ONEVALUE;

        twCap.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE));
        if (twCap.hContainer)
        {
            pVal = (pTW_ONEVALUE)GlobalLock(twCap.hContainer);
            pVal->ItemType = TWTY_FIX32;
            pVal->Item = (TW_UINT32)value;
            GlobalUnlock(twCap.hContainer);
            ret_value = SetCapability(twCap);
            GlobalFree(twCap.hContainer);
        }
        return ret_value;
    }
    return FALSE;
}

/*
Sets the capability of the Twain Data Source
*/
BOOL CTwain::SetCapability(TW_CAPABILITY& cap)
{
    if (DSOpen())
    {
        return CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_CAPABILITY, MSG_SET, (TW_MEMREF)&cap);
    }
    return FALSE;
}

/*
Sets the number of images which can be accpeted by the application at one time
*/
BOOL CTwain::SetImageCount(TW_INT16 nCount)
{
    if (SetCapability(CAP_XFERCOUNT, (TW_UINT16)nCount, TRUE))
    {
        m_nImageCount = nCount;
        return TRUE;
    }
    else
    {
        if (GetRC() == TWRC_CHECKSTATUS)
        {
            TW_UINT32 count;
            if (GetCapability(CAP_XFERCOUNT, count))
            {
                nCount = (TW_INT16)count;
                if (SetCapability(CAP_XFERCOUNT, nCount))
                {
                    m_nImageCount = nCount;
                    return TRUE;
                }
            }
        }
    }
    return FALSE;
}

/*
Called to enable the Twain Acquire Dialog. This too can be
overridden but is a helluva job .
*/
BOOL CTwain::EnableSource(BOOL showUI)
{
    if (DSOpen() && !SourceEnabled())
    {
        twUI.ShowUI = FALSE;
        twUI.hParent = (TW_HANDLE)m_hMessageWnd;
        if (CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, (TW_MEMREF)&twUI))
        {
            m_bSourceEnabled = TRUE;
            m_bModalUI = twUI.ModalUI;
        }
        else
        {
            m_bSourceEnabled = FALSE;
            m_bModalUI = TRUE;
        }
        return m_bSourceEnabled;
    }
    return FALSE;
}

/*
Called to acquire images from the source. parameter numImages i the
numberof images that you an handle concurrently
*/
BOOL CTwain::Acquire(int numImages, TW_UINT16 duplex, TW_UINT16 size, TW_UINT16 pixel, TW_UINT16 resolution)
{
    if (DSOpen() || OpenSource())
    {
        BOOL ret_value = SetCapability(CAP_DUPLEXENABLED, duplex, TRUE);
        ret_value = SetCapability(ICAP_SUPPORTEDSIZES, size, TRUE);
        ret_value = SetCapability(ICAP_PIXELTYPE, pixel, TRUE);
        ret_value = SetResolution(ICAP_XRESOLUTION, resolution);
        ret_value = SetResolution(ICAP_YRESOLUTION, resolution);
        //设置为jpeg格式返回,默认的位图文件很大
        SetCapability(ICAP_IMAGEFILEFORMAT, TWCP_JPEG);
        if (SetImageCount(numImages))
        {
            if (EnableSource())
            {
                return TRUE;
            }
        }
    }
    return FALSE;
}

/*
 Called to disable the source.
*/
BOOL CTwain::DisableSource()
{
    if (SourceEnabled())
    {
        if (CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, &twUI))
        {
            m_bSourceEnabled = FALSE;
            return TRUE;
        }
    }
    return FALSE;
}

/*
Called by ProcessMessage to Translate a TWAIN message
*/
void CTwain::TranslateMessage(TW_EVENT& twEvent)
{
    switch (twEvent.TWMessage)
    {
    case MSG_XFERREADY:
        TransferImage();
        break;
    case MSG_CLOSEDSREQ:
        if (CanClose())
        {
            CloseDS();
        }
        break;
    }
}

/*
Gets Imageinfo for an image which is about to be transferred.
*/
BOOL CTwain::GetImageInfo(TW_IMAGEINFO& info)
{
    if (SourceEnabled())
    {
        return CallTwainProc(&m_AppId, &m_Source, DG_IMAGE, DAT_IMAGEINFO, MSG_GET, (TW_MEMREF)&info);
    }
    return FALSE;
}

/*
Trasnfers the image or cancels the transfer depending on the state of the
TWAIN system
*/
void CTwain::TransferImage()
{
    TW_IMAGEINFO info;
    BOOL bContinue = TRUE;
    while (bContinue)
    {
        if (GetImageInfo(info))
        {
            int permission;
            permission = ShouldTransfer(info);
            switch (permission)
            {
            case TWCPP_CANCELTHIS:
                bContinue = EndTransfer();
                break;
            case TWCPP_CANCELALL:
                CancelTransfer();
                bContinue = FALSE;
                break;
            case TWCPP_DOTRANSFER:
                bContinue = GetImage(info);
                break;
            }
        }
    }
}

/*
Ends the current transfer.
Returns TRUE if the more images are pending
*/
BOOL CTwain::EndTransfer()
{
    TW_PENDINGXFERS twPend;
    if (CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER, (TW_MEMREF)&twPend))
    {
        if (twPend.Count == 0)
        {
            DisableSource();
        }
        return twPend.Count != 0;
    }
    return FALSE;
}

/*
Aborts all transfers
*/
void CTwain::CancelTransfer()
{
    TW_PENDINGXFERS twPend;
    CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, (TW_MEMREF)&twPend);
}

/*
Calls TWAIN to actually get the image
*/
BOOL CTwain::GetImage(TW_IMAGEINFO& info)
{
    HANDLE hBitmap;
    CallTwainProc(&m_AppId, &m_Source, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, &hBitmap);
    switch (m_returnCode)
    {
    case TWRC_XFERDONE:
        CopyImage(hBitmap, info);
        break;
    case TWRC_CANCEL:
        break;
    case TWRC_FAILURE:
        CancelTransfer();
        return FALSE;

    }
    GlobalFree(hBitmap);
    return EndTransfer();
}

/**
* 获取驱动列表
*/
vector<TW_IDENTITY> CTwain::getSources()
{
    sourceList.clear();
    if (CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETFIRST, &m_Source)) {
        while (CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETNEXT, &m_Source)) {
            TW_IDENTITY temp_Source = m_Source;
            sourceList.push_back(temp_Source);
        }
    }
    return sourceList;
}

void CTwain::CopyImage(HANDLE hBitmap, TW_IMAGEINFO& info) {
    storeImage(hBitmap, info);
}


TW_IDENTITY CTwain::getSourceByName(const char* sourceName) {
    for (size_t i = 0; i < sourceList.size(); i++) {
        // 使用迭代器访问向量元素
        string name = sourceList[i].ProductName;
        if (sourceName == name) {
            return sourceList[i];
        }
    }
    return TW_IDENTITY{};
}
void CALLBACK ScanCallbackFunction(pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, TW_UINT32 uiMsg, TW_MEMREF pData)
{
    int a = 0;
}


void CTwain::startScan(const char* sourceName) {
    TW_IDENTITY source = getSourceByName(sourceName);
    if (source.Id == 0 && source.Version.MajorNum == 0 && source.Version.MinorNum == 0) {
        return;
    }
    if (OpenSource(&source))
    {
        BOOL ret_value = SetCapability(CAP_DUPLEXENABLED, 0, TRUE);
        ret_value = SetCapability(ICAP_SUPPORTEDSIZES, 0, TRUE);
        ret_value = SetCapability(ICAP_PIXELTYPE, 0, TRUE);
        ret_value = SetResolution(ICAP_XRESOLUTION, 0);
        ret_value = SetResolution(ICAP_YRESOLUTION, 300);
        //SetImageCount(-1)
        EnableSource(true);
        //注册回调事件
        if (SourceEnabled())
        {
            TW_EVENT twEvent;
            MSG windowsMsg;
            windowsMsg.message = WM_USER;     // 模拟的消息类型
            windowsMsg.wParam = 0;            // 模拟的消息参数
            windowsMsg.lParam = 0;            // 模拟的消息参数
            windowsMsg.hwnd = GetConsoleWindow(); // 控制台窗口句柄,获取当前控制台窗口的句柄
            twEvent.pEvent = &windowsMsg;
            twEvent.TWMessage = MSG_NULL;
            while (true)
            {
                //间隔2s
                std::this_thread::sleep_for(std::chrono::seconds(2));
                CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, (TW_MEMREF)&twEvent);
                TranslateMessage(twEvent);
            }
            

        }
    }
}

  • 写回答

5条回答 默认 最新

  • 急速光粒 2023-05-22 17:25
    关注

    请确认你的消息构建:

        MSG windowsMsg;
                windowsMsg.message = WM_USER;     // 模拟的消息类型
                windowsMsg.wParam = 0;            // 模拟的消息参数
                windowsMsg.lParam = 0;            // 模拟的消息参数
                windowsMsg.hwnd = GetConsoleWindow(); // 控制台窗口句柄,获取当前控制台窗口的句柄
                twEvent.pEvent = &windowsMsg;
                twEvent.TWMessage = MSG_NULL;
    
    

    和消息循环中的一致。,我感觉这里您构建的有点问题。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 5月26日
  • 赞助了问题酬金15元 5月22日
  • 创建了问题 5月22日

悬赏问题

  • ¥100 科大讯飞语音唤醒词,unbuntu环境,报错
  • ¥15 可以实现这个有不同背景颜色的九九乘法表吗?
  • ¥50 python写segy数据时出错2
  • ¥20 关于R studio 做精确稳定检验的问题!(语言-r语言)
  • ¥50 用贝叶斯决策方法,设计CAD程序
  • ¥20 关于#目标检测#的问题:(qq收集表到时间才能填写,填写的份数有上限)
  • ¥50 ZYNQ7020双核FLAHS烧写的问题
  • ¥20 ue 5 中想要实现第一人称人物左右行走摆动的效果,摄像头只向右摆动一次(关键词-结点)
  • ¥15 AD9164瞬时带宽1.8G,怎么计算出来?
  • ¥15 鼠标右键,撤销删除 复制 移动,要怎样删除? HKEY_CLASSES_ROOT*\shellex\ContextMenuHandlers 没用