Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/extensions/source/scanner/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 18 kB image not shown  

Quelle  twain32shim.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


/*
 * twain32shim.exe is a separate 32-bit executable that serves as a shim
 * between LibreOffice and Windows' 32-bit TWAIN component. Without it,
 * it's impossible for 64-bit program to use TWAIN on Windows.
 * Using 64-bit TWAIN DSM library from twain.org to avoid using the shim
 * is not an option, because scanner manufacturers only provide 32-bit
 * drivers, and 64-bit drivers are only offered as 3rd-party commercial
 * products. The shim is also used in 32-bit LibreOffice for uniformity.
*/


#include "twain32shim.hxx"
#include <systools/win32/comtools.hxx>
#include <tools/helpers.hxx>
#include <twain/twain.h>
#include <o3tl/unit_conversion.hxx>

#define WM_TWAIN_FALLBACK (WM_SHIM_INTERNAL + 0)

namespace
{
double FixToDouble(const TW_FIX32& rFix) { return rFix.Whole + rFix.Frac / 65536.; }

const wchar_t sTwainWndClass[] = L"TwainClass";

class ImpTwain
{
public:
    ImpTwain(HANDLE hParentThread);
    ~ImpTwain();

private:
    enum class TWAINState
    {
        DSMunloaded = 1,
        DSMloaded = 2,
        DSMopened = 3,
        DSopened = 4,
        DSenabled = 5,
        DSreadyToXfer = 6,
        Xferring = 7,
    };

    TW_IDENTITY m_aAppId;
    TW_IDENTITY m_aSrcId;
    DWORD m_nParentThreadId;
    HANDLE m_hProc;
    DSMENTRYPROC m_pDSM = nullptr;
    HMODULE m_hMod = nullptr;
    TWAINState m_nCurState = TWAINState::DSMunloaded;
    HWND m_hTwainWnd = nullptr;
    HHOOK m_hTwainHook = nullptr;
    HANDLE m_hMap = nullptr; // the *duplicated* handle

    static bool IsTwainClassWnd(HWND hWnd);
    static ImpTwain* GetImpFromWnd(HWND hWnd);
    static void ImplCreateWnd(HWND hWnd, LPARAM lParam);
    static LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam);
    static LRESULT CALLBACK MsgHook(int nCode, WPARAM wParam, LPARAM lParam);

    void Destroy() { ImplFallback(TWAIN_EVENT_QUIT); }
    bool SelectSource();
    bool InitXfer();

    void NotifyParent(WPARAM nEvent, LPARAM lParam);
    bool ImplHandleMsg(MSG* pMsg);
    void ImplOpenSourceManager();
    void ImplOpenSource();
    bool ImplEnableSource();
    void ImplXfer();
    void ImplFallback(WPARAM nEvent);

    void ImplFallbackHdl(WPARAM nEvent);
    void ImplRequestHdl(WPARAM nRequest);
};

//static
bool ImpTwain::IsTwainClassWnd(HWND hWnd)
{
    const int nBufSize = SAL_N_ELEMENTS(sTwainWndClass);
    wchar_t sClassName[nBufSize];
    return (GetClassNameW(hWnd, sClassName, nBufSize) && wcscmp(sClassName, sTwainWndClass) == 0);
}

//static
ImpTwain* ImpTwain::GetImpFromWnd(HWND hWnd)
{
    if (!IsTwainClassWnd(hWnd))
        return nullptr;
    return reinterpret_cast<ImpTwain*>(GetWindowLongPtrW(hWnd, GWLP_USERDATA));
}

//static
void ImpTwain::ImplCreateWnd(HWND hWnd, LPARAM lParam)
{
    CREATESTRUCT* pCS = reinterpret_cast<CREATESTRUCT*>(lParam);
    if (pCS && IsTwainClassWnd(hWnd))
        SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCS->lpCreateParams));
}

// static
LRESULT CALLBACK ImpTwain::WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    ImpTwain* pImpTwain = GetImpFromWnd(hWnd);
    switch (nMsg)
    {
        case WM_CREATE:
            ImplCreateWnd(hWnd, lParam);
            break;
        case WM_TWAIN_FALLBACK:
            if (pImpTwain)
                pImpTwain->ImplFallbackHdl(wParam);
            break;
        case WM_TWAIN_REQUEST:
            if (pImpTwain)
                pImpTwain->ImplRequestHdl(wParam);
            break;
    }
    return DefWindowProcW(hWnd, nMsg, wParam, lParam);
}

// static
LRESULT CALLBACK ImpTwain::MsgHook(int nCode, WPARAM wParam, LPARAM lParam)
{
    MSG* pMsg = reinterpret_cast<MSG*>(lParam);
    if (nCode >= 0 && pMsg)
    {
        ImpTwain* pImpTwain = GetImpFromWnd(pMsg->hwnd);
        if (pImpTwain && pImpTwain->ImplHandleMsg(pMsg))
        {
            pMsg->message = WM_USER;
            pMsg->lParam = 0;

            return 0;
        }
    }

    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}

