From: Bernd Herd codeberg@herdsoft.com
Renamed dlls/twain_32 to dlls/twaindsm thus creating twaindsm.dll as platform independent name for the Twain Data Source Manager as it is used on 64-Bit windows.
Create new dlls/twain_32 as a wrapper for 32-Bit Windows only that forwards all calls to the main implementation in twaindsm.dll.
Send the DG_CONTROL/DAT_ENTRYPOINT/MSG_SET before DG_CONTROL/DAT_IDENTITY/MSG_OPENDS so Twain 2.x aware data sources like sane.ds don't need to know the filename of the Twain DSM ro find the DSM entry point.
On 32-Bit Systems LoadLibrary for the twain_32.dll wrapper when opening the DS and it is not 2.x compliant. This makes sure it will find the DSM_Entry of twain_32.dll if it uses GetModuleHandle("twain_32.dll") to find the DLL. --- MAINTAINERS | 1 + configure | 6 ++- configure.ac | 5 +- dlls/twain_32/Makefile.in | 6 +-- dlls/twain_32/twain_32.c | 50 +++++++++++++++++++ dlls/twain_32/twain_32.spec | 2 +- dlls/twaindsm/Makefile.in | 8 +++ dlls/{twain_32 => twaindsm}/dsm_ctrl.c | 47 +++++++++++++---- dlls/{twain_32 => twaindsm}/resource.h | 0 dlls/{twain_32 => twaindsm}/tests/Makefile.in | 2 +- dlls/{twain_32 => twaindsm}/tests/dsm.c | 4 +- dlls/{twain_32 => twaindsm}/twain.rc | 0 dlls/{twain_32 => twaindsm}/twain32_main.c | 0 dlls/{twain_32 => twaindsm}/twain_i.h | 0 dlls/twaindsm/twaindsm.spec | 1 + 15 files changed, 112 insertions(+), 20 deletions(-) create mode 100644 dlls/twain_32/twain_32.c create mode 100644 dlls/twaindsm/Makefile.in rename dlls/{twain_32 => twaindsm}/dsm_ctrl.c (94%) rename dlls/{twain_32 => twaindsm}/resource.h (100%) rename dlls/{twain_32 => twaindsm}/tests/Makefile.in (64%) rename dlls/{twain_32 => twaindsm}/tests/dsm.c (99%) rename dlls/{twain_32 => twaindsm}/twain.rc (100%) rename dlls/{twain_32 => twaindsm}/twain32_main.c (100%) rename dlls/{twain_32 => twaindsm}/twain_i.h (100%) create mode 100644 dlls/twaindsm/twaindsm.spec
diff --git a/MAINTAINERS b/MAINTAINERS index c34abcecbc3..6167ff4464e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -325,6 +325,7 @@ F: dlls/uxtheme/ TWAIN P: Esme Povirk esme@codeweavers.com @madewokherd F: dlls/twain_32/ +F: dlls/twaindsm/ F: dlls/sane.ds/ F: dlls/gphoto2.ds/
diff --git a/configure b/configure index 7aebd970364..b9570edfe40 100755 --- a/configure +++ b/configure @@ -1461,6 +1461,7 @@ enable_tdi_sys enable_threadpoolwinrt enable_traffic enable_twain_32 +enable_twaindsm enable_twinapi_appcore enable_tzres enable_ucrtbase @@ -22283,6 +22284,8 @@ enable_msiexec=${enable_msiexec:-yes} enable_netsh=${enable_netsh:-yes} enable_regsvr32=${enable_regsvr32:-yes} enable_rundll32=${enable_rundll32:-yes} +enable_twaindsm=${enable_twaindsm:-yes} +enable_twain_32=${enable_twain_32:-i386}
enable_win16=${enable_win16:-i386} enable_w32skrnl=${enable_w32skrnl:-$enable_win16} @@ -23071,7 +23074,8 @@ wine_fn_config_makefile dlls/toolhelp.dll16 enable_win16 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/twaindsm/tests enable_tests 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 06a3a820e4c..05e2ba85b91 100644 --- a/configure.ac +++ b/configure.ac @@ -2420,6 +2420,8 @@ enable_msiexec=${enable_msiexec:-yes} enable_netsh=${enable_netsh:-yes} enable_regsvr32=${enable_regsvr32:-yes} enable_rundll32=${enable_rundll32:-yes} +enable_twaindsm=${enable_twaindsm:-yes} +enable_twain_32=${enable_twain_32:-i386}
dnl Disable Win16-related modules enable_win16=${enable_win16:-i386} @@ -3211,7 +3213,8 @@ WINE_CONFIG_MAKEFILE(dlls/toolhelp.dll16) 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/twaindsm/tests) WINE_CONFIG_MAKEFILE(dlls/twinapi.appcore) WINE_CONFIG_MAKEFILE(dlls/twinapi.appcore/tests) WINE_CONFIG_MAKEFILE(dlls/typelib.dll16) diff --git a/dlls/twain_32/Makefile.in b/dlls/twain_32/Makefile.in index b4ac1e27729..94225d4cc64 100644 --- a/dlls/twain_32/Makefile.in +++ b/dlls/twain_32/Makefile.in @@ -1,7 +1,5 @@ MODULE = twain_32.dll -IMPORTS = user32 +IMPORTS = user32 twaindsm
SOURCES = \ - dsm_ctrl.c \ - twain.rc \ - twain32_main.c + twain_32.c diff --git a/dlls/twain_32/twain_32.c b/dlls/twain_32/twain_32.c new file mode 100644 index 00000000000..eba57a4578b --- /dev/null +++ b/dlls/twain_32/twain_32.c @@ -0,0 +1,50 @@ +/* + * TWAIN32 functions + * + * Copyright 2025 Bernd Herd codeberg@herdsoft.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * This implements twain_32.dll as a simple wrapper for 32-Bit Intel Windows + * to forward all requests to the platform independent + * main inplementation in twaindsm.dll + */ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "twain.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(twain); + +/* Main entry point for the TWAIN_32.dll library */ +TW_UINT16 WINAPI +TWAIN_32_DSM_Entry ( + pTW_IDENTITY pOrigin, + pTW_IDENTITY pDest, + TW_UINT32 DG, + TW_UINT16 DAT, + TW_UINT16 MSG, + TW_MEMREF pData) +{ + TRACE("twain_32 (DG=%ld DAT=%d MSG=%d)\n", DG, DAT, MSG); + + /* Just forward the call to the twaindsm.dll */ + return DSM_Entry(pOrigin, pDest, DG, DAT, MSG, pData); +} diff --git a/dlls/twain_32/twain_32.spec b/dlls/twain_32/twain_32.spec index fe79c9f19dc..cc2fc585157 100644 --- a/dlls/twain_32/twain_32.spec +++ b/dlls/twain_32/twain_32.spec @@ -1 +1 @@ -@ stdcall DSM_Entry(ptr ptr long long long ptr) +@ stdcall DSM_Entry(ptr ptr long long long ptr) TWAIN_32_DSM_Entry diff --git a/dlls/twaindsm/Makefile.in b/dlls/twaindsm/Makefile.in new file mode 100644 index 00000000000..ad8053803da --- /dev/null +++ b/dlls/twaindsm/Makefile.in @@ -0,0 +1,8 @@ +MODULE = twaindsm.dll +IMPORTLIB = twaindsm +IMPORTS = user32 + +SOURCES = \ + dsm_ctrl.c \ + twain.rc \ + twain32_main.c diff --git a/dlls/twain_32/dsm_ctrl.c b/dlls/twaindsm/dsm_ctrl.c similarity index 94% rename from dlls/twain_32/dsm_ctrl.c rename to dlls/twaindsm/dsm_ctrl.c index 89e788f979d..c77f4f32b6b 100644 --- a/dlls/twain_32/dsm_ctrl.c +++ b/dlls/twaindsm/dsm_ctrl.c @@ -47,6 +47,11 @@ struct all_devices { static int nrdevices = 0; static struct all_devices *devices = NULL;
+#ifndef WIN64 +/* Instance-Handle of the twain_32.dll */ +HINSTANCE hinstTwain_32 = NULL; +#endif // !WIN64 + static void twain_add_onedriver(const WCHAR *dsname) { HMODULE hmod; @@ -276,6 +281,12 @@ TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData) /* This causes crashes due to still open Windows, so leave out for now. * FreeLibrary (currentDS->hmod); */ +#ifndef WIN64 + if (!(pIdentity->SupportedGroups & DF_DS2)) { + FreeLibrary(hinstTwain_32); + } +#endif // !WIN64 + if (prevDS) prevDS->next = currentDS->next; else @@ -381,8 +392,34 @@ 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. */ + newSource->dsEntry (pOrigin, DG_CONTROL, DAT_ENTRYPOINT, MSG_SET, (TW_ENTRYPOINT *) &_entrypoints); + } +#ifndef WIN64 + else { + /* DS is Version 1.x. Make sure twain_32.dll is loaded in case DS uses GetModuleHandle("twain_32") */ + hinstTwain_32 = LoadLibraryW(L"twain_32.dll"); + } +#endif // !WIN64 + /* 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); @@ -398,16 +435,6 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData) newSource->event_window = NULL; 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; }
diff --git a/dlls/twain_32/resource.h b/dlls/twaindsm/resource.h similarity index 100% rename from dlls/twain_32/resource.h rename to dlls/twaindsm/resource.h diff --git a/dlls/twain_32/tests/Makefile.in b/dlls/twaindsm/tests/Makefile.in similarity index 64% rename from dlls/twain_32/tests/Makefile.in rename to dlls/twaindsm/tests/Makefile.in index 13052327d7d..11cc60fef8b 100644 --- a/dlls/twain_32/tests/Makefile.in +++ b/dlls/twaindsm/tests/Makefile.in @@ -1,4 +1,4 @@ -TESTDLL = twain_32.dll +TESTDLL = twaindsm.dll IMPORTS = user32 gdi32
SOURCES = \ diff --git a/dlls/twain_32/tests/dsm.c b/dlls/twaindsm/tests/dsm.c similarity index 99% rename from dlls/twain_32/tests/dsm.c rename to dlls/twaindsm/tests/dsm.c index f12110c664b..85893ed4827 100644 --- a/dlls/twain_32/tests/dsm.c +++ b/dlls/twaindsm/tests/dsm.c @@ -845,10 +845,10 @@ START_TEST(dsm) return; }
- htwain = LoadLibraryA("twain_32.dll"); + htwain = LoadLibraryA("twaindsm.dll"); if (! htwain) { - win_skip("twain_32.dll not available, skipping tests\n"); + win_skip("twaindsm.dll not available, skipping tests\n"); return; } pDSM_Entry = (void*)GetProcAddress(htwain, "DSM_Entry"); diff --git a/dlls/twain_32/twain.rc b/dlls/twaindsm/twain.rc similarity index 100% rename from dlls/twain_32/twain.rc rename to dlls/twaindsm/twain.rc diff --git a/dlls/twain_32/twain32_main.c b/dlls/twaindsm/twain32_main.c similarity index 100% rename from dlls/twain_32/twain32_main.c rename to dlls/twaindsm/twain32_main.c diff --git a/dlls/twain_32/twain_i.h b/dlls/twaindsm/twain_i.h similarity index 100% rename from dlls/twain_32/twain_i.h rename to dlls/twaindsm/twain_i.h 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)