64-Bit Windows applications typically expect the Twain Data Source manager to be named twaindsm.dll.
Send the DG_CONTROL/DAT_ENTRYPOINT/MSG_SET before DG_CONTROL/DAT_IDENTITY/MSG_OPENDS so sane.ds does not need to know the module name to find the DSM entry point.
An explanation for the request to offer the name twaindsm.dll for 64-Bit windows is offered here by the TWAIN organization:
https://github.com/twain/twain-dsm/blob/master/TWAIN_DSM/src/readme.doc
They cannot update the twain_32.dll installed and maintained by Microsoft, so they choose the new name twaindsm.dll instead. This name is also used at least by Irfanview and oder versions oder LibreOffice (Newer Libreoffice version start a 32-Bit process for 32-Bit TWAIN usage).
This Merge request also changes the twain_32.dll so that it send it's entry points earlier, before opening the data source. This is the same behaviour as the twaindsm.dll from the TWAIN organization from [https://github.com/twain/twain-dsm/%5D(https://github.com/twain/twain-dsm/bl...)
Otherwise sane.ds would in MSG_OPENDS try to find the DSM entry point based on the module-name "twain_32" in sane_main.c:60:
``` if (SANE_dsmentry == NULL) { HMODULE moddsm = GetModuleHandleW(L"twain_32");
if (moddsm) SANE_dsmentry = (void*)GetProcAddress(moddsm, "DSM_Entry");
if (!SANE_dsmentry) { ERR("can't find DSM entry point\n"); return TWRC_FAILURE; } } ```
-- v3: dlls/twain_32, dlls/twaindsm: Build Twain DSM as twaindsm.dll on 64-Bit build dlls/twain_32: Offer TWAIN DSM as twaindsm.dll for 64 Bit and send DAT_ENTRYPOINT earlier.
From: Bernd Herd codeberg@herdsoft.com
64-Bit windows applications expect the Twain Data Source manager to be named twaindsm.dll.
Send the DG_CONTROL/DAT_ENTRYPOINT/MSG_SET before DG_CONTROL/DAT_IDENTITY/MSG_OPENDS so sane.ds does not need to know the module name to find the DSM entry point --- dlls/twain_32/dsm_ctrl.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-)
diff --git a/dlls/twain_32/dsm_ctrl.c b/dlls/twain_32/dsm_ctrl.c index 89e788f979d..436654bfcc7 100644 --- a/dlls/twain_32/dsm_ctrl.c +++ b/dlls/twain_32/dsm_ctrl.c @@ -381,8 +381,31 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData) } newSource->hmod = hmod; newSource->dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry"); + if (!newSource->dsEntry) { + ERR("Failed to find DS_Entry() in TWAIN DS %s\n", debugstr_w(devices[i].modname)); + DSM_twCC = TWCC_OPERATIONERROR; + HeapFree(GetProcessHeap(), 0, newSource); + return TWRC_FAILURE; + } /* Assign id for the opened data source */ pIdentity->Id = DSM_sourceId ++; + /* Get the Identity of the new DS, so we know the SupportedGroups */ + if (TWRC_SUCCESS != newSource->dsEntry (NULL, DG_CONTROL, DAT_IDENTITY, MSG_GET, pIdentity)) { + DSM_twCC = TWCC_OPERATIONERROR; + HeapFree(GetProcessHeap(), 0, newSource); + DSM_sourceId--; + return TWRC_FAILURE; + } + /* Tell the source our entry points */ + if (pIdentity->SupportedGroups & DF_DS2) { + /* This makes sure that the DS knows the current address of our DSM_Entry + * function so there is no risk that it is using a stale copy. + * The other entry points are also set for formal reasons, + * but are currently not used. + */ + newSource->dsEntry (pOrigin, DG_CONTROL, DAT_ENTRYPOINT, MSG_SET, (TW_ENTRYPOINT *) &_entrypoints); + } + /* Open the data source */ if (TWRC_SUCCESS != newSource->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, pIdentity)) { DSM_twCC = TWCC_OPERATIONERROR; HeapFree(GetProcessHeap(), 0, newSource); @@ -399,15 +422,6 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData) activeSources = newSource; DSM_twCC = TWCC_SUCCESS;
- /* Tell the source our entry points */ - if (pIdentity->SupportedGroups & DF_DS2) { - /* This makes sure that the DS knows the current address of our DSM_Entry - * function so there is no risk that it is using a stale copy. - * The other entry points are also set for formal reasons, - * but are currently not used. - */ - newSource->dsEntry (pOrigin, DG_CONTROL, DAT_ENTRYPOINT, MSG_SET, (TW_ENTRYPOINT *) &_entrypoints); - } return TWRC_SUCCESS; }
From: Bernd Herd codeberg@herdsoft.com
Adds dlls/twaindsm to build from the same source as twain_32 but named twaindsm.dll for 64-Bit build.
Change configure.ac to build only twaindsm.dll on 64-Bit and only twain_32.dll on 32-Bit, but can be overwritten with configure options --- configure | 11 +++++++++++ configure.ac | 11 +++++++++++ dlls/twaindsm/Makefile.in | 10 ++++++++++ dlls/twaindsm/twaindsm.spec | 1 + 4 files changed, 33 insertions(+) create mode 100644 dlls/twaindsm/Makefile.in create mode 100644 dlls/twaindsm/twaindsm.spec
diff --git a/configure b/configure index e3fd3140529..cc4ad89b790 100755 --- a/configure +++ b/configure @@ -1457,6 +1457,7 @@ enable_tdi_sys enable_threadpoolwinrt enable_traffic enable_twain_32 +enable_twaindsm enable_twinapi_appcore enable_tzres enable_ucrtbase @@ -6554,6 +6555,15 @@ then fi fi
+if test "x$enable_win64" = "xyes" ; +then if test "x$enable_twain_32" != "xyes" ; then + enable_twain_32="no"; + fi +else if test "x$enable_twaindsm" != "xyes" ; then + enable_twaindsm="no"; + fi +fi + case $build_os in cygwin*|mingw32*) toolsext=".exe" ;; @@ -23063,6 +23073,7 @@ wine_fn_config_makefile dlls/traffic enable_traffic wine_fn_config_makefile dlls/twain.dll16 enable_win16 wine_fn_config_makefile dlls/twain_32 enable_twain_32 wine_fn_config_makefile dlls/twain_32/tests enable_tests +wine_fn_config_makefile dlls/twaindsm enable_twaindsm wine_fn_config_makefile dlls/twinapi.appcore enable_twinapi_appcore wine_fn_config_makefile dlls/twinapi.appcore/tests enable_tests wine_fn_config_makefile dlls/typelib.dll16 enable_win16 diff --git a/configure.ac b/configure.ac index a9db2abcce4..79a7bfda551 100644 --- a/configure.ac +++ b/configure.ac @@ -171,6 +171,16 @@ then fi fi
+dnl by default build twaindsm.dll with 64-Bit build and twain_32.dll with 32-Bit build +if test "x$enable_win64" = "xyes" ; +then if test "x$enable_twain_32" != "xyes" ; then + enable_twain_32="no"; + fi +else if test "x$enable_twaindsm" != "xyes" ; then + enable_twaindsm="no"; + fi +fi + case $build_os in cygwin*|mingw32*) AC_SUBST(toolsext,".exe") ;; *) AC_SUBST(toolsext,"") ;; @@ -3208,6 +3218,7 @@ WINE_CONFIG_MAKEFILE(dlls/traffic) WINE_CONFIG_MAKEFILE(dlls/twain.dll16) WINE_CONFIG_MAKEFILE(dlls/twain_32) WINE_CONFIG_MAKEFILE(dlls/twain_32/tests) +WINE_CONFIG_MAKEFILE(dlls/twaindsm) WINE_CONFIG_MAKEFILE(dlls/twinapi.appcore) WINE_CONFIG_MAKEFILE(dlls/twinapi.appcore/tests) WINE_CONFIG_MAKEFILE(dlls/typelib.dll16) diff --git a/dlls/twaindsm/Makefile.in b/dlls/twaindsm/Makefile.in new file mode 100644 index 00000000000..653c0419b39 --- /dev/null +++ b/dlls/twaindsm/Makefile.in @@ -0,0 +1,10 @@ +MODULE = twaindsm.dll +IMPORTS = user32 + +PARENTSRC = ../twain_32 + + +SOURCES = \ + dsm_ctrl.c \ + twain.rc \ + twain32_main.c diff --git a/dlls/twaindsm/twaindsm.spec b/dlls/twaindsm/twaindsm.spec new file mode 100644 index 00000000000..fe79c9f19dc --- /dev/null +++ b/dlls/twaindsm/twaindsm.spec @@ -0,0 +1 @@ +@ stdcall DSM_Entry(ptr ptr long long long ptr)
Esme Povirk (@madewokherd) commented about configure.ac:
fifi
+dnl by default build twaindsm.dll with 64-Bit build and twain_32.dll with 32-Bit build +if test "x$enable_win64" = "xyes" ; +then if test "x$enable_twain_32" != "xyes" ; then
enable_twain_32="no";fi+else if test "x$enable_twaindsm" != "xyes" ; then
enable_twaindsm="no";fi+fi
This way of doing it won't work correctly when we're building multiple architectures in a single tree. I think this should be similar to enable_dpnsvr below.
I still think that twaindsm should be provided on all architectures, though at the moment it's unclear to me where it should look for data sources on arm64.
Am Freitag, dem 21.11.2025 um 14:12 -0600 schrieb Esme Povirk (@madewokherd):
Merge Request https://gitlab.winehq.org/wine/wine/-/merge_requests/9519%C2%A0wurde von Esme Povirk überprüft
-- Esme Povirk started a new discussion on configure.ac: https://gitlab.winehq.org/wine/wine/-/merge_requests/9519#note_123300
> + enable_twaindsm="no"; > + fi > +fi
This way of doing it won't work correctly when we're building multiple architectures in a single tree. I think this should be similar to enable_dpnsvr below.
I'll likely take a look into this over the weekend...
But I'm insecure here.
I still think that twaindsm should be provided on all architectures,
That surprises me.
If you have an old twain 1.x compatible data source, that does not support the TWAIN 2.x extensions with the callbacks, that old data source needs to find the DSM using the "twain_32" name. As does sane.ds if being called from an old version 1.x DSM.
So such an old Data Source cannot be used with twaindsm.dll, it must be used by twain_32.dll, else it won't work. Even if you just make a copy of twain_32.dll as twaindsm.dll and use that, an old Twain 1.x data source will likely fail. It will also fail with the twaindsm.dll provided by twain.org.
https://github.com/twain/twain-dsm/blob/master/TWAIN_DSM/src/readme.doc
Describes that problem in chapter 3.3.3:
"TWAIN Drivers that want to use twaindsm32.dll need to add the flag SF_DSM2_DS to the TW_IDENTITY SupportedGroups member. This is to trigger the application that they can use twaindsm32.dll. When a TWAIN Driver is opened it can check the TW_IDENITY SupportedGroups flag. If the DSM has set SF_DSM2_DSM the TWAIN Driver knows that it has been opened by twaindsm32.dll. The twaindsm32.dll after successfully opening a DS can immediately send a DG_CONTROL/ DAT_CALLBACK/ MSG_REGISTER_CALLBACK triplet message to the DS. This message will contain the TW_CALLBACK structure with handle to the DSM_Entry function. If the DS does not receive this message it will default to the old method of LoadLibrary() and GetProcAddress() and assume it is being called by the TWAIN_32.DLL. TWAIN Application that want to support both twaindsm32.dll and TWAIN_32.DLL should first review all the drivers by using TWAIN_32.dll using getFirst() getNext on all the drivers. By checking the TW_IDENTIY SupportedGroups flag against SF_DSM2_DS the app knows that the TWAIN Driver can use the twaindsm32.dll. The TWAIN Application can safely use the twaindsm32.dll to open this TWIAN Driver by name. The application does not set the SF_DSM2_DSM or SF_DSM2_DS flag to indicate they want to use the twaindsm32.dll the DSM will do this."
That sounds lake an awfully complex process.
The only good reason to add this complexity for 32-Bit is if an application profits from the TWAIN 2.x extensions and thus the twaindsm.dll. And I don't think wine's dlls/twain_32 supports all TWAIN 2.0 features yet.
Compared to 64-Bit:
"Since Microsoft is not installing the DSM in 64-bit versions of Windows, this is the only DSM that TWAIN Applications and TWAIN Drivers need to look for."
So we can expect all 64-Bit data sources to know how to handle this.
If an application really wants to use the TWAIN 2.0 extensions in twaindsm.dll for 32-Bit, it can still install the TWAIN organization Version on 32-Bit...
I read that some users successfully use TCP/IP-Based TWAIN Data Sources Drivers designed for Windows unter Wine.
Twain has a driver database and it seems 64-Bit TWAIN for Windows is usually only offered by higher-class devices.
though at the moment it's unclear to me where it should look for data sources on arm64.
I have no idea of the ARM64 architecture yet.
The twain spec says: "The name of the TWAIN directory is "twain_32" for 32-bit Sources and "twain_64" for 64-bit Sources (on 64-bit systems only)." (PDF-Page 645).
Shouldn't that be sufficient? Or do we need to distinguish betwen ARM64 and AMD64 binaries on the same machine?
Does ARM64 Architecture mean that I can compile up to 30 year old WIN32 source codes with few changes and run it on a raspberry pi? Could be cool.
:-)
Hello,
I still think that twaindsm should be provided on all architectures,
today I found that the 32-Bit twaindsm.dll from TWAIN.org hooks up all calls to "GetProcAddress" to see if a DS asks for the address of "DSM_Entry" in "twain_32.dll" and give it the "DSM_Entry" in itself instead:
https://github.com/twain/twain-dsm/blob/master/TWAIN_DSM/src/hook.cpp
Maybe we should move everything to twaindsm.dll, and have twain_32.dll simply forward to twaindsm.