diff options
-rw-r--r-- | Repository.mk | 2 | ||||
-rw-r--r-- | config_host.mk.in | 2 | ||||
-rw-r--r-- | configure.ac | 23 | ||||
-rw-r--r-- | extensions/Executable_twain32shim.mk | 33 | ||||
-rw-r--r-- | extensions/Library_scn.mk | 15 | ||||
-rw-r--r-- | extensions/Module_extensions.mk | 5 | ||||
-rw-r--r-- | extensions/source/scanner/scanwin.cxx | 1010 | ||||
-rw-r--r-- | extensions/source/scanner/twain32shim.cxx | 598 | ||||
-rw-r--r-- | extensions/source/scanner/twain32shim.hxx | 70 | ||||
-rw-r--r-- | external/twain_dsm/ExternalPackage_twain_dsm.mk | 20 | ||||
-rw-r--r-- | external/twain_dsm/ExternalProject_twain_dsm.mk | 33 | ||||
-rw-r--r-- | external/twain_dsm/Module_twain_dsm.mk | 8 | ||||
-rw-r--r-- | external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.filters.patch | 54 | ||||
-rw-r--r-- | external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.patch | 265 | ||||
-rw-r--r-- | external/twain_dsm/UnpackedTarball_twain_dsm.mk | 9 | ||||
-rw-r--r-- | external/twain_dsm/fix-non-us-ascii-chars-part1.patch | 26 | ||||
-rw-r--r-- | external/twain_dsm/fix-non-us-ascii-chars-part2.patch | 44 | ||||
-rw-r--r-- | solenv/gbuild/Executable.mk | 1 | ||||
-rw-r--r-- | solenv/gbuild/LinkTarget.mk | 8 | ||||
-rw-r--r-- | solenv/gbuild/platform/com_MSC_class.mk | 14 |
20 files changed, 1095 insertions, 1145 deletions
diff --git a/Repository.mk b/Repository.mk index c32f93413e8a..4ae85285c7d0 100644 --- a/Repository.mk +++ b/Repository.mk @@ -151,6 +151,7 @@ $(eval $(call gb_Helper_register_executables_for_install,OOO,brand, \ unoinfo \ unopkg \ unopkg_com \ + twain32shim \ ) \ )) @@ -957,7 +958,6 @@ $(eval $(call gb_Helper_register_packages_for_install,ooo,\ )) \ sfx2_classification \ $(if $(filter OPENCL,$(BUILD_TYPE)),sc_opencl_runtimetest) \ - $(if $(and $(filter WNT,$(OS)), $(filter X86_64,$(CPUNAME))),twain_dsm) \ $(if $(ENABLE_HTMLHELP),\ helpcontent2_html_dynamic \ helpcontent2_html_media \ diff --git a/config_host.mk.in b/config_host.mk.in index a74114d0643d..55a4c00c8219 100644 --- a/config_host.mk.in +++ b/config_host.mk.in @@ -52,6 +52,7 @@ export BUILD_TYPE=@BUILD_TYPE@ export BUILD_UNOWINREG=@BUILD_UNOWINREG@ export BUILD_VER_STRING=@BUILD_VER_STRING@ export BUILD_X64=@BUILD_X64@ +export BUILD_X86=@BUILD_X86@ export BZIP2_CFLAGS=$(gb_SPACE)@BZIP2_CFLAGS@ export BZIP2_LIBS=$(gb_SPACE)@BZIP2_LIBS@ export CAIRO_CFLAGS=$(gb_SPACE)@CAIRO_CFLAGS@ @@ -86,6 +87,7 @@ export CUSTOM_BRAND_DIR=@CUSTOM_BRAND_DIR@ export CUSTOM_BRAND_IMAGES=@CUSTOM_BRAND_IMAGES@ export CXX=@CXX@ export CXX_X64_BINARY=@CXX_X64_BINARY@ +export CXX_X86_BINARY=@CXX_X86_BINARY@ @x_CXXFLAGS@ export CXXFLAGS=@CXXFLAGS@ export CXXFLAGS_CXX11=@CXXFLAGS_CXX11@ export DATADIR=@DATADIR@ diff --git a/configure.ac b/configure.ac index b2651e3f1aca..a499c8870cc5 100644 --- a/configure.ac +++ b/configure.ac @@ -3600,6 +3600,29 @@ if test "$_os" = "WINNT"; then # These are passed to the environment and then used in gbuild/platform/com_MSC_class.mk AC_SUBST(CXX_X64_BINARY) AC_SUBST(LINK_X64_BINARY) + + # Check for 32-bit compiler to use to build the 32-bit TWAIN shim + # needed to support TWAIN scan on both 32- and 64-bit systems + + BUILD_X86= + CXX_X86_BINARY= + + if test "$BITNESS_OVERRIDE" = "64"; then + AC_MSG_CHECKING([for a x86 compiler and libraries for 32-bit binaries required for TWAIN support]) + if "$VC_PRODUCT_DIR/Tools/MSVC/$vcbuildnumber/bin/HostX86/x86/cl.exe" -? </dev/null >/dev/null 2>&1; then + BUILD_X86=TRUE + CXX_X86_BINARY="$VC_PRODUCT_DIR/Tools/MSVC/$vcbuildnumber/bin/HostX86/x86/cl.exe -arch:SSE" + AC_MSG_RESULT([found]) + else + AC_MSG_RESULT([not found]) + AC_MSG_WARN([Installation set will not contain 32-bit binaries required for TWAIN support]) + fi + else + BUILD_X86=TRUE + CXX_X86_BINARY=$MSVC_CXX + fi + AC_SUBST(BUILD_X86) + AC_SUBST(CXX_X86_BINARY) fi AC_SUBST(VCVER) AC_SUBST(DEVENV) diff --git a/extensions/Executable_twain32shim.mk b/extensions/Executable_twain32shim.mk new file mode 100644 index 000000000000..29a6e6427198 --- /dev/null +++ b/extensions/Executable_twain32shim.mk @@ -0,0 +1,33 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Executable_Executable,twain32shim)) + +$(eval $(call gb_Executable_set_targettype_gui,twain32shim,YES)) + +$(eval $(call gb_Executable_set_x86,twain32shim,YES)) + +$(eval $(call gb_Executable_use_externals,twain32shim,\ + sane_headers \ +)) + +$(eval $(call gb_Executable_set_include,twain32shim,\ + -I$(SRCDIR)/extensions/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Executable_add_exception_objects,twain32shim,\ + extensions/source/scanner/twain32shim \ +)) + +$(eval $(call gb_Executable_use_system_win32_libs,twain32shim,\ + shell32 \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/extensions/Library_scn.mk b/extensions/Library_scn.mk index 94fbaf0eddb4..71051e112624 100644 --- a/extensions/Library_scn.mk +++ b/extensions/Library_scn.mk @@ -13,7 +13,7 @@ $(eval $(call gb_Library_Library,scn)) $(eval $(call gb_Library_use_externals,scn,\ boost_headers \ - sane_headers \ + $(if $(filter-out WNT,$(OS)),sane_headers) \ )) $(eval $(call gb_Library_set_include,scn,\ @@ -26,15 +26,16 @@ $(eval $(call gb_Library_set_componentfile,scn,extensions/source/scanner/scn)) $(eval $(call gb_Library_use_sdk_api,scn)) $(eval $(call gb_Library_use_libraries,scn,\ - svt \ - vcl \ - tl \ - utl \ comphelper \ - cppuhelper \ cppu \ - sal \ + cppuhelper \ i18nlangtag \ + sal \ + $(if $(filter WNT,$(OS)),salhelper) \ + svt \ + tl \ + utl \ + vcl \ )) $(eval $(call gb_Library_add_exception_objects,scn,\ diff --git a/extensions/Module_extensions.mk b/extensions/Module_extensions.mk index f86235abc224..f84a9ca4fbec 100644 --- a/extensions/Module_extensions.mk +++ b/extensions/Module_extensions.mk @@ -17,9 +17,12 @@ ifneq ($(filter-out iOS ANDROID,$(OS)),) $(eval $(call gb_Module_add_targets,extensions,\ Library_abp \ Library_ldapbe2 \ - $(if $(filter WNT,$(OS)),Library_WinUserInfoBe) \ Library_log \ Library_scn \ + $(if $(filter WNT,$(OS)), \ + Library_WinUserInfoBe \ + $(if $(filter TRUE,$(BUILD_X86)),Executable_twain32shim) \ + ) \ UIConfig_sabpilot \ UIConfig_scanner \ )) diff --git a/extensions/source/scanner/scanwin.cxx b/extensions/source/scanner/scanwin.cxx index dada5ebea5fd..fce8f72577ba 100644 --- a/extensions/source/scanner/scanwin.cxx +++ b/extensions/source/scanner/scanwin.cxx @@ -18,50 +18,23 @@ */ #include <com/sun/star/uno/Reference.hxx> -#include <com/sun/star/util/CloseVetoException.hpp> -#include <com/sun/star/util/XCloseable.hpp> -#include <com/sun/star/util/XCloseBroadcaster.hpp> -#include <com/sun/star/util/XCloseListener.hpp> -#include <com/sun/star/frame/XFrame.hpp> -#include <com/sun/star/frame/Desktop.hpp> -#include <com/sun/star/beans/XPropertySet.hpp> -#include <cppuhelper/implbase.hxx> -#include <comphelper/processfactory.hxx> - -#include <prewin.h> -#include <postwin.h> -#include <math.h> + +#include "twain32shim.hxx" + +#include <config_folders.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <osl/conditn.hxx> +#include <osl/file.hxx> +#include <osl/mutex.hxx> +#include <rtl/bootstrap.hxx> +#include <salhelper/thread.hxx> #include <tools/stream.hxx> #include <tools/helpers.hxx> -#include <osl/mutex.hxx> -#include <osl/module.hxx> -#include <osl/diagnose.h> #include <vcl/svapp.hxx> -#include <vcl/wrkwin.hxx> -#include <vcl/sysdata.hxx> #include "scanner.hxx" -#include <twain/twain.h> - using namespace ::com::sun::star; -#define TWAIN_EVENT_NONE 0x00000000UL -#define TWAIN_EVENT_QUIT 0x00000001UL -#define TWAIN_EVENT_SCANNING 0x00000002UL -#define TWAIN_EVENT_XFER 0x00000004UL - -#define PFUNC (*pDSM) -#define PTWAINMSG MSG* -#define FIXTODOUBLE( nFix ) (static_cast<double>(nFix.Whole)+static_cast<double>(nFix.Frac)/65536.) -#define FIXTOLONG( nFix ) (static_cast<long>(floor(FIXTODOUBLE(nFix)+0.5))) -#define TWAIN_FUNCNAME "DSM_Entry" - -#if defined(TWH_64BIT) -# define TWAIN_LIBNAME "TWAINDSM.DLL" -#else -# define TWAIN_LIBNAME "TWAIN_32.DLL" -#endif - enum TwainState { TWAIN_STATE_NONE = 0, @@ -70,849 +43,534 @@ enum TwainState TWAIN_STATE_CANCELED = 3 }; -static LRESULT CALLBACK TwainMsgProc( int nCode, WPARAM wParam, LPARAM lParam ); - -class ImpTwain : public ::cppu::WeakImplHelper< util::XCloseListener > +struct HANDLEDeleter { - friend LRESULT CALLBACK TwainMsgProc( int nCode, WPARAM wParam, LPARAM lParam ); - - uno::Reference< uno::XInterface > mxSelfRef; - uno::Reference< scanner::XScannerManager > mxMgr; - ScannerManager& mrMgr; - TW_IDENTITY aAppIdent; - TW_IDENTITY aSrcIdent; - Link<unsigned long,void> aNotifyLink; - DSMENTRYPROC pDSM; - osl::Module* pMod; - ULONG_PTR nCurState; - HWND hTwainWnd; - HHOOK hTwainHook; - bool mbCloseFrameOnExit; - - bool ImplHandleMsg( void* pMsg ); - void ImplOpenSourceManager(); - void ImplOpenSource(); - bool ImplEnableSource(); - void ImplXfer(); - void ImplFallback( ULONG_PTR nEvent ); - void ImplDeregisterCloseListener(); - void ImplRegisterCloseListener(); - - DECL_LINK( ImplFallbackHdl, void*, void ); - DECL_LINK( ImplDestroyHdl, void*, void ); - - // from util::XCloseListener - virtual void SAL_CALL queryClosing( const lang::EventObject& Source, sal_Bool GetsOwnership ) override; - virtual void SAL_CALL notifyClosing( const lang::EventObject& Source ) override; - - // from lang::XEventListener - virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; - -public: - - ImpTwain( ScannerManager& rMgr, const Link<unsigned long,void>& rNotifyLink ); - ~ImpTwain() override; - - void Destroy(); - - bool SelectSource(); - bool InitXfer(); + using pointer = HANDLE; + void operator()(HANDLE h) { CloseHandle(h); } }; -static ImpTwain* pImpTwainInstance = nullptr; - -static LRESULT CALLBACK TwainWndProc( HWND hWnd,UINT nMsg, WPARAM nPar1, LPARAM nPar2 ) -{ - return DefWindowProcW( hWnd, nMsg, nPar1, nPar2 ); -} - -LRESULT CALLBACK TwainMsgProc( int nCode, WPARAM wParam, LPARAM lParam ) -{ - MSG* pMsg = reinterpret_cast<MSG*>(lParam); - - if( ( nCode < 0 ) || ( pImpTwainInstance->hTwainWnd != pMsg->hwnd ) || !pImpTwainInstance->ImplHandleMsg( reinterpret_cast<void*>(lParam) ) ) - { - return CallNextHookEx( pImpTwainInstance->hTwainHook, nCode, wParam, lParam ); - } - else - { - pMsg->message = WM_USER; - pMsg->lParam = 0; - - return 0; - } -} +using ScopedHANDLE = std::unique_ptr<HANDLE, HANDLEDeleter>; -namespace { - -uno::Reference< frame::XFrame > ImplGetActiveFrame() +class Twain { - try + friend class ShimListenerThread; + class ShimListenerThread : public salhelper::Thread { - // query desktop instance - uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); - - uno::Reference< frame::XFrame > xActiveFrame = xDesktop->getActiveFrame(); - - if( xActiveFrame.is() ) + public: + ShimListenerThread(Twain& rOwner) + : salhelper::Thread("TWAINShimListenerThread") + , mrOwner(rOwner) { - return xActiveFrame; } - } - catch( const uno::Exception& ) - { - } + void execute() override; + const OUString& getError() { return msErrorReported; } + + // These methods are executed outside of own thread + bool WaitInitialization(); + bool WaitRequestResult(); + void DontNotify() { mbDontNotify = true; } + void RequestDestroy(); + bool RequestSelectSource(); + bool RequestInitXfer(); + + private: + Twain& mrOwner; + bool mbDontNotify = false; + HWND mhWndShim = nullptr; // shim main window handle + OUString msErrorReported; + osl::Condition mcInitCompleted; // initially not set + bool mbInitSucceeded = false; + osl::Condition mcGotRequestResult; + bool mbRequestResult = false; + + void SendShimRequest(WPARAM nRequest); + bool SendShimRequestWithResult(WPARAM nRequest); + void NotificationHdl(WPARAM nEvent, LPARAM lParam); + void NotifyOwner(WPARAM nEvent); + void NotifyXFerOwner(LPARAM nHandle); + }; + uno::Reference<lang::XEventListener> mxListener; + uno::Reference<scanner::XScannerManager> mxMgr; + ScannerManager* mpCurMgr = nullptr; + TwainState meState = TWAIN_STATE_NONE; + rtl::Reference<ShimListenerThread> mpThread; + osl::Mutex maMutex; + + DECL_LINK(ImpNotifyHdl, void*, void); + DECL_LINK(ImpNotifyXferHdl, void*, void); + void Notify(WPARAM nEvent); // called by shim communication thread to notify me + void NotifyXFer(LPARAM nHandle); // called by shim communication thread to notify me + + bool InitializeNewShim(ScannerManager& rMgr); + + void Reset(); // cleanup thread and manager - OSL_FAIL("ImpTwain::ImplGetActiveFrame: Could not determine active frame!"); - return uno::Reference< frame::XFrame >(); -} +public: + Twain(); + ~Twain(); -uno::Reference< util::XCloseBroadcaster > ImplGetActiveFrameCloseBroadcaster() -{ - try - { - return uno::Reference< util::XCloseBroadcaster >( ImplGetActiveFrame(), uno::UNO_QUERY ); - } - catch( const uno::Exception& ) - { - } + bool SelectSource(ScannerManager& rMgr); + bool PerformTransfer(ScannerManager& rMgr, + const uno::Reference<lang::XEventListener>& rxListener); - OSL_FAIL("ImpTwain::ImplGetActiveFrameCloseBroadcaster: Could determine close broadcaster on active frame!"); - return uno::Reference< util::XCloseBroadcaster >(); -} + TwainState GetState() const { return meState; } +}; -void ImplSendCloseEvent() +bool Twain::ShimListenerThread::WaitInitialization() { - try - { - uno::Reference< util::XCloseable > xCloseable( ImplGetActiveFrame(), uno::UNO_QUERY ); - - if( xCloseable.is() ) - xCloseable->close( true ); - } - catch( const uno::Exception& ) - { - } - - OSL_FAIL("ImpTwain::ImplSendCloseEvent: Could not send required close broadcast!"); -} - + mcInitCompleted.wait(); + return mbInitSucceeded; } -// #107835# hold reference to ScannerManager, to prevent premature death -ImpTwain::ImpTwain( ScannerManager& rMgr, const Link<unsigned long,void>& rNotifyLink ) : - mxMgr( uno::Reference< scanner::XScannerManager >( static_cast< OWeakObject* >( &rMgr ), uno::UNO_QUERY) ), - mrMgr( rMgr ), - aNotifyLink( rNotifyLink ), - pDSM( nullptr ), - pMod( nullptr ), - nCurState( 1 ), - hTwainWnd( nullptr ), - hTwainHook( nullptr ), - mbCloseFrameOnExit( false ) +bool Twain::ShimListenerThread::WaitRequestResult() { - // setup TWAIN window - pImpTwainInstance = this; - - aAppIdent.Id = 0; - aAppIdent.Version.MajorNum = 1; - aAppIdent.Version.MinorNum = 0; - aAppIdent.Version.Language = TWLG_USA; - aAppIdent.Version.Country = TWCY_USA; - aAppIdent.ProtocolMajor = TWON_PROTOCOLMAJOR; - aAppIdent.ProtocolMinor = TWON_PROTOCOLMINOR; - aAppIdent.SupportedGroups = DG_IMAGE | DG_CONTROL; - strncpy( aAppIdent.Version.Info, "8.0", 32 ); - aAppIdent.Version.Info[32] = aAppIdent.Version.Info[33] = 0; - strncpy( aAppIdent.Manufacturer, "Sun Microsystems", 32 ); - aAppIdent.Manufacturer[32] = aAppIdent.Manufacturer[33] = 0; - strncpy( aAppIdent.ProductFamily,"Office", 32 ); - aAppIdent.ProductFamily[32] = aAppIdent.ProductFamily[33] = 0; - strncpy( aAppIdent.ProductName, "Office", 32 ); - aAppIdent.ProductName[32] = aAppIdent.ProductName[33] = 0; - - WNDCLASSW aWc = { 0, &TwainWndProc, 0, sizeof( WNDCLASSW ), GetModuleHandleW( nullptr ), nullptr, nullptr, nullptr, nullptr, L"TwainClass" }; - RegisterClassW( &aWc ); - - hTwainWnd = CreateWindowExW( WS_EX_TOPMOST, aWc.lpszClassName, L"TWAIN", 0, 0, 0, 0, 0, HWND_DESKTOP, nullptr, aWc.hInstance, nullptr ); - hTwainHook = SetWindowsHookExW( WH_GETMESSAGE, &TwainMsgProc, nullptr, GetCurrentThreadId() ); - - // block destruction until ImplDestroyHdl is called - mxSelfRef = static_cast< ::cppu::OWeakObject* >( this ); + mcGotRequestResult.wait(); + return mbRequestResult; } -ImpTwain::~ImpTwain() -{ - // are we responsible for application shutdown? - if( mbCloseFrameOnExit ) - ImplSendCloseEvent(); -} +void Twain::ShimListenerThread::RequestDestroy() { SendShimRequest(TWAIN_REQUEST_QUIT); } -void ImpTwain::Destroy() +bool Twain::ShimListenerThread::RequestSelectSource() { - ImplFallback( TWAIN_EVENT_NONE ); - Application::PostUserEvent( LINK( this, ImpTwain, ImplDestroyHdl ) ); + assert(mbInitSucceeded); + return SendShimRequestWithResult(TWAIN_REQUEST_SELECTSOURCE); } -bool ImpTwain::SelectSource() +bool Twain::ShimListenerThread::RequestInitXfer() { - TW_UINT16 nRet = TWRC_FAILURE; - - ImplOpenSourceManager(); - - if( 3 == nCurState ) - { - TW_IDENTITY aIdent; - - aIdent.Id = 0; - aIdent.ProductName[ 0 ] = '\0'; - aNotifyLink.Call( TWAIN_EVENT_SCANNING ); - nRet = PFUNC( &aAppIdent, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, &aIdent ); - } - - ImplFallback( TWAIN_EVENT_QUIT ); - - return( TWRC_SUCCESS == nRet ); + assert(mbInitSucceeded); + return SendShimRequestWithResult(TWAIN_REQUEST_INITXFER); } -bool ImpTwain::InitXfer() +void Twain::ShimListenerThread::SendShimRequest(WPARAM nRequest) { - bool bRet = false; - - ImplOpenSourceManager(); - - if( 3 == nCurState ) - { - ImplOpenSource(); - - if( 4 == nCurState ) - bRet = ImplEnableSource(); - } - - if( !bRet ) - ImplFallback( TWAIN_EVENT_QUIT ); - - return bRet; + if (mhWndShim) + PostMessageW(mhWndShim, WM_TWAIN_REQUEST, nRequest, 0); } -void ImpTwain::ImplOpenSourceManager() +bool Twain::ShimListenerThread::SendShimRequestWithResult(WPARAM nRequest) { - if( 1 == nCurState ) - { - pMod = new ::osl::Module( OUString() ); - - if( pMod->load( TWAIN_LIBNAME ) ) - { - nCurState = 2; - - pDSM = reinterpret_cast<DSMENTRYPROC>(pMod->getSymbol(TWAIN_FUNCNAME)); - if (pDSM && - ( PFUNC( &aAppIdent, nullptr, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, &hTwainWnd ) == TWRC_SUCCESS ) ) - { - nCurState = 3; - } - } - else - { - delete pMod; - pMod = nullptr; - } - } + mcGotRequestResult.reset(); + mbRequestResult = false; + SendShimRequest(nRequest); + return WaitRequestResult(); } -void ImpTwain::ImplOpenSource() +void Twain::ShimListenerThread::NotifyOwner(WPARAM nEvent) { - if( 3 == nCurState ) - { - if( ( PFUNC( &aAppIdent, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, &aSrcIdent ) == TWRC_SUCCESS ) && - ( PFUNC( &aAppIdent, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, &aSrcIdent ) == TWRC_SUCCESS ) ) - { - TW_CAPABILITY aCap = { CAP_XFERCOUNT, TWON_ONEVALUE, GlobalAlloc( GHND, sizeof( TW_ONEVALUE ) ) }; - TW_ONEVALUE* pVal = static_cast<TW_ONEVALUE*>(GlobalLock( aCap.hContainer )); - - pVal->ItemType = TWTY_INT16; - pVal->Item = 1; - GlobalUnlock( aCap.hContainer ); - PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &aCap ); - GlobalFree( aCap.hContainer ); - nCurState = 4; - } - } + if (!mbDontNotify) + mrOwner.Notify(nEvent); } -bool ImpTwain::ImplEnableSource() +void Twain::ShimListenerThread::NotifyXFerOwner(LPARAM nHandle) { - bool bRet = false; - - if( 4 == nCurState ) - { - TW_USERINTERFACE aUI = { true, true, hTwainWnd }; - - aNotifyLink.Call( TWAIN_EVENT_SCANNING ); - nCurState = 5; - - // register as vetoable close listener, to prevent application to die under us - ImplRegisterCloseListener(); - - if( PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, &aUI ) == TWRC_SUCCESS ) - { - bRet = true; - } - else - { - nCurState = 4; - - // deregister as vetoable close listener, dialog failed - ImplDeregisterCloseListener(); - } - } - - return bRet; + if (!mbDontNotify) + mrOwner.NotifyXFer(nHandle); } -bool ImpTwain::ImplHandleMsg( void* pMsg ) +// May only be called from the own thread, so no threading issues modifying self +void Twain::ShimListenerThread::NotificationHdl(WPARAM nEvent, LPARAM lParam) { - TW_UINT16 nRet; - PTWAINMSG pMess = static_cast<PTWAINMSG>(pMsg); - TW_EVENT aEvt = { pMess, MSG_NULL }; - - if (pDSM) - nRet = PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, &aEvt ); - else - nRet = TWRC_NOTDSEVENT; - - if( aEvt.TWMessage != MSG_NULL ) + switch (nEvent) { - switch( aEvt.TWMessage ) - { - case MSG_XFERREADY: + case TWAIN_EVENT_NOTIFYHWND: // shim reported its main HWND for communications + if (!mcInitCompleted.check()) // only if not yet initialized! { - ULONG_PTR nEvent = TWAIN_EVENT_QUIT; - - if( 5 == nCurState ) - { - nCurState = 6; - ImplXfer(); + // Owner is still waiting mcInitCompleted in its Twain::InitializeNewShim, + // holding its access mutex + mhWndShim = reinterpret_cast<HWND>(lParam); - if( mrMgr.GetData() ) - nEvent = TWAIN_EVENT_XFER; - } - else if( 7 == nCurState && mrMgr.GetData() ) - { - // Already sent TWAIN_EVENT_XFER; not processed yet; - // duplicate event - avoid deleting mpImpTwain - nEvent = TWAIN_EVENT_NONE; - } - - ImplFallback( nEvent ); + mbInitSucceeded = lParam != 0; + mcInitCompleted.set(); } break; - - case MSG_CLOSEDSREQ: - ImplFallback( TWAIN_EVENT_QUIT ); + case TWAIN_EVENT_SCANNING: + NotifyOwner(nEvent); break; - - default: + case TWAIN_EVENT_XFER: + NotifyXFerOwner(lParam); break; - } + case TWAIN_EVENT_REQUESTRESULT: + mbRequestResult = lParam; + mcGotRequestResult.set(); + break; + // We don't handle TWAIN_EVENT_QUIT notification from shim, because we send it ourselves + // in the end of execute() } - else - nRet = TWRC_NOTDSEVENT; - - return( TWRC_DSEVENT == nRet ); } -void ImpTwain::ImplXfer() +// Spawn a separate 32-bit process to use TWAIN on Windows, and listen for its notifications +void Twain::ShimListenerThread::execute() { - if( nCurState == 6 ) - { - TW_IMAGEINFO aInfo; - HANDLE hDIB = nullptr; - long nWidth, nHeight, nXRes, nYRes; + MSG msg; + // Initialize thread message queue before launching shim process + PeekMessageW(&msg, 0, 0, 0, PM_NOREMOVE); - if( PFUNC( &aAppIdent, &aSrcIdent, DG_IMAGE, DAT_IMAGEINFO, MSG_GET, &aInfo ) == TWRC_SUCCESS ) + try + { + ScopedHANDLE hShimProcess; { - nWidth = aInfo.ImageWidth; - nHeight = aInfo.ImageLength; - nXRes = FIXTOLONG( aInfo.XResolution ); - nYRes = FIXTOLONG( aInfo.YResolution ); + // Determine twain32shim executable URL: + OUString shimURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/twain32shim.exe"); + rtl::Bootstrap::expandMacros(shimURL); + + OUString sCmdLine; + if (osl::FileBase::getSystemPathFromFileURL(shimURL, sCmdLine) != osl_File_E_None) + throw std::exception("getSystemPathFromFileURL failed!"); + + HANDLE hDup; + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), + &hDup, SYNCHRONIZE | THREAD_QUERY_LIMITED_INFORMATION, TRUE, 0)) + ThrowLastError("DuplicateHandle"); + // we will not need our copy as soon as shim has its own inherited one + ScopedHANDLE hScopedDup(hDup); + DWORD nDup = reinterpret_cast<DWORD>(hDup); + if (reinterpret_cast<HANDLE>(nDup) != hDup) + throw std::exception("HANDLE does not fit to 32 bit - cannot pass to shim!"); + + // Send this thread handle as the first parameter + sCmdLine = "\"" + sCmdLine + "\" " + OUString::number(nDup); + + // We need a WinAPI HANDLE of the process to be able to wait on it and detect the process + // termination; so use WinAPI to start the process, not osl_executeProcess. + + STARTUPINFOW si{}; // null-initialize + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + PROCESS_INFORMATION pi; + + if (!CreateProcessW(nullptr, const_cast<LPWSTR>(o3tl::toW(sCmdLine.getStr())), nullptr, + nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) + ThrowLastError("CreateProcessW"); + + CloseHandle(pi.hThread); + hShimProcess.reset(pi.hProcess); } - else - nWidth = nHeight = nXRes = nYRes = -1; - - switch( PFUNC( &aAppIdent, &aSrcIdent, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, &hDIB ) ) + HANDLE h = hShimProcess.get(); + while (true) { - case TWRC_CANCEL: - nCurState = 7; - break; - - case TWRC_XFERDONE: + DWORD nWaitResult = MsgWaitForMultipleObjects(1, &h, FALSE, INFINITE, + QS_ALLPOSTMESSAGE | QS_SENDMESSAGE); + // Process any messages in queue before checking if we need to break, to not loose + // possible pending notifications + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { - if( hDIB ) + // process it here + if (msg.message == WM_TWAIN_EVENT) { - if( ( nXRes != -1 ) && ( nYRes != - 1 ) && ( nWidth != - 1 ) && ( nHeight != - 1 ) ) - { - // set resolution of bitmap - BITMAPINFOHEADER* pBIH = static_cast<BITMAPINFOHEADER*>(GlobalLock( static_cast<HGLOBAL>(hDIB) )); - static const double fFactor = 100.0 / 2.54; - - pBIH->biXPelsPerMeter = FRound( fFactor * nXRes ); - pBIH->biYPelsPerMeter = FRound( fFactor * nYRes ); - - GlobalUnlock( static_cast<HGLOBAL>(hDIB) ); - } - - mrMgr.SetData( static_cast<void*>(hDIB) ); + NotificationHdl(msg.wParam, msg.lParam); } - else - GlobalFree( static_cast<HGLOBAL>(hDIB) ); - - nCurState = 7; } - break; - - default: - break; - } - } -} - -void ImpTwain::ImplFallback( ULONG_PTR nEvent ) -{ - Application::PostUserEvent( LINK( this, ImpTwain, ImplFallbackHdl ), reinterpret_cast<void*>(nEvent) ); -} - -IMPL_LINK( ImpTwain, ImplFallbackHdl, void*, pData, void ) -{ - const sal_uIntPtr nEvent = reinterpret_cast<sal_uIntPtr>(pData); - bool bFallback = true; - - switch( nCurState ) - { - case 7: - case 6: - { - TW_PENDINGXFERS aXfers; - - if( PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER, &aXfers ) == TWRC_SUCCESS ) + if (nWaitResult == WAIT_OBJECT_0) { - if( aXfers.Count != 0 ) - PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, &aXfers ); + // shim process exited - return + break; + } + if (nWaitResult == WAIT_FAILED) + { + // Some Win32 error - report and return + ThrowLastError("MsgWaitForMultipleObjects"); } - - nCurState = 5; - } - break; - - case 5: - { - TW_USERINTERFACE aUI = { true, true, hTwainWnd }; - - PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, &aUI ); - nCurState = 4; - - // deregister as vetoable close listener - ImplDeregisterCloseListener(); - } - break; - - case 4: - { - PFUNC( &aAppIdent, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, &aSrcIdent ); - nCurState = 3; - } - break; - - case 3: - { - PFUNC( &aAppIdent, nullptr, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, &hTwainWnd ); - nCurState = 2; - } - break; - - case 2: - { - delete pMod; - pMod = nullptr; - nCurState = 1; - } - break; - - default: - { - if( nEvent != TWAIN_EVENT_NONE ) - aNotifyLink.Call( nEvent ); - - bFallback = false; - } - break; - } - - if( bFallback ) - ImplFallback( nEvent ); -} - -IMPL_LINK_NOARG( ImpTwain, ImplDestroyHdl, void*, void ) -{ - if( hTwainWnd ) - DestroyWindow( hTwainWnd ); - - if( hTwainHook ) - UnhookWindowsHookEx( hTwainHook ); - - // permit destruction of ourselves (normally, refcount - // should drop to zero exactly here) - mxSelfRef = nullptr; - pImpTwainInstance = nullptr; -} - -void ImpTwain::ImplRegisterCloseListener() -{ - try - { - uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( ImplGetActiveFrameCloseBroadcaster() ); - - if( xCloseBroadcaster.is() ) - { - xCloseBroadcaster->addCloseListener(this); - return; // successfully registered as a close listener - } - else - { - // interface unknown. don't register, then - OSL_FAIL("ImpTwain::ImplRegisterCloseListener: XFrame has no XCloseBroadcaster!"); - return; } } - catch( const uno::Exception& ) + catch (const std::exception& e) { + msErrorReported = OUString(e.what(), strlen(e.what()), RTL_TEXTENCODING_UTF8); + // allow owner to resume (in case the condition isn't set yet) + mcInitCompleted.set(); // let mbInitSucceeded keep its (maybe false) value! } - - OSL_FAIL("ImpTwain::ImplRegisterCloseListener: Could not register as close listener!"); + // allow owner to resume (in case the conditions isn't set yet) + mcGotRequestResult.set(); + NotifyOwner(TWAIN_EVENT_QUIT); } -void ImpTwain::ImplDeregisterCloseListener() -{ - try - { - uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( - ImplGetActiveFrameCloseBroadcaster() ); +Twain::Twain() {} - if( xCloseBroadcaster.is() ) - { - xCloseBroadcaster->removeCloseListener(this); - return; // successfully deregistered as a close listener - } - else - { - // interface unknown. don't deregister, then - OSL_FAIL("ImpTwain::ImplDeregisterCloseListener: XFrame has no XCloseBroadcaster!"); - return; - } - } - catch( const uno::Exception& ) +Twain::~Twain() +{ + osl::MutexGuard aGuard(maMutex); + if (mpThread) { + mpThread->DontNotify(); + mpThread->RequestDestroy(); + mpThread->join(); + mpThread.clear(); } - - OSL_FAIL("ImpTwain::ImplDeregisterCloseListener: Could not deregister as close listener!"); -} - -void SAL_CALL ImpTwain::queryClosing( const lang::EventObject& /*Source*/, sal_Bool GetsOwnership ) -{ - // shall we re-send the close query later on? - mbCloseFrameOnExit = GetsOwnership; - - // the sole purpose of this listener is to forbid closing of the listened-at frame - throw util::CloseVetoException(); -} - -void SAL_CALL ImpTwain::notifyClosing( const lang::EventObject& /*Source*/ ) -{ - // should not happen - OSL_FAIL("ImpTwain::notifyClosing called, but we vetoed the closing before!"); } -void SAL_CALL ImpTwain::disposing( const lang::EventObject& /*Source*/ ) +void Twain::Reset() { - // we're not holding any references to the frame, thus noop + mpThread->join(); + if (!mpThread->getError().isEmpty()) + SAL_WARN("extensions.scanner", mpThread->getError()); + mpThread.clear(); + mpCurMgr = nullptr; + mxMgr.clear(); } -class Twain +bool Twain::InitializeNewShim(ScannerManager& rMgr) { - uno::Reference< lang::XEventListener > mxListener; - uno::Reference< scanner::XScannerManager > mxMgr; - const ScannerManager* mpCurMgr; - ImpTwain* mpImpTwain; - TwainState meState; - - DECL_LINK( ImpNotifyHdl, unsigned long, void ); + osl::MutexGuard aGuard(maMutex); + if (mpThread) + return false; // Have a shim for another task already! -public: - - Twain(); - ~Twain(); + // hold reference to ScannerManager, to prevent premature death + mxMgr.set(static_cast<OWeakObject*>(const_cast<ScannerManager*>(mpCurMgr = &rMgr)), + uno::UNO_QUERY); - bool SelectSource( ScannerManager& rMgr ); - bool PerformTransfer( ScannerManager& rMgr, const uno::Reference< lang::XEventListener >& rxListener ); + mpThread.set(new ShimListenerThread(*this)); + mpThread->launch(); + const bool bSuccess = mpThread->WaitInitialization(); + if (!bSuccess) + Reset(); - TwainState GetState() const { return meState; } -}; + return bSuccess; +} -Twain::Twain() : - mpCurMgr( nullptr ), - mpImpTwain( nullptr ), - meState( TWAIN_STATE_NONE ) +void Twain::Notify(WPARAM nEvent) { + Application::PostUserEvent(LINK(this, Twain, ImpNotifyHdl), reinterpret_cast<void*>(nEvent)); } -Twain::~Twain() +void Twain::NotifyXFer(LPARAM nHandle) { - if( mpImpTwain ) - mpImpTwain->Destroy(); + Application::PostUserEvent(LINK(this, Twain, ImpNotifyXferHdl), + reinterpret_cast<void*>(nHandle)); } -bool Twain::SelectSource( ScannerManager& rMgr ) +bool Twain::SelectSource(ScannerManager& rMgr) { - bool bRet; + osl::MutexGuard aGuard(maMutex); + bool bRet = false; - if( !mpImpTwain ) + if (InitializeNewShim(rMgr)) { - // hold reference to ScannerManager, to prevent premature death - mxMgr.set( static_cast< OWeakObject* >( const_cast< ScannerManager* >( mpCurMgr = &rMgr ) ), - uno::UNO_QUERY ); - meState = TWAIN_STATE_NONE; - mpImpTwain = new ImpTwain( rMgr, LINK( this, Twain, ImpNotifyHdl ) ); - bRet = mpImpTwain->SelectSource(); + bRet = mpThread->RequestSelectSource(); } - else - bRet = false; return bRet; } -bool Twain::PerformTransfer( ScannerManager& rMgr, const uno::Reference< lang::XEventListener >& rxListener ) +bool Twain::PerformTransfer(ScannerManager& rMgr, + const uno::Reference<lang::XEventListener>& rxListener) { - bool bRet; + osl::MutexGuard aGuard(maMutex); + bool bRet = false; - if( !mpImpTwain ) + if (InitializeNewShim(rMgr)) { - // hold reference to ScannerManager, to prevent premature death - mxMgr.set( static_cast< OWeakObject* >( const_cast< ScannerManager* >( mpCurMgr = &rMgr ) ), - uno::UNO_QUERY ); - mxListener = rxListener; meState = TWAIN_STATE_NONE; - mpImpTwain = new ImpTwain( rMgr, LINK( this, Twain, ImpNotifyHdl ) ); - bRet = mpImpTwain->InitXfer(); + bRet = mpThread->RequestInitXfer(); } - else - bRet = false; return bRet; } -IMPL_LINK( Twain, ImpNotifyHdl, unsigned long, nEvent, void ) +IMPL_LINK(Twain, ImpNotifyHdl, void*, pParam, void) { - switch( nEvent ) + osl::MutexGuard aGuard(maMutex); + WPARAM nEvent = reinterpret_cast<WPARAM>(pParam); + switch (nEvent) { case TWAIN_EVENT_SCANNING: meState = TWAIN_STATE_SCANNING; - break; + break; case TWAIN_EVENT_QUIT: { - if( meState != TWAIN_STATE_DONE ) + if (meState != TWAIN_STATE_DONE) meState = TWAIN_STATE_CANCELED; - if( mpImpTwain ) - { - mpImpTwain->Destroy(); - mpImpTwain = nullptr; - mpCurMgr = nullptr; - } + lang::EventObject event(mxMgr); // mxMgr will be cleared below - if( mxListener.is() ) - mxListener->disposing( lang::EventObject( mxMgr ) ); + if (mpThread) + Reset(); - mxListener = nullptr; + if (mxListener.is()) + { + mxListener->disposing(event); + mxListener.clear(); + } } break; - case TWAIN_EVENT_XFER: - { - if( mpImpTwain ) - { - meState = ( mpCurMgr->GetData() ? TWAIN_STATE_DONE : TWAIN_STATE_CANCELED ); + default: + break; + } +} - mpImpTwain->Destroy(); - mpImpTwain = nullptr; - mpCurMgr = nullptr; +IMPL_LINK(Twain, ImpNotifyXferHdl, void*, pParam, void) +{ + osl::MutexGuard aGuard(maMutex); + if (mpThread) + { + mpCurMgr->SetData(pParam); + meState = pParam ? TWAIN_STATE_DONE : TWAIN_STATE_CANCELED; - if( mxListener.is() ) - mxListener->disposing( lang::EventObject( mxMgr ) ); - } + lang::EventObject event(mxMgr); // mxMgr will be cleared below - mxListener = nullptr; - } - break; + Reset(); - default: - break; + if (mxListener.is()) + mxListener->disposing(lang::EventObject(mxMgr)); } + + mxListener.clear(); } static Twain aTwain; -void ScannerManager::AcquireData() -{ -} +void ScannerManager::AcquireData() {} void ScannerManager::ReleaseData() { - if( mpData ) + if (mpData) { - GlobalFree( static_cast<HGLOBAL>(mpData) ); + CloseHandle(static_cast<HANDLE>(mpData)); mpData = nullptr; } } awt::Size ScannerManager::getSize() { - awt::Size aRet; - HGLOBAL hDIB = static_cast<HGLOBAL>(mpData); + awt::Size aRet; - if( hDIB ) + if (mpData) { - BITMAPINFOHEADER* pBIH = static_cast<BITMAPINFOHEADER*>(GlobalLock( hDIB )); - - if( pBIH ) + HANDLE hMap = static_cast<HANDLE>(mpData); + // map full size + const sal_Int8* pMap = static_cast<sal_Int8*>(MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)); + if (pMap) { + const BITMAPINFOHEADER* pBIH = reinterpret_cast<const BITMAPINFOHEADER*>(pMap + 4); aRet.Width = pBIH->biWidth; aRet.Height = pBIH->biHeight; - } - else - aRet.Width = aRet.Height = 0; - GlobalUnlock( hDIB ); + UnmapViewOfFile(pMap); + } } - else - aRet.Width = aRet.Height = 0; return aRet; } -uno::Sequence< sal_Int8 > ScannerManager::getDIB() +uno::Sequence<sal_Int8> ScannerManager::getDIB() { - uno::Sequence< sal_Int8 > aRet; + uno::Sequence<sal_Int8> aRet; - if( mpData ) + if (mpData) { - HGLOBAL hDIB = static_cast<HGLOBAL>(mpData); - const sal_uInt32 nDIBSize = GlobalSize( hDIB ); - BITMAPINFOHEADER* pBIH = static_cast<BITMAPINFOHEADER*>(GlobalLock( hDIB )); - - if( pBIH ) + HANDLE hMap = static_cast<HANDLE>(mpData); + // map full size + const sal_Int8* pMap = static_cast<sal_Int8*>(MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)); + if (pMap) { - sal_uInt32 nColEntries; + DWORD nDIBSize; + memcpy(&nDIBSize, pMap, 4); // size of the following DIB - switch( pBIH->biBitCount ) + const BITMAPINFOHEADER* pBIH = reinterpret_cast<const BITMAPINFOHEADER*>(pMap + 4); + + sal_uInt32 nColEntries = 0; + + switch (pBIH->biBitCount) { case 1: case 4: case 8: - nColEntries = pBIH->biClrUsed ? pBIH->biClrUsed : ( 1 << pBIH->biBitCount ); - break; + nColEntries = pBIH->biClrUsed ? pBIH->biClrUsed : (1 << pBIH->biBitCount); + break; case 24: nColEntries = pBIH->biClrUsed ? pBIH->biClrUsed : 0; - break; + break; case 16: case 32: - { nColEntries = pBIH->biClrUsed; - - if( pBIH->biCompression == BI_BITFIELDS ) + if (pBIH->biCompression == BI_BITFIELDS) nColEntries += 3; - } - break; - - default: - nColEntries = 0; - break; + break; } - aRet = uno::Sequence< sal_Int8 >( sizeof( BITMAPFILEHEADER ) + nDIBSize ); + aRet = uno::Sequence<sal_Int8>(sizeof(BITMAPFILEHEADER) + nDIBSize); - sal_Int8* pBuf = aRet.getArray(); - SvMemoryStream* pMemStm = new SvMemoryStream( pBuf, sizeof( BITMAPFILEHEADER ), StreamMode::WRITE ); + sal_Int8* pBuf = aRet.getArray(); + SvMemoryStream* pMemStm + = new SvMemoryStream(pBuf, sizeof(BITMAPFILEHEADER), StreamMode::WRITE); - pMemStm->WriteChar( 'B' ).WriteChar( 'M' ).WriteUInt32( 0 ).WriteUInt32( 0 ); - pMemStm->WriteUInt32( sizeof( BITMAPFILEHEADER ) + pBIH->biSize + ( nColEntries * sizeof( RGBQUAD ) ) ); + pMemStm->WriteChar('B').WriteChar('M').WriteUInt32(0).WriteUInt32(0); + pMemStm->WriteUInt32(sizeof(BITMAPFILEHEADER) + pBIH->biSize + + (nColEntries * sizeof(RGBQUAD))); delete pMemStm; - memcpy( pBuf + sizeof( BITMAPFILEHEADER ), pBIH, nDIBSize ); + memcpy(pBuf + sizeof(BITMAPFILEHEADER), pBIH, nDIBSize); + + UnmapViewOfFile(pMap); } - GlobalUnlock( hDIB ); ReleaseData(); } return aRet; } -uno::Sequence< ScannerContext > SAL_CALL ScannerManager::getAvailableScanners() +uno::Sequence<ScannerContext> SAL_CALL ScannerManager::getAvailableScanners() { - osl::MutexGuard aGuard( maProtector ); - uno::Sequence< ScannerContext > aRet( 1 ); + osl::MutexGuard aGuard(maProtector); + uno::Sequence<ScannerContext> aRet(1); - aRet.getArray()[0].ScannerName = "TWAIN" ; + aRet.getArray()[0].ScannerName = "TWAIN"; aRet.getArray()[0].InternalData = 0; return aRet; } -sal_Bool SAL_CALL ScannerManager::configureScannerAndScan( ScannerContext& rContext, const uno::Reference< lang::XEventListener >& ) +sal_Bool SAL_CALL ScannerManager::configureScannerAndScan( + ScannerContext& rContext, const uno::Reference<lang::XEventListener>&) { - osl::MutexGuard aGuard( maProtector ); - uno::Reference< XScannerManager > xThis( this ); + osl::MutexGuard aGuard(maProtector); + uno::Reference<XScannerManager> xThis(this); - if( rContext.InternalData != 0 || rContext.ScannerName != "TWAIN" ) - throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext ); + if (rContext.InternalData != 0 || rContext.ScannerName != "TWAIN") + throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext); ReleaseData(); - return aTwain.SelectSource( *this ); + return aTwain.SelectSource(*this); } -void SAL_CALL ScannerManager::startScan( const ScannerContext& rContext, const uno::Reference< lang::XEventListener >& rxListener ) +void SAL_CALL ScannerManager::startScan(const ScannerContext& rContext, + const uno::Reference<lang::XEventListener>& rxListener) { - osl::MutexGuard aGuard( maProtector ); - uno::Reference< XScannerManager > xThis( this ); + osl::MutexGuard aGuard(maProtector); + uno::Reference<XScannerManager> xThis(this); - if( rContext.InternalData != 0 || rContext.ScannerName != "TWAIN" ) - throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext ); + if (rContext.InternalData != 0 || rContext.ScannerName != "TWAIN") + throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext); ReleaseData(); - aTwain.PerformTransfer( *this, rxListener ); + aTwain.PerformTransfer(*this, rxListener); } -ScanError SAL_CALL ScannerManager::getError( const ScannerContext& rContext ) +ScanError SAL_CALL ScannerManager::getError(const ScannerContext& rContext) { - osl::MutexGuard aGuard( maProtector ); - uno::Reference< XScannerManager > xThis( this ); + osl::MutexGuard aGuard(maProtector); + uno::Reference<XScannerManager> xThis(this); - if( rContext.InternalData != 0 || rContext.ScannerName != "TWAIN" ) - throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext ); + if (rContext.InternalData != 0 || rContext.ScannerName != "TWAIN") + throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext); - return( ( aTwain.GetState() == TWAIN_STATE_CANCELED ) ? ScanError_ScanCanceled : ScanError_ScanErrorNone ); + return ((aTwain.GetState() == TWAIN_STATE_CANCELED) ? ScanError_ScanCanceled + : ScanError_ScanErrorNone); } -uno::Reference< awt::XBitmap > SAL_CALL ScannerManager::getBitmap( const ScannerContext& /*rContext*/ ) +uno::Reference<awt::XBitmap> SAL_CALL ScannerManager::getBitmap(const ScannerContext& /*rContext*/) { - osl::MutexGuard aGuard( maProtector ); - return uno::Reference< awt::XBitmap >( this ); + osl::MutexGuard aGuard(maProtector); + return uno::Reference<awt::XBitmap>(this); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/scanner/twain32shim.cxx b/extensions/source/scanner/twain32shim.cxx new file mode 100644 index 000000000000..c5a4c0b3fa6f --- /dev/null +++ b/extensions/source/scanner/twain32shim.cxx @@ -0,0 +1,598 @@ +/* -*- 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 <tools/helpers.hxx> +#include <twain/twain.h> + +#define WM_TWAIN_FALLBACK (WM_SHIM_INTERNAL + 0) + +namespace +{ +long FixToLong(const TW_FIX32& rFix) +{ + return static_cast<long>(floor(rFix.Whole + rFix.Frac / 65536. + 0.5)); +} + +const wchar_t sTwainWndClass[] = L"TwainClass"; + +class ImpTwain +{ +public: + ImpTwain(HANDLE hParentThread); + ~ImpTwain(); + +private: + TW_IDENTITY m_aAppId; + TW_IDENTITY m_aSrcId; + DWORD m_nParentThreadId; + HANDLE m_hProc; + DSMENTRYPROC m_pDSM = nullptr; + HMODULE m_hMod = nullptr; + ULONG_PTR m_nCurState = 1; + 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(); + 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(0, 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_nParentThreadId(GetThreadId(hParentThread)) + , m_hProc(GetProcOfThread(hParentThread)) +{ + m_aAppId.Id = 0; + m_aAppId.Version.MajorNum = 1; + m_aAppId.Version.MinorNum = 0; + m_aAppId.Version.Language = TWLG_USA; + m_aAppId.Version.Country = TWCY_USA; + m_aAppId.ProtocolMajor = TWON_PROTOCOLMAJOR; + m_aAppId.ProtocolMinor = TWON_PROTOCOLMINOR; + m_aAppId.SupportedGroups = DG_IMAGE | DG_CONTROL; + strncpy(m_aAppId.Version.Info, "8.0", 32); + m_aAppId.Version.Info[32] = m_aAppId.Version.Info[33] = 0; + strncpy(m_aAppId.Manufacturer, "Sun Microsystems", 32); + m_aAppId.Manufacturer[32] = m_aAppId.Manufacturer[33] = 0; + strncpy(m_aAppId.ProductFamily, "Office", 32); + m_aAppId.ProductFamily[32] = m_aAppId.ProductFamily[33] = 0; + strncpy(m_aAppId.ProductName, "Office", 32); + m_aAppId.ProductName[32] = m_aAppId.ProductName[33] = 0; + + 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); +} + +void ImpTwain::Destroy() { ImplFallback(TWAIN_EVENT_QUIT); } + +bool ImpTwain::SelectSource() +{ + TW_UINT16 nRet = TWRC_FAILURE; + + ImplOpenSourceManager(); + + if (3 == 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); + } + + ImplFallback(TWAIN_EVENT_QUIT); + + return (TWRC_SUCCESS == nRet); +} + +bool ImpTwain::InitXfer() +{ + bool bRet = false; + + ImplOpenSourceManager(); + + if (3 == m_nCurState) + { + ImplOpenSource(); + + if (4 == m_nCurState) + bRet = ImplEnableSource(); + } + + if (!bRet) + ImplFallback(TWAIN_EVENT_QUIT); + + return bRet; +} + +void ImpTwain::ImplOpenSourceManager() +{ + if (1 == m_nCurState) + { + if ((m_hMod = LoadLibraryW(L"TWAIN_32.DLL"))) + { + m_nCurState = 2; + + 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 = 3; + } + } + } +} + +void ImpTwain::ImplOpenSource() +{ + if (3 == 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)); + + 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 = 4; + } + } +} + +bool ImpTwain::ImplEnableSource() +{ + bool bRet = false; + + if (4 == m_nCurState) + { + TW_USERINTERFACE aUI = { true, true, m_hTwainWnd }; + + NotifyParent(TWAIN_EVENT_SCANNING, 0); + m_nCurState = 5; + + if (m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, &aUI) + == TWRC_SUCCESS) + { + bRet = true; + } + else + { + // dialog failed + m_nCurState = 4; + } + } + + return bRet; +} + +void ImpTwain::NotifyParent(WPARAM nEvent, LPARAM lParam) +{ + PostThreadMessageW(m_nParentThreadId, WM_TWAIN_EVENT, nEvent, lParam); +} + +bool ImpTwain::ImplHandleMsg(MSG* pMsg) +{ + TW_UINT16 nRet; + TW_EVENT aEvt = { pMsg, MSG_NULL }; + + if (m_pDSM) + nRet = m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, &aEvt); + else + nRet = TWRC_NOTDSEVENT; + + if (aEvt.TWMessage != MSG_NULL) + { + switch (aEvt.TWMessage) + { + case MSG_XFERREADY: + { + WPARAM nEvent = TWAIN_EVENT_QUIT; + + if (5 == m_nCurState) + { + m_nCurState = 6; + ImplXfer(); + + if (m_hMap) + nEvent = TWAIN_EVENT_XFER; + } + else if (7 == m_nCurState && m_hMap) + { + // Already sent TWAIN_EVENT_XFER; not processed yet; + // duplicate event + nEvent = TWAIN_EVENT_NONE; + } + + ImplFallback(nEvent); + } + break; + + case MSG_CLOSEDSREQ: + ImplFallback(TWAIN_EVENT_QUIT); + break; + + default: + break; + } + } + else + nRet = TWRC_NOTDSEVENT; + + return (TWRC_DSEVENT == nRet); +} + +void ImpTwain::ImplXfer() +{ + if (m_nCurState == 6) + { + TW_IMAGEINFO aInfo; + HANDLE hDIB = nullptr; + long nWidth, nHeight, nXRes, nYRes; + + if (m_pDSM(&m_aAppId, &m_aSrcId, DG_IMAGE, DAT_IMAGEINFO, MSG_GET, &aInfo) == TWRC_SUCCESS) + { + nWidth = aInfo.ImageWidth; + nHeight = aInfo.ImageLength; + nXRes = FixToLong(aInfo.XResolution); + nYRes = FixToLong(aInfo.YResolution); + } + else + nWidth = nHeight = nXRes = nYRes = -1; + + switch (m_pDSM(&m_aAppId, &m_aSrcId, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, &hDIB)) + { + case TWRC_CANCEL: + m_nCurState = 7; + 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 != -1) && (nYRes != -1) && (nWidth != -1) && (nHeight != -1)) + { + // set resolution of bitmap + BITMAPINFOHEADER* pBIH = static_cast<BITMAPINFOHEADER*>(pBmpMem); + static const double fFactor = 100.0 / 2.54; + + pBIH->biXPelsPerMeter = FRound(fFactor * nXRes); + pBIH->biYPelsPerMeter = FRound(fFactor * nYRes); + } + + 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 = 7; + } + 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 7: + case 6: + { + 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 = 5; + } + break; + + case 5: + { + TW_USERINTERFACE aUI = { true, true, m_hTwainWnd }; + + m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, &aUI); + m_nCurState = 4; + } + break; + + case 4: + { + m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, &m_aSrcId); + m_nCurState = 3; + } + break; + + case 3: + { + m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, &m_hTwainWnd); + m_nCurState = 2; + } + break; + + case 2: + { + m_pDSM = nullptr; + FreeLibrary(m_hMod); + m_hMod = nullptr; + m_nCurState = 1; + } + break; + + default: + { + if (nEvent > TWAIN_EVENT_NONE) + NotifyParent(nEvent, reinterpret_cast<LPARAM>(m_hMap)); + if (nEvent == TWAIN_EVENT_QUIT) + 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, SelectSource()); + break; + case TWAIN_REQUEST_INITXFER: + NotifyParent(TWAIN_EVENT_REQUESTRESULT, 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_ALLPOSTMESSAGE | QS_SENDMESSAGE); + 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: */ diff --git a/extensions/source/scanner/twain32shim.hxx b/extensions/source/scanner/twain32shim.hxx new file mode 100644 index 000000000000..b72df438d596 --- /dev/null +++ b/extensions/source/scanner/twain32shim.hxx @@ -0,0 +1,70 @@ +/* -*- 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/. + * + */ + +#ifndef INCLUDED_EXTENSIONS_SOURCE_SCANNER_TWAIN32SHIM_HXX +#define INCLUDED_EXTENSIONS_SOURCE_SCANNER_TWAIN32SHIM_HXX + +#include <prewin.h> +#include <postwin.h> +#include <exception> +#include <string> +#include <sstream> +#include <iomanip> + +// Don't use WM_USER + +// Notifications from shim to parent; wParam = event id +#define WM_TWAIN_EVENT (WM_USER + 1) + +// lParam is HWND +#define TWAIN_EVENT_NOTIFYHWND 0 + +// lParam is result (bool indicating success) +#define TWAIN_EVENT_REQUESTRESULT 1 + +// lParam is ignored +#define TWAIN_EVENT_NONE 10 +#define TWAIN_EVENT_QUIT 11 +#define TWAIN_EVENT_SCANNING 12 + +// lParam is HANDLE to shared file mapping valid in context of parent process +#define TWAIN_EVENT_XFER 13 + +// Requests from parent to shim; wParam = request id +#define WM_TWAIN_REQUEST (WM_USER + 2) + +#define TWAIN_REQUEST_QUIT 0 // Destroy() +#define TWAIN_REQUEST_SELECTSOURCE 1 +#define TWAIN_REQUEST_INITXFER 2 + +// messages starting from this are not to be used for interprocess communications +#define WM_SHIM_INTERNAL (WM_USER + 200) + +template <typename IntType> std::string Num2Hex(IntType n) +{ + std::stringstream sMsg; + sMsg << "0x" << std::uppercase << std::setfill('0') << std::setw(sizeof(n) * 2) << std::hex + << n; + return sMsg.str(); +} + +void ThrowWin32Error(const char* sFunc, DWORD nWin32Error) +{ + std::stringstream sMsg; + sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!"; + + throw std::exception(sMsg.str().c_str()); +} + +void ThrowLastError(const char* sFunc) { ThrowWin32Error(sFunc, GetLastError()); } + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/external/twain_dsm/ExternalPackage_twain_dsm.mk b/external/twain_dsm/ExternalPackage_twain_dsm.mk deleted file mode 100644 index a2d1bb2ad32e..000000000000 --- a/external/twain_dsm/ExternalPackage_twain_dsm.mk +++ /dev/null @@ -1,20 +0,0 @@ -# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- -# -# 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/. -# - -$(eval $(call gb_ExternalPackage_ExternalPackage,twain_dsm,twain_dsm)) - -$(eval $(call gb_ExternalPackage_use_external_project,twain_dsm,twain_dsm)) - -ifeq ($(OS),WNT) -$(eval $(call gb_ExternalPackage_add_file,twain_dsm,$(LIBO_BIN_FOLDER)/TWAINDSM.dll,PCBuild/out/TWAINDSM.dll)) -endif - -# headers are not delivered, but used from unpacked dir pub/include/ - -# vim: set noet sw=4 ts=4: diff --git a/external/twain_dsm/ExternalProject_twain_dsm.mk b/external/twain_dsm/ExternalProject_twain_dsm.mk deleted file mode 100644 index f1e2285f7583..000000000000 --- a/external/twain_dsm/ExternalProject_twain_dsm.mk +++ /dev/null @@ -1,33 +0,0 @@ -# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- -# -# 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/. -# - -$(eval $(call gb_ExternalProject_ExternalProject,twain_dsm)) - -$(eval $(call gb_ExternalProject_use_unpacked,twain_dsm,twain_dsm)) - -$(eval $(call gb_ExternalProject_register_targets,twain_dsm,\ - build \ -)) - -ifeq ($(OS),WNT) - -$(call gb_ExternalProject_get_state_target,twain_dsm,build) : - $(call gb_ExternalProject_run,build,\ - MSBuild.exe visual_studio/TWAIN_DSM_VS2015.sln /t:Build \ - /p:Configuration=$(if $(MSVC_USE_DEBUG_RUNTIME),Debug,Release) \ - /p:Platform=$(if $(filter INTEL,$(CPUNAME)),Win32,x64) \ - /p:OutDir=../PCBuild/out/ /p:IntDir=../PCBuild/obj/ \ - $(if $(filter 140,$(VCVER)),/p:PlatformToolset=v140 /p:VisualStudioVersion=14.0 /ToolsVersion:14.0) \ - $(if $(filter 150,$(VCVER)),/p:PlatformToolset=v141 /p:VisualStudioVersion=15.0 /ToolsVersion:15.0) \ - $(if $(filter 150-10,$(VCVER)-$(WINDOWS_SDK_VERSION)),/p:WindowsTargetPlatformVersion=$(UCRTVERSION)) \ - ) - -endif - -# vim: set noet sw=4 ts=4: diff --git a/external/twain_dsm/Module_twain_dsm.mk b/external/twain_dsm/Module_twain_dsm.mk index 10db9b9a4dac..bbaedb77b5d0 100644 --- a/external/twain_dsm/Module_twain_dsm.mk +++ b/external/twain_dsm/Module_twain_dsm.mk @@ -10,15 +10,11 @@ $(eval $(call gb_Module_Module,twain_dsm)) ifeq ($(OS),WNT) - +ifeq ($(BUILD_X86),TRUE) $(eval $(call gb_Module_add_targets,twain_dsm,\ UnpackedTarball_twain_dsm \ - $(if $(filter X86_64,$(CPUNAME)), \ - ExternalProject_twain_dsm \ - ExternalPackage_twain_dsm \ - ) \ )) - +endif endif # vim: set noet sw=4 ts=4: diff --git a/external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.filters.patch b/external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.filters.patch deleted file mode 100644 index e73ae63dc855..000000000000 --- a/external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.filters.patch +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/visual_studio/TWAIN_DSM_VS2015.vcxproj.filters b/visual_studio/TWAIN_DSM_VS2015.vcxproj.filters -new file mode 100755 -index 000000000000..a17a30d0097f ---- /dev/null -+++ b/visual_studio/TWAIN_DSM_VS2015.vcxproj.filters -@@ -0,0 +1,47 @@ -+<?xml version="1.0" encoding="utf-8"?> -+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -+ <ItemGroup> -+ <Filter Include="Source Files"> -+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> -+ <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> -+ </Filter> -+ <Filter Include="Header Files"> -+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> -+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> -+ </Filter> -+ <Filter Include="Resource Files"> -+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> -+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx</Extensions> -+ </Filter> -+ </ItemGroup> -+ <ItemGroup> -+ <ClCompile Include="..\src\apps.cpp"> -+ <Filter>Source Files</Filter> -+ </ClCompile> -+ <ClCompile Include="..\src\dsm.cpp"> -+ <Filter>Source Files</Filter> -+ </ClCompile> -+ <ClCompile Include="..\src\hook.cpp"> -+ <Filter>Source Files</Filter> -+ </ClCompile> -+ <ClCompile Include="..\src\log.cpp"> -+ <Filter>Source Files</Filter> -+ </ClCompile> -+ </ItemGroup> -+ <ItemGroup> -+ <ClInclude Include="..\src\dsm.h"> -+ <Filter>Header Files</Filter> -+ </ClInclude> -+ <ClInclude Include="..\src\resource.h"> -+ <Filter>Header Files</Filter> -+ </ClInclude> -+ <ClInclude Include="..\src\twain.h"> -+ <Filter>Header Files</Filter> -+ </ClInclude> -+ </ItemGroup> -+ <ItemGroup> -+ <ResourceCompile Include="..\src\dsm.rc"> -+ <Filter>Resource Files</Filter> -+ </ResourceCompile> -+ </ItemGroup> -+</Project> -\ No newline at end of file diff --git a/external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.patch b/external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.patch deleted file mode 100644 index 40e131d2ad71..000000000000 --- a/external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.patch +++ /dev/null @@ -1,265 +0,0 @@ -diff --git a/visual_studio/TWAIN_DSM_VS2015.vcxproj b/visual_studio/TWAIN_DSM_VS2015.vcxproj -new file mode 100755 -index 000000000000..425c39966171 ---- /dev/null -+++ b/visual_studio/TWAIN_DSM_VS2015.vcxproj -@@ -0,0 +1,258 @@ -+<?xml version="1.0" encoding="utf-8"?> -+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -+ <ItemGroup Label="ProjectConfigurations"> -+ <ProjectConfiguration Include="Debug|Win32"> -+ <Configuration>Debug</Configuration> -+ <Platform>Win32</Platform> -+ </ProjectConfiguration> -+ <ProjectConfiguration Include="Debug|x64"> -+ <Configuration>Debug</Configuration> -+ <Platform>x64</Platform> -+ </ProjectConfiguration> -+ <ProjectConfiguration Include="Release|Win32"> -+ <Configuration>Release</Configuration> -+ <Platform>Win32</Platform> -+ </ProjectConfiguration> -+ <ProjectConfiguration Include="Release|x64"> -+ <Configuration>Release</Configuration> -+ <Platform>x64</Platform> -+ </ProjectConfiguration> -+ </ItemGroup> -+ <PropertyGroup Label="Globals"> -+ <ProjectGuid>{5F73EBC7-6A0E-4CBF-A37C-CB167E4CC379}</ProjectGuid> -+ <RootNamespace>TWAINDSM</RootNamespace> -+ <Keyword>Win32Proj</Keyword> -+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> -+ </PropertyGroup> -+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> -+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> -+ <ConfigurationType>DynamicLibrary</ConfigurationType> -+ <CharacterSet>MultiByte</CharacterSet> -+ <PlatformToolset>v140</PlatformToolset> -+ </PropertyGroup> -+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> -+ <ConfigurationType>DynamicLibrary</ConfigurationType> -+ <CharacterSet>MultiByte</CharacterSet> -+ <PlatformToolset>v140</PlatformToolset> -+ </PropertyGroup> -+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> -+ <ConfigurationType>DynamicLibrary</ConfigurationType> -+ <CharacterSet>MultiByte</CharacterSet> -+ <PlatformToolset>v140</PlatformToolset> -+ </PropertyGroup> -+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> -+ <ConfigurationType>DynamicLibrary</ConfigurationType> -+ <CharacterSet>MultiByte</CharacterSet> -+ <PlatformToolset>v140</PlatformToolset> -+ </PropertyGroup> -+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> -+ <ImportGroup Label="ExtensionSettings"> -+ </ImportGroup> -+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> -+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> -+ </ImportGroup> -+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> -+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> -+ </ImportGroup> -+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> -+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> -+ </ImportGroup> -+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> -+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> -+ </ImportGroup> -+ <PropertyGroup Label="UserMacros" /> -+ <PropertyGroup> -+ <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion> -+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)15_32\</OutDir> -+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)15_32\</IntDir> -+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental> -+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Configuration)15_64\</OutDir> -+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Configuration)15_64\</IntDir> -+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> -+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)15_32\</OutDir> -+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)15_32\</IntDir> -+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> -+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Configuration)15_64\</OutDir> -+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Configuration)15_64\</IntDir> -+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> -+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> -+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> -+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> -+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> -+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> -+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> -+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> -+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> -+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> -+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> -+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> -+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> -+ </PropertyGroup> -+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> -+ <TargetName>TWAINDSM</TargetName> -+ </PropertyGroup> -+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> -+ <TargetName>TWAINDSM</TargetName> -+ </PropertyGroup> -+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> -+ <TargetName>TWAINDSM</TargetName> -+ </PropertyGroup> -+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> -+ <TargetName>TWAINDSM</TargetName> -+ </PropertyGroup> -+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> -+ <PreBuildEvent> -+ <Message>create pub folders if do not exist</Message> -+ <Command> -+ </Command> -+ </PreBuildEvent> -+ <ClCompile> -+ <Optimization>Disabled</Optimization> -+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> -+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TWAIN_DSM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> -+ <MinimalRebuild>true</MinimalRebuild> -+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> -+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> -+ <PrecompiledHeader> -+ </PrecompiledHeader> -+ <WarningLevel>Level4</WarningLevel> -+ <TreatWarningAsError>false</TreatWarningAsError> -+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> -+ </ClCompile> -+ <Link> -+ <OutputFile>$(OutDir)TWAINDSM.dll</OutputFile> -+ <SuppressStartupBanner>true</SuppressStartupBanner> -+ <AdditionalLibraryDirectories>..\..\pub\external\lib;..\..\pub\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> -+ <ModuleDefinitionFile>..\src\dsm.def</ModuleDefinitionFile> -+ <GenerateDebugInformation>true</GenerateDebugInformation> -+ <ProgramDatabaseFile>$(OutDir)TWAINDSM.pdb</ProgramDatabaseFile> -+ <SubSystem>Windows</SubSystem> -+ <RandomizedBaseAddress>true</RandomizedBaseAddress> -+ <DataExecutionPrevention>true</DataExecutionPrevention> -+ <ImportLibrary>$(OutDir)TWAINDSM.lib</ImportLibrary> -+ <TargetMachine>MachineX86</TargetMachine> -+ </Link> -+ </ItemDefinitionGroup> -+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> -+ <PreBuildEvent> -+ <Message>create pub folders if do not exist</Message> -+ <Command> -+ </Command> -+ </PreBuildEvent> -+ <Midl> -+ <TargetEnvironment>X64</TargetEnvironment> -+ </Midl> -+ <ClCompile> -+ <Optimization>Disabled</Optimization> -+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> -+ <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;TWAIN_DSM_EXPORTS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> -+ <MinimalRebuild>true</MinimalRebuild> -+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> -+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> -+ <PrecompiledHeader> -+ </PrecompiledHeader> -+ <WarningLevel>Level4</WarningLevel> -+ <TreatWarningAsError>false</TreatWarningAsError> -+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> -+ </ClCompile> -+ <ResourceCompile> -+ <PreprocessorDefinitions>_VC80_UPGRADE=0x0710;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> -+ </ResourceCompile> -+ <Link> -+ <OutputFile>$(ProjectDir)\$(OutDir)TWAINDSM.dll</OutputFile> -+ <SuppressStartupBanner>true</SuppressStartupBanner> -+ <AdditionalLibraryDirectories>..\..\pub\external\lib;..\..\pub\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> -+ <ModuleDefinitionFile>..\src\dsm.def</ModuleDefinitionFile> -+ <GenerateDebugInformation>true</GenerateDebugInformation> -+ <ProgramDatabaseFile>$(OutDir)TWAINDSM.pdb</ProgramDatabaseFile> -+ <SubSystem>Windows</SubSystem> -+ <RandomizedBaseAddress>true</RandomizedBaseAddress> -+ <DataExecutionPrevention>true</DataExecutionPrevention> -+ <ImportLibrary>$(OutDir)TWAINDSM.lib</ImportLibrary> -+ <TargetMachine>MachineX64</TargetMachine> -+ </Link> -+ </ItemDefinitionGroup> -+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> -+ <PreBuildEvent> -+ <Message>create pub folders if do not exist</Message> -+ <Command> -+ </Command> -+ </PreBuildEvent> -+ <ClCompile> -+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> -+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;TWAIN_DSM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> -+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary> -+ <PrecompiledHeader> -+ </PrecompiledHeader> -+ <WarningLevel>Level3</WarningLevel> -+ <TreatWarningAsError>false</TreatWarningAsError> -+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> -+ </ClCompile> -+ <Link> -+ <OutputFile>$(OutDir)TWAINDSM.dll</OutputFile> -+ <AdditionalLibraryDirectories>..\..\pub\external\lib;..\..\pub\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> -+ <ModuleDefinitionFile>..\src\dsm.def</ModuleDefinitionFile> -+ <GenerateDebugInformation>true</GenerateDebugInformation> -+ <SubSystem>Windows</SubSystem> -+ <OptimizeReferences>true</OptimizeReferences> -+ <EnableCOMDATFolding>true</EnableCOMDATFolding> -+ <RandomizedBaseAddress>true</RandomizedBaseAddress> -+ <DataExecutionPrevention>true</DataExecutionPrevention> -+ <ImportLibrary>$(OutDir)TWAINDSM.lib</ImportLibrary> -+ <TargetMachine>MachineX86</TargetMachine> -+ </Link> -+ </ItemDefinitionGroup> -+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> -+ <PreBuildEvent> -+ <Message>create pub folders if do not exist</Message> -+ <Command> -+ </Command> -+ </PreBuildEvent> -+ <Midl> -+ <TargetEnvironment>X64</TargetEnvironment> -+ </Midl> -+ <ClCompile> -+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> -+ <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;TWAIN_DSM_EXPORTS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> -+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary> -+ <PrecompiledHeader> -+ </PrecompiledHeader> -+ <WarningLevel>Level3</WarningLevel> -+ <TreatWarningAsError>false</TreatWarningAsError> -+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> -+ </ClCompile> -+ <ResourceCompile> -+ <PreprocessorDefinitions>_VC80_UPGRADE=0x0710;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> -+ </ResourceCompile> -+ <Link> -+ <OutputFile>$(ProjectDir)\$(OutDir)TWAINDSM.dll</OutputFile> -+ <AdditionalLibraryDirectories>..\..\pub\external\lib;..\..\pub\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> -+ <ModuleDefinitionFile>..\src\dsm.def</ModuleDefinitionFile> -+ <GenerateDebugInformation>true</GenerateDebugInformation> -+ <SubSystem>Windows</SubSystem> -+ <OptimizeReferences>true</OptimizeReferences> -+ <EnableCOMDATFolding>true</EnableCOMDATFolding> -+ <RandomizedBaseAddress>true</RandomizedBaseAddress> -+ <DataExecutionPrevention>true</DataExecutionPrevention> -+ <ImportLibrary>$(OutDir)TWAINDSM.lib</ImportLibrary> -+ <TargetMachine>MachineX64</TargetMachine> -+ </Link> -+ </ItemDefinitionGroup> -+ <ItemGroup> -+ <ClCompile Include="..\src\apps.cpp" /> -+ <ClCompile Include="..\src\dsm.cpp" /> -+ <ClCompile Include="..\src\hook.cpp" /> -+ <ClCompile Include="..\src\log.cpp" /> -+ </ItemGroup> -+ <ItemGroup> -+ <ClInclude Include="..\src\dsm.h" /> -+ <ClInclude Include="..\src\resource.h" /> -+ <ClInclude Include="..\src\twain.h" /> -+ </ItemGroup> -+ <ItemGroup> -+ <ResourceCompile Include="..\src\dsm.rc" /> -+ </ItemGroup> -+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -+ <ImportGroup Label="ExtensionTargets"> -+ </ImportGroup> -+</Project> -\ No newline at end of file diff --git a/external/twain_dsm/UnpackedTarball_twain_dsm.mk b/external/twain_dsm/UnpackedTarball_twain_dsm.mk index ac0c3ce7c8c6..11be31e9eb81 100644 --- a/external/twain_dsm/UnpackedTarball_twain_dsm.mk +++ b/external/twain_dsm/UnpackedTarball_twain_dsm.mk @@ -11,13 +11,4 @@ $(eval $(call gb_UnpackedTarball_UnpackedTarball,twain_dsm)) $(eval $(call gb_UnpackedTarball_set_tarball,twain_dsm,$(TWAIN_DSM_TARBALL))) -$(eval $(call gb_UnpackedTarball_set_patchlevel,twain_dsm,1)) - -$(eval $(call gb_UnpackedTarball_add_patches,twain_dsm, \ - external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.patch \ - external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.filters.patch \ - external/twain_dsm/fix-non-us-ascii-chars-part1.patch \ - external/twain_dsm/fix-non-us-ascii-chars-part2.patch \ -)) - # vim: set noet sw=4 ts=4: diff --git a/external/twain_dsm/fix-non-us-ascii-chars-part1.patch b/external/twain_dsm/fix-non-us-ascii-chars-part1.patch deleted file mode 100644 index 0fb29dee45ce..000000000000 --- a/external/twain_dsm/fix-non-us-ascii-chars-part1.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/src/apps.cpp b/src/apps.cpp -index 54c6347573fa..45d169d5b148 100755 ---- a/src/apps.cpp -+++ b/src/apps.cpp -@@ -1,7 +1,7 @@ - /*************************************************************************** - * TWAIN Data Source Manager version 2.1 - * Manages image acquisition data sources used by a machine. -- * Copyright © 2007 TWAIN Working Group: -+ * Copyright (C) 2007 TWAIN Working Group: - * Adobe Systems Incorporated,AnyDoc Software Inc., Eastman Kodak Company, - * Fujitsu Computer Products of America, JFL Peripheral Solutions Inc., - * Ricoh Corporation, and Xerox Corporation. -diff --git a/src/log.cpp b/src/log.cpp -index 496e9d9c52a1..3b05307981ad 100755 ---- a/src/log.cpp -+++ b/src/log.cpp -@@ -1,7 +1,7 @@ - /*************************************************************************** - * TWAIN Data Source Manager version 2.1 - * Manages image acquisition data sources used by a machine. -- * Copyright © 2007 TWAIN Working Group: -+ * Copyright (C) 2007 TWAIN Working Group: - * Adobe Systems Incorporated,AnyDoc Software Inc., Eastman Kodak Company, - * Fujitsu Computer Products of America, JFL Peripheral Solutions Inc., - * Ricoh Corporation, and Xerox Corporation. diff --git a/external/twain_dsm/fix-non-us-ascii-chars-part2.patch b/external/twain_dsm/fix-non-us-ascii-chars-part2.patch deleted file mode 100644 index 29e78bf00faf..000000000000 --- a/external/twain_dsm/fix-non-us-ascii-chars-part2.patch +++ /dev/null @@ -1,44 +0,0 @@ -diff --git a/src/dsm.cpp b/src/dsm.cpp -index 1b74285c5d72..efd8f443ca02 100755 ---- a/src/dsm.cpp -+++ b/src/dsm.cpp -@@ -1,7 +1,7 @@ - /*************************************************************************** - * TWAIN Data Source Manager version 2.1 - * Manages image acquisition data sources used by a machine. -- * Copyright © 2007 TWAIN Working Group: -+ * Copyright (C) 2007 TWAIN Working Group: - * Adobe Systems Incorporated,AnyDoc Software Inc., Eastman Kodak Company, - * Fujitsu Computer Products of America, JFL Peripheral Solutions Inc., - * Ricoh Corporation, and Xerox Corporation. -@@ -38,7 +38,7 @@ - /*! \mainpage Data Source Manager - * - * The Source Manager provides the communication path between the -- * Application and the Source, supports the user’s selection of a -+ * Application and the Source, supports the user's selection of a - * Source, and loads the Source for access by the Application. - * Communications from Application to Source Manager or the Source - * to Source Manager (via DAT_NULL) arrive in exclusively through -@@ -51,7 +51,7 @@ - * - * - * -- * Copyright © 2007 TWAIN Working Group: Adobe Systems Incorporated, -+ * Copyright (C) 2007 TWAIN Working Group: Adobe Systems Incorporated, - * AnyDoc Software Inc., Eastman Kodak Company, - * Fujitsu Computer Products of America, JFL Peripheral Solutions Inc., - * Ricoh Corporation, and Xerox Corporation. -diff --git a/src/dsm.h b/src/dsm.h -index f7afb316d3de..c156d3843a9e 100755 ---- a/src/dsm.h -+++ b/src/dsm.h -@@ -1,7 +1,7 @@ - /*************************************************************************** - * TWAIN Data Source Manager version 2.1 - * Manages image acquisition data sources used by a machine. -- * Copyright © 2007 TWAIN Working Group: -+ * Copyright (C) 2007 TWAIN Working Group: - * Adobe Systems Incorporated,AnyDoc Software Inc., Eastman Kodak Company, - * Fujitsu Computer Products of America, JFL Peripheral Solutions Inc., - * Ricoh Corporation, and Xerox Corporation. diff --git a/solenv/gbuild/Executable.mk b/solenv/gbuild/Executable.mk index 8e955cc0ac79..c2285f05ac04 100644 --- a/solenv/gbuild/Executable.mk +++ b/solenv/gbuild/Executable.mk @@ -120,6 +120,7 @@ gb_Executable_add_defs = $(call gb_Executable__forward_to_Linktarget,$(subst gb_ gb_Executable_set_include = $(call gb_Executable__forward_to_Linktarget,$(subst gb_Executable_,,$(0)),$(1),$(2),$(3)) gb_Executable_add_ldflags = $(call gb_Executable__forward_to_Linktarget,$(subst gb_Executable_,,$(0)),$(1),$(2),$(3)) gb_Executable_set_ldflags = $(call gb_Executable__forward_to_Linktarget,$(subst gb_Executable_,,$(0)),$(1),$(2),$(3)) +gb_Executable_set_x86 = $(call gb_Executable__forward_to_Linktarget,$(subst gb_Executable_,,$(0)),$(1),$(2),$(3)) gb_Executable_add_libs = $(call gb_Executable__forward_to_Linktarget,$(subst gb_Executable_,,$(0)),$(1),$(2),$(3)) gb_Executable_disable_standard_system_libs = $(call gb_Executable__forward_to_Linktarget,$(subst gb_Executable_,,$(0)),$(1),$(2),$(3)) gb_Executable_use_system_darwin_frameworks = $(call gb_Executable__forward_to_Linktarget,$(subst gb_Executable_,,$(0)),$(1),$(2),$(3)) diff --git a/solenv/gbuild/LinkTarget.mk b/solenv/gbuild/LinkTarget.mk index bc6d3610daf6..96be7ad5f01c 100644 --- a/solenv/gbuild/LinkTarget.mk +++ b/solenv/gbuild/LinkTarget.mk @@ -711,6 +711,7 @@ $(WORKDIR)/Headers/% : # - TARGETTYPE is the type of linktarget as some platforms need very different # command to link different targettypes. # - LIBRARY_X64 is only relevant for building a x64 library on windows. +# - PE_X86 is only relevant for building a x86 binaries on Windows. # # Since most variables are set on the linktarget and not on the object, the # object learns about these setting via GNU makes scoping of target variables. @@ -780,6 +781,7 @@ $(call gb_LinkTarget_get_target,$(1)) : PCH_NAME := $(call gb_LinkTarget_get_target,$(1)) : PCHOBJS := $(call gb_LinkTarget_get_target,$(1)) : PCHOBJEX := $(call gb_LinkTarget_get_target,$(1)) : PCHOBJNOEX := +$(call gb_LinkTarget_get_target,$(1)) : PE_X86 := $(call gb_LinkTarget_get_target,$(1)) : PDBFILE := $(call gb_LinkTarget_get_target,$(1)) : TARGETGUI := $(call gb_LinkTarget_get_target,$(1)) : EXTRAOBJECTLISTS := @@ -1435,6 +1437,12 @@ $(call gb_LinkTarget_get_target,$(1)) : LIBRARY_X64 := $(2) endef +# call gb_LinkTarget_set_x86,linktarget,boolean +define gb_LinkTarget_set_x86 +$(call gb_LinkTarget_get_target,$(1)) : PE_X86 := $(2) + +endef + # call gb_LinkTarget_set_ilibtarget,linktarget,ilibfilename define gb_LinkTarget_set_ilibtarget $(call gb_LinkTarget_get_clean_target,$(1)) \ diff --git a/solenv/gbuild/platform/com_MSC_class.mk b/solenv/gbuild/platform/com_MSC_class.mk index 18db567bfc50..333c8b4d80bc 100644 --- a/solenv/gbuild/platform/com_MSC_class.mk +++ b/solenv/gbuild/platform/com_MSC_class.mk @@ -40,9 +40,10 @@ $(call gb_Helper_abbreviate_dirs,\ mkdir -p $(dir $(1)) $(dir $(4)) && \ unset INCLUDE && \ $(if $(filter YES,$(CXXOBJECT_X64)), $(CXX_X64_BINARY), \ - $(if $(filter %.c,$(3)), $(gb_CC), \ - $(if $(filter -clr,$(2)), \ - $(MSVC_CXX) -I$(SRCDIR)/solenv/clang-cl,$(gb_CXX)))) \ + $(if $(filter YES,$(PE_X86)), $(CXX_X86_BINARY), \ + $(if $(filter %.c,$(3)), $(gb_CC), \ + $(if $(filter -clr,$(2)), \ + $(MSVC_CXX) -I$(SRCDIR)/solenv/clang-cl,$(gb_CXX))))) \ $(DEFS) \ $(gb_LTOFLAGS) \ $(2) \ @@ -57,6 +58,7 @@ $(call gb_Helper_abbreviate_dirs,\ $(if $(COMPILER_TEST),,$(gb_COMPILERDEPFLAGS)) \ $(INCLUDE) \ $(if $(filter YES,$(CXXOBJECT_X64)), -U_X86_ -D_AMD64_,) \ + $(if $(filter YES,$(PE_X86)), -D_X86_ -U_AMD64_,) \ -c $(3) \ -Fo$(1)) $(if $(filter $(true),$(gb_SYMBOL)),/link /DEBUG:FASTLINK) \ $(if $(COMPILER_TEST),,$(call gb_create_deps,$(4),$(1),$(3))) @@ -172,11 +174,17 @@ $(call gb_Helper_abbreviate_dirs,\ $(if $(filter Executable,$(TARGETTYPE)),$(gb_Executable_TARGETTYPEFLAGS)) \ $(if $(filter YES,$(LIBRARY_X64)),,$(if $(filter YES,$(TARGETGUI)), -SUBSYSTEM:WINDOWS$(MSC_SUBSYSTEM_VERSION), -SUBSYSTEM:CONSOLE$(MSC_SUBSYSTEM_VERSION))) \ $(if $(filter YES,$(LIBRARY_X64)), -MACHINE:X64) \ + $(if $(filter YES,$(PE_X86)), -MACHINE:X86) \ $(if $(filter YES,$(LIBRARY_X64)), \ -LIBPATH:$(COMPATH)/lib/$(if $(filter 140,$(VCVER)),amd64,x64) \ -LIBPATH:$(WINDOWS_SDK_HOME)/lib/x64 \ -LIBPATH:$(UCRTSDKDIR)lib/$(UCRTVERSION)/ucrt/x64 \ $(if $(filter 80 81 10,$(WINDOWS_SDK_VERSION)),-LIBPATH:$(WINDOWS_SDK_HOME)/lib/$(WINDOWS_SDK_LIB_SUBDIR)/um/x64)) \ + $(if $(filter YES,$(PE_X86)), \ + -LIBPATH:$(COMPATH)/lib/x86 \ + -LIBPATH:$(WINDOWS_SDK_HOME)/lib/x86 \ + -LIBPATH:$(UCRTSDKDIR)lib/$(UCRTVERSION)/ucrt/x86 \ + $(if $(filter 80 81 10,$(WINDOWS_SDK_VERSION)),-LIBPATH:$(WINDOWS_SDK_HOME)/lib/$(WINDOWS_SDK_LIB_SUBDIR)/um/x86)) \ $(T_LDFLAGS) \ $(if $(filter Library CppunitTest Executable,$(TARGETTYPE)),/NATVIS:$(SRCDIR)/solenv/vs/LibreOffice.natvis) \ @$${RESPONSEFILE} \ |