[Libreoffice-commits] core.git: config_host.mk.in configure.ac extensions/Executable_twain32shim.mk extensions/Library_scn.mk extensions/Module_extensions.mk extensions/source external/twain_dsm Repository.mk solenv/gbuild

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Sat Dec 29 08:59:18 UTC 2018


 Repository.mk                                             |    2 
 config_host.mk.in                                         |    2 
 configure.ac                                              |   23 
 extensions/Executable_twain32shim.mk                      |   33 
 extensions/Library_scn.mk                                 |   15 
 extensions/Module_extensions.mk                           |    5 
 extensions/source/scanner/scanwin.cxx                     | 1010 ++++----------
 extensions/source/scanner/twain32shim.cxx                 |  598 ++++++++
 extensions/source/scanner/twain32shim.hxx                 |   70 
 external/twain_dsm/ExternalPackage_twain_dsm.mk           |   20 
 external/twain_dsm/ExternalProject_twain_dsm.mk           |   33 
 external/twain_dsm/Module_twain_dsm.mk                    |    8 
 external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.filters.patch |   54 
 external/twain_dsm/TWAIN_DSM_VS2015.vcxproj.patch         |  265 ---
 external/twain_dsm/UnpackedTarball_twain_dsm.mk           |    9 
 external/twain_dsm/fix-non-us-ascii-chars-part1.patch     |   26 
 external/twain_dsm/fix-non-us-ascii-chars-part2.patch     |   44 
 solenv/gbuild/Executable.mk                               |    1 
 solenv/gbuild/LinkTarget.mk                               |    8 
 solenv/gbuild/platform/com_MSC_class.mk                   |   14 
 20 files changed, 1095 insertions(+), 1145 deletions(-)

New commits:
commit 648f24f5d1dae3f0fd5b132d68a5e39066f2572d
Author:     Mike Kaganski <mike.kaganski at collabora.com>
AuthorDate: Fri Dec 28 20:23:02 2018 +0300
Commit:     Mike Kaganski <mike.kaganski at collabora.com>
CommitDate: Sat Dec 29 09:58:09 2018 +0100

    tdf#114635: reimplement TWAIN-based scan using 32-bit shim on Windows
    
    Since TWAIN is only actually available as 32-bit component on Windows,
    to use it in a 64-bit program, we need a 32-bit shim program that does
    all actual communication with TWAIN subsystem.
    
    This change reimplements TWAIN implementation to be a separate 32-bit
    process. Image is transfered from the shim to main program using file
    mapping API.
    
    This reverts most of commit 585d9806961342e95f7318fb947bd31e9f86dee0.
    64-bit LibreOffice doesn't bundle TWAIN DSM library now. TWAIN DSM
    source code is still used for TWAIN headers.
    
    Change-Id: I46f178ad36acd97a9eff156624b99036fcbb83f8
    Reviewed-on: https://gerrit.libreoffice.org/65688
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>

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} \


More information about the Libreoffice-commits mailing list