HANDLE GetProcOfThread(HANDLE hThread)
{
    DWORD nProcId = GetProcessIdOfThread(hThread);
    if (!nProcId)
        ThrowLastError("GetProcessIdOfThread");
    HANDLE hRet = OpenProcess(PROCESS_DUP_HANDLE, FALSE, nProcId);
    if (!hRet)
        ThrowLastError("OpenProcess");
    return hRet;
}

ImpTwain::ImpTwain(HANDLE hParentThread)
    : m_aAppId{ /* Id              */ 0,
                { /* Version.MajorNum */ 1,
                  /* Version.MinorNum */ 0,
                  /* Version.Language */ TWLG_USA,
                  /* Version.Country  */ TWCY_USA,
                  /* Version.Info     */ "8.0" },
                /* ProtocolMajor   */ TWON_PROTOCOLMAJOR,
                /* ProtocolMinor   */ TWON_PROTOCOLMINOR,
                /* SupportedGroups */ DG_IMAGE | DG_CONTROL,
                /* Manufacturer    */ "Sun Microsystems",
                /* ProductFamily   */ "Office",
                /* ProductName     */ "Office" }
    , m_nParentThreadId(GetThreadId(hParentThread))
    , m_hProc(GetProcOfThread(hParentThread))
{
    WNDCLASSW aWc = { 0,       &WndProc, 0,       sizeof(WNDCLASSW), GetModuleHandleW(nullptr),
                      nullptr, nullptr,  nullptr, nullptr,           sTwainWndClass };
    if (!RegisterClassW(&aWc))
        ThrowLastError("RegisterClassW");
    m_hTwainWnd = CreateWindowExW(WS_EX_TOPMOST, aWc.lpszClassName, L"TWAIN", 0, 0, 0, 0, 0,
                                  HWND_DESKTOP, nullptr, aWc.hInstance, this);
    if (!m_hTwainWnd)
        ThrowLastError("CreateWindowExW");
    m_hTwainHook = SetWindowsHookExW(WH_GETMESSAGE, &MsgHook, nullptr, GetCurrentThreadId());
    if (!m_hTwainHook)
        ThrowLastError("SetWindowsHookExW");

    NotifyParent(TWAIN_EVENT_NOTIFYHWND, reinterpret_cast<LPARAM>(m_hTwainWnd));
}

ImpTwain::~ImpTwain()
{
    DestroyWindow(m_hTwainWnd);
    UnhookWindowsHookEx(m_hTwainHook);
}

bool ImpTwain::SelectSource()
{
    TW_UINT16 nRet = TWRC_FAILURE;

    ImplOpenSourceManager();

    if (TWAINState::DSMopened == m_nCurState)
    {
        TW_IDENTITY aIdent;

        aIdent.Id = 0;
        aIdent.ProductName[0] = '\0';
        NotifyParent(TWAIN_EVENT_SCANNING, 0);
        nRet = m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, &aIdent);
    }

    Destroy();
    return (TWRC_SUCCESS == nRet);
}

bool ImpTwain::InitXfer()
{
    bool bRet = false;

    ImplOpenSourceManager();

    if (TWAINState::DSMopened == m_nCurState)
    {
        ImplOpenSource();

        if (TWAINState::DSopened == m_nCurState)
            bRet = ImplEnableSource();
    }

    if (!bRet)
        Destroy();

    return bRet;
}

void ImpTwain::ImplOpenSourceManager()
{
    if (TWAINState::DSMunloaded == m_nCurState)
    {
        m_hMod = LoadLibraryW(L"TWAIN_32.DLL");
        if (!m_hMod)
        {
            // Windows directory might not be in DLL search path sometimes, so try the full path
            sal::systools::CoTaskMemAllocated<wchar_t> sPath;
            if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Windows, 0, nullptr, &sPath)))
            {
                std::wstring sPathAndFile(sPath);
                sPathAndFile += L"\\TWAIN_32.DLL";
                m_hMod = LoadLibraryW(sPathAndFile.c_str());
            }
        }
        if (m_hMod)
        {
            m_nCurState = TWAINState::DSMloaded;

            m_pDSM = reinterpret_cast<DSMENTRYPROC>(GetProcAddress(m_hMod, "DSM_Entry"));
            if (m_pDSM
                && (m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, &m_hTwainWnd)
                    == TWRC_SUCCESS))
            {
                m_nCurState = TWAINState::DSMopened;
            }
        }
    }
}

