605 lines
18 KiB
C++
605 lines
18 KiB
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 = { true, true, 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 = { true, true, 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: */
|