[PATCH 0/1] MR9731: dlls/twaindsm: Save default data source in registry
Solves bugs: #9404 and #79663 This Merge Request changes the Data Source Manager twaindsm.dll alias twain_32.dll to save the data source selected in the selection dialog into the windows registry. This enables applications that do not save the result of the DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT call to be able to choose a data source the same as when using the twain.org DSM. While the twain.org DSM only expects every data source .ds to be responsible for only one data source and thus every call to DG_CONTROL/DAT_IDENTITY/MSG_GET to return the same result, the sane.ds and gphoto2.ds use an extension of the protocol by iterating through cameras/ scanners, always returning a different TW_IDENTITY on every call to DG_CONTROL/DAT_IDENTITY/MSG_GET. For that reason on DG_CONTROL/DAT_IDENTITY/MSG_OPENDS we cannot ask the just opened DS for it's identity as the twain.org DSM does, because it might have more than one Identity. Instead we search the list of data sources retrieved when the DSM was opened for either the identity given by the app or the identity retreived from the registry and give that identity to the MSG_OPENDS call to make the sane.ds open as for the backend we want it to use. This also means that the twaindsm.dll version from twain.org will not be able to manage more than one scanner with sane.ds and one camera with gphoto2.ds as only the wine version of the DSM has the neccessary extension. The twain.org version of the DSM does only save the name of the .DS file choosen by the user to the registry. Since sane.ds and gphoto2.ds can address more than one device, this is not sufficient for us. So the registry entries are not compatible to the ones from the twain.org version. Instead of the filename, we save Manufacturer, ProductFamily and espcially ProductName to the registry under a different registry key. This MR also solves a problem introdoced with commit https://gitlab.winehq.org/wine/wine/-/commit/c7570143bf2305e2292cf5a75abd20c... (by myself) that it was no longer possible to control in DG_CONTROL/DAT_IDENTITY/MSG_OPENDS which of several configured sane data sources are to be opened by sane.ds. A second problem reported in bug report #9404 that crashed the sane.ds when using the user interface with the sane test backend was recently already addressed in commit https://gitlab.winehq.org/wine/wine/-/commit/04449af761451c312f70a71cb62ae7a.... -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9731
From: Bernd Herd <codeberg(a)herdsoft.com> Closes bugs: #9404 and #79663 --- dlls/twaindsm/Makefile.in | 2 +- dlls/twaindsm/dsm_ctrl.c | 191 ++++++++++++++++++++++++++++++++++---- 2 files changed, 174 insertions(+), 19 deletions(-) diff --git a/dlls/twaindsm/Makefile.in b/dlls/twaindsm/Makefile.in index ad8053803da..7e17ee6bd7c 100644 --- a/dlls/twaindsm/Makefile.in +++ b/dlls/twaindsm/Makefile.in @@ -1,6 +1,6 @@ MODULE = twaindsm.dll IMPORTLIB = twaindsm -IMPORTS = user32 +IMPORTS = user32 kernelbase SOURCES = \ dsm_ctrl.c \ diff --git a/dlls/twaindsm/dsm_ctrl.c b/dlls/twaindsm/dsm_ctrl.c index d033c94443a..12a426bde6c 100644 --- a/dlls/twaindsm/dsm_ctrl.c +++ b/dlls/twaindsm/dsm_ctrl.c @@ -26,6 +26,7 @@ #include "windef.h" #include "winbase.h" #include "winuser.h" +#include "winreg.h" #include "twain.h" #include "twain_i.h" #include "resource.h" @@ -230,6 +231,162 @@ static const TW_ENTRYPOINT _entrypoints = { .DSM_MemUnlock = _DSM_MemUnlock }; + +/* Registry key to store the twain default data source to */ +#ifdef WIN64 +# define TWAINDSM_REG_LOC "Software\\Wine\\twaindsm64\\Default Source" +#else +# define TWAINDSM_REG_LOC "Software\\Wine\\twaindsm32\\Default Source" +#endif + +/** @brief Find the default data source. + * + * If the data source stored in the registry is in the + * list of available data sources, return it. + * Otherwise return the first data source autodetected. + * + * @param pOrigin Application Identity + * @param pDest OUT: Data Source identity retrieved. + * @return TWRC_SUCCESS on success + */ +static TW_UINT16 +twain_get_default_ds(pTW_IDENTITY pOrigin, pTW_IDENTITY pDest) +{ + TW_UINT16 twRC = TWRC_FAILURE; + HKEY hKey; + + twain_autodetect(); + + if (RegOpenKeyExA(HKEY_CURRENT_USER, + TWAINDSM_REG_LOC, + 0, + KEY_READ, + &hKey) == ERROR_SUCCESS ) + { + TW_IDENTITY read; + + // Look for the subkey "Default Source". + DWORD dwType; + DWORD dwSizeManufacturer = sizeof(read.Manufacturer); + DWORD dwSizeProductFamily= sizeof(read.ProductFamily); + DWORD dwSizeProductName = sizeof(read.ProductName); + + if (RegQueryValueExA(hKey, + "Manufacturer", + NULL, + &dwType, + (LPBYTE)read.Manufacturer, + &dwSizeManufacturer) == ERROR_SUCCESS && + dwType == REG_SZ && + RegQueryValueExA(hKey, + "ProductFamily", + NULL, + &dwType, + (LPBYTE)read.ProductFamily, + &dwSizeProductFamily) == ERROR_SUCCESS && + dwType == REG_SZ && + RegQueryValueExA(hKey, + "ProductName", + NULL, + &dwType, + (LPBYTE)read.ProductName, + &dwSizeProductName) == ERROR_SUCCESS && + dwType == REG_SZ) + { + /* Scan the lust of available data sources if the one from + * the registry is among them */ + for (int i=0; i<nrdevices && twRC == TWRC_FAILURE; i++) + { + if (!strcmp(devices[i].identity.Manufacturer, read.Manufacturer) && + !strcmp(devices[i].identity.ProductFamily, read.ProductFamily) && + !strcmp(devices[i].identity.ProductName, read.ProductName) ) + { + /* Found a match */ + *pDest = devices[i].identity; + twRC = TWRC_SUCCESS; + } + } + } + + // Close the registry key handle. + RegCloseKey(hKey); + } + + if (twRC == TWRC_FAILURE && + nrdevices) + { + /* No adequate default stored, return the first match available */ + *pDest = devices[0].identity; + twRC = TWRC_SUCCESS; + } + else + { + DSM_twCC = TWCC_NODS; + } + return twRC; +} + + + + +/** @brief Save the seleted default data source to the registry + * + * @param pSource Data source Identity + */ +static void +twain_save_default_ds(const TW_IDENTITY *pSource) +{ + HKEY hKey; + if (RegCreateKeyExA(HKEY_CURRENT_USER, + TWAINDSM_REG_LOC, + 0, + NULL, + 0, + KEY_WRITE, + NULL, + &hKey, + NULL) == ERROR_SUCCESS) + { + if (RegSetValueExA(hKey, + "Manufacturer", + 0, + REG_SZ, + (LPBYTE)pSource->Manufacturer, + strlen(pSource->Manufacturer)+1) == ERROR_SUCCESS && + RegSetValueExA(hKey, + "ProductFamily", + 0, + REG_SZ, + (LPBYTE)pSource->ProductFamily, + strlen(pSource->ProductFamily)+1) == ERROR_SUCCESS && + RegSetValueExA(hKey, + "ProductName", + 0, + REG_SZ, + (LPBYTE)pSource->ProductName, + strlen(pSource->ProductName)+1) == ERROR_SUCCESS) + { + TRACE("Saved new default data source (%s, %s, %s)\n", + pSource->Manufacturer, pSource->ProductFamily, pSource->ProductName); + } + else + { + ERR("Failed to save new default data source\n"); + } + + // Close the registry key handle. + RegCloseKey(hKey); + } + else + { + ERR("Failed to create registry key for default data source\n"); + } +} + + + + + /* DG_CONTROL/DAT_NULL/MSG_CLOSEDSREQ|MSG_DEVICEEVENT|MSG_XFERREADY */ TW_UINT16 TWAIN_ControlNull (pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, activeDS *pSource, TW_UINT16 MSG, TW_MEMREF pData) { @@ -387,13 +544,7 @@ TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData) pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData; TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n"); - DSM_twCC = TWCC_NODS; - twain_autodetect(); - if (!nrdevices) - return TWRC_FAILURE; - *pSourceIdentity = devices[0].identity; - DSM_twCC = TWCC_SUCCESS; - return TWRC_SUCCESS; + return twain_get_default_ds(pOrigin, pSourceIdentity); } /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */ @@ -449,6 +600,10 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData) return TWRC_FAILURE; } + if (!pIdentity->ProductName[0]) { + twain_get_default_ds(pOrigin, pIdentity); + } + if (pIdentity->ProductName[0] != '\0') { /* Make sure the source to be opened exists in the device list */ for (i = 0; i<nrdevices; i++) @@ -458,6 +613,8 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData) i = 0; } /* else use the first device */ + *pIdentity = devices[i].identity; + /* the source is found in the device list */ newSource = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof (activeDS)); if (!newSource) { @@ -482,13 +639,6 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData) } /* 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 @@ -535,6 +685,7 @@ static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, int i; HWND sourcelist; BOOL any_devices = FALSE; + int selected_index = 0; SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)data); @@ -551,14 +702,17 @@ static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, index = SendMessageA(sourcelist, LB_ADDSTRING, 0, (LPARAM)id->ProductName); SendMessageW(sourcelist, LB_SETITEMDATA, (WPARAM)index, (LPARAM)i); any_devices = TRUE; + + if (!strcmp(data->result->ProductName, id->ProductName)) + selected_index = index; } if (any_devices) { EnableWindow(GetDlgItem(hwnd, IDOK), TRUE); - /* FIXME: Select the supplied product name or default source. */ - SendMessageW(sourcelist, LB_SETCURSEL, 0, 0); + /* Select the supplied product name or default source. */ + SendMessageW(sourcelist, LB_SETCURSEL, selected_index, 0); } return TRUE; @@ -590,7 +744,8 @@ static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, *data->result = devices[index].identity; - /* FIXME: Save this as the default source */ + /* Save this as the default source */ + twain_save_default_ds(&devices[index].identity); EndDialog(hwnd, 1); return TRUE; @@ -609,7 +764,7 @@ TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData) TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%lx ProductName=%s\n", pOrigin->SupportedGroups, wine_dbgstr_a(param.result->ProductName)); - twain_autodetect(); + twain_get_default_ds(pOrigin, (TW_IDENTITY *) pData); if (!IsWindow(parent)) parent = NULL; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9731
participants (2)
-
Bernd Herd -
Bernd Herd (@herdsoft)