void ImpTwain::ImplOpenSource()
{
    if (TWAINState::DSMopened == m_nCurState)
    {
        if ((m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, &m_aSrcId)
             == TWRC_SUCCESS)
            && (m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, &m_aSrcId)
                == TWRC_SUCCESS))
        {
            TW_CAPABILITY aCap
                = { CAP_XFERCOUNT, TWON_ONEVALUE, GlobalAlloc(GHND, sizeof(TW_ONEVALUE)) };
            TW_ONEVALUE* pVal = static_cast<TW_ONEVALUE*>(GlobalLock(aCap.hContainer));
            assert(pVal);

            pVal->ItemType = TWTY_INT16;
            pVal->Item = 1;
            GlobalUnlock(aCap.hContainer);
            m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &aCap);
            GlobalFree(aCap.hContainer);
            m_nCurState = TWAINState::DSopened;
        }
    }
}

bool ImpTwain::ImplEnableSource()
{
    bool bRet = false;

    if (TWAINState::DSopened == m_nCurState)
    {
        TW_USERINTERFACE aUI = { truetrue, m_hTwainWnd };

        NotifyParent(TWAIN_EVENT_SCANNING, 0);
        m_nCurState = TWAINState::DSenabled;

        if (m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, &aUI)
            == TWRC_SUCCESS)
        {
            bRet = true;
        }
        else
        {
            // dialog failed
            m_nCurState = TWAINState::DSopened;
        }
    }

    return bRet;
}

void ImpTwain::NotifyParent(WPARAM nEvent, LPARAM lParam)
{
    PostThreadMessageW(m_nParentThreadId, WM_TWAIN_EVENT, nEvent, lParam);
}

bool ImpTwain::ImplHandleMsg(MSG* pMsg)
{
    if (!m_pDSM)
        return false;

    TW_EVENT aEvt = { pMsg, MSG_NULL };
    TW_UINT16 nRet = m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, &aEvt);

    switch (aEvt.TWMessage)
    {
        case MSG_XFERREADY:
        {
            WPARAM nEvent = TWAIN_EVENT_QUIT;

            if (TWAINState::DSenabled == m_nCurState)
            {
                m_nCurState = TWAINState::DSreadyToXfer;
                ImplXfer();

                if (m_hMap)
                    nEvent = TWAIN_EVENT_XFER;
            }
            else if (TWAINState::Xferring == m_nCurState && m_hMap)
            {
                // Already sent TWAIN_EVENT_XFER; not processed yet;
                // duplicate event
                nEvent = TWAIN_EVENT_NONE;
            }

            ImplFallback(nEvent);
        }
        break;

        case MSG_CLOSEDSREQ:
            Destroy();
            break;

        case MSG_NULL:
            nRet = TWRC_NOTDSEVENT;
            break;
    }

    return (TWRC_DSEVENT == nRet);
}

void ImpTwain::ImplXfer()
{
    if (m_nCurState == TWAINState::DSreadyToXfer)
    {
        TW_IMAGEINFO aInfo;
        HANDLE hDIB = nullptr;
        double nXRes, nYRes;

        if (m_pDSM(&m_aAppId, &m_aSrcId, DG_IMAGE, DAT_IMAGEINFO, MSG_GET, &aInfo) == TWRC_SUCCESS)
        {
            nXRes = FixToDouble(aInfo.XResolution);
            nYRes = FixToDouble(aInfo.YResolution);
        }
        else
            nXRes = nYRes = -1;

        switch (m_pDSM(&m_aAppId, &m_aSrcId, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, &hDIB))
        {
            case TWRC_CANCEL:
                m_nCurState = TWAINState::Xferring;
                break;

            case TWRC_XFERDONE:
            {
                if (hDIB)
                {
                    m_hMap = nullptr;
                    const HGLOBAL hGlob = static_cast<HGLOBAL>(hDIB);
                    const SIZE_T nDIBSize = GlobalSize(hGlob);
                    const DWORD nMapSize = nDIBSize + 4; // leading 4 bytes for size
                    if (nMapSize > nDIBSize) // check for wrap
                    {
                        if (LPVOID pBmpMem = GlobalLock(hGlob))
                        {
                            if ((nXRes > 0) && (nYRes > 0))
                            {
                                // set resolution of bitmap
                                BITMAPINFOHEADER* pBIH = static_cast<BITMAPINFOHEADER*>(pBmpMem);

                                const auto[m, d]
                                    = getConversionMulDiv(o3tl::Length::in, o3tl::Length::m);
                                pBIH->biXPelsPerMeter = std::round(o3tl::convert(nXRes, d, m));
                                pBIH->biYPelsPerMeter = std::round(o3tl::convert(nYRes, d, m));
                            }

                            HANDLE hMap = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
                                                             PAGE_READWRITE, 0, nMapSize, nullptr);
                            if (hMap)
                            {
                                LPVOID pMap = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, nMapSize);
                                if (pMap)
                                {
                                    memcpy(pMap, &nMapSize, 4); // size of the following DIB
                                    memcpy(static_cast<char*>(pMap) + 4, pBmpMem, nDIBSize);
                                    FlushViewOfFile(pMap, nDIBSize);
                                    UnmapViewOfFile(pMap);

                                    DuplicateHandle(GetCurrentProcess(), hMap, m_hProc, &m_hMap, 0,
                                                    FALSE, DUPLICATE_SAME_ACCESS);
                                }

                                CloseHandle(hMap);
                            }

                            GlobalUnlock(hGlob);
                        }
                    }
                }

                GlobalFree(static_cast<HGLOBAL>(hDIB));

                m_nCurState = TWAINState::Xferring;
            }
            break;

            default:
                break;
        }
    }
}

void ImpTwain::ImplFallback(WPARAM nEvent)
{
    PostMessageW(m_hTwainWnd, WM_TWAIN_FALLBACK, nEvent, 0);
}

void ImpTwain::ImplFallbackHdl(WPARAM nEvent)
{
    bool bFallback = true;

    switch (m_nCurState)
    {
        case TWAINState::Xferring:
        case TWAINState::DSreadyToXfer:
        {
            TW_PENDINGXFERS aXfers;

            if (m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER, &aXfers)
                == TWRC_SUCCESS)
            {
                if (aXfers.Count != 0)
                    m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, &aXfers);
            }

            m_nCurState = TWAINState::DSenabled;
        }
        break;

        case TWAINState::DSenabled:
        {
            TW_USERINTERFACE aUI = { truetrue, m_hTwainWnd };

            m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, &aUI);
            m_nCurState = TWAINState::DSopened;
        }
        break;

        case TWAINState::DSopened:
        {
            m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, &m_aSrcId);
            m_nCurState = TWAINState::DSMopened;
        }
        break;

        case TWAINState::DSMopened:
        {
            m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, &m_hTwainWnd);
            m_nCurState = TWAINState::DSMloaded;
        }
        break;

        case TWAINState::DSMloaded:
        {
            m_pDSM = nullptr;
            FreeLibrary(m_hMod);
            m_hMod = nullptr;
            m_nCurState = TWAINState::DSMunloaded;
        }
        break;

        case TWAINState::DSMunloaded:
        {
            if (nEvent > TWAIN_EVENT_NONE)
                NotifyParent(nEvent, reinterpret_cast<LPARAM>(m_hMap));
            PostQuitMessage(0);

            bFallback = false;
        }
        break;
    }

    if (bFallback)
        ImplFallback(nEvent);
}

void ImpTwain::ImplRequestHdl(WPARAM nRequest)
{
    switch (nRequest)
    {
        case TWAIN_REQUEST_QUIT:
            Destroy();
            break;
        case TWAIN_REQUEST_SELECTSOURCE:
            NotifyParent(TWAIN_EVENT_REQUESTRESULT, LPARAM(SelectSource()));
            break;
        case TWAIN_REQUEST_INITXFER:
            NotifyParent(TWAIN_EVENT_REQUESTRESULT, LPARAM(InitXfer()));
            break;
    }
}
// namespace

int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
    int argc = 0;
    LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
    if (argc != 2)
        return 1; // Wrong argument count
    // 1st argument is parent thread handle; must be inherited.
    // HANDLE is 32-bit in 32-bit applications, so wcstoul is OK.
    HANDLE hParentThread = reinterpret_cast<HANDLE>(wcstoul(argv[1], nullptr, 10));
    LocalFree(argv);
    if (!hParentThread)
        return 2; // Invalid parent thread handle argument value

    int nRet = 0;
    try
    {
        ImpTwain aImpTwain(hParentThread); // creates main window

        MSG msg;
        while (true)
        {
            DWORD nWaitResult
                = MsgWaitForMultipleObjects(1, &hParentThread, FALSE, INFINITE, QS_ALLINPUT);
            if (nWaitResult == WAIT_OBJECT_0)
                return 5; // Parent process' thread died before we exited
            if (nWaitResult == WAIT_FAILED)
                return 6; // Some Win32 error
            // nWaitResult == WAIT_OBJECT_0 + nCount => an event is in queue
            bool bQuit = false;
            while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
            {
                // process it here
                if (msg.message == WM_QUIT)
                {
                    bQuit = true;
                    nRet = msg.wParam;
                }
                else
                {
                    TranslateMessage(&msg);
                    DispatchMessageW(&msg);
                }
            }
            if (bQuit)
                break;
        }
    }
    catch (const std::exception& e)
    {
        printf("Exception thrown: %s", e.what());
        nRet = 7;
    }
    return nRet;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=91 H=95 G=92

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.