[PATCH v2 0/6] MR10078: cfgmgr32: Implement CM_Enumerate_Classes(_Ex).
-- v2: cfgmgr32: Implement CM_Enumerate_Classes(_Ex). cfgmgr32: Cache some common root registry keys. cfgmgr32: Implement CM_Open_Class_Key(_Ex)(A|W). cfgmgr32: Implement CM_Get_Class_Key_Name(_Ex)(A|W). cfgmgr32: Share sources with setupapi. cfgmgr32: Split sources to separate files. https://gitlab.winehq.org/wine/wine/-/merge_requests/10078
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/Makefile.in | 4 +- dlls/cfgmgr32/cfgmgr32.c | 106 ++++++++++ dlls/cfgmgr32/cfgmgr32_private.h | 51 +++++ dlls/cfgmgr32/{main.c => devobject.c} | 293 +------------------------- dlls/cfgmgr32/notification.c | 203 ++++++++++++++++++ 5 files changed, 364 insertions(+), 293 deletions(-) create mode 100644 dlls/cfgmgr32/cfgmgr32.c create mode 100644 dlls/cfgmgr32/cfgmgr32_private.h rename dlls/cfgmgr32/{main.c => devobject.c} (80%) create mode 100644 dlls/cfgmgr32/notification.c diff --git a/dlls/cfgmgr32/Makefile.in b/dlls/cfgmgr32/Makefile.in index fd819760e6f..1c04637124f 100644 --- a/dlls/cfgmgr32/Makefile.in +++ b/dlls/cfgmgr32/Makefile.in @@ -3,4 +3,6 @@ IMPORTLIB = cfgmgr32 IMPORTS = advapi32 rpcrt4 sechost setupapi SOURCES = \ - main.c + cfgmgr32.c \ + devobject.c \ + notification.c diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c new file mode 100644 index 00000000000..7362069490a --- /dev/null +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2023 Mohamad Al-Jaf + * Copyright (C) 2025 Vibhav Pant + * + * 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 + */ + +#include "cfgmgr32_private.h" +#include "initguid.h" +#include "devpkey.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/*********************************************************************** + * CM_MapCrToWin32Err (cfgmgr32.@) + */ +DWORD WINAPI CM_MapCrToWin32Err( CONFIGRET code, DWORD default_error ) +{ + TRACE( "code: %#lx, default_error: %ld\n", code, default_error ); + + switch (code) + { + case CR_SUCCESS: return ERROR_SUCCESS; + case CR_OUT_OF_MEMORY: return ERROR_NOT_ENOUGH_MEMORY; + case CR_INVALID_POINTER: return ERROR_INVALID_USER_BUFFER; + case CR_INVALID_FLAG: return ERROR_INVALID_FLAGS; + case CR_INVALID_DEVNODE: + case CR_INVALID_DEVICE_ID: + case CR_INVALID_MACHINENAME: + case CR_INVALID_PROPERTY: + case CR_INVALID_REFERENCE_STRING: return ERROR_INVALID_DATA; + case CR_NO_SUCH_DEVNODE: + case CR_NO_SUCH_VALUE: + case CR_NO_SUCH_DEVICE_INTERFACE: return ERROR_NOT_FOUND; + case CR_ALREADY_SUCH_DEVNODE: return ERROR_ALREADY_EXISTS; + case CR_BUFFER_SMALL: return ERROR_INSUFFICIENT_BUFFER; + case CR_NO_REGISTRY_HANDLE: return ERROR_INVALID_HANDLE; + case CR_REGISTRY_ERROR: return ERROR_REGISTRY_CORRUPT; + case CR_NO_SUCH_REGISTRY_KEY: return ERROR_FILE_NOT_FOUND; + case CR_REMOTE_COMM_FAILURE: + case CR_MACHINE_UNAVAILABLE: + case CR_NO_CM_SERVICES: return ERROR_SERVICE_NOT_ACTIVE; + case CR_ACCESS_DENIED: return ERROR_ACCESS_DENIED; + case CR_CALL_NOT_IMPLEMENTED: return ERROR_CALL_NOT_IMPLEMENTED; + } + + return default_error; +} + +/*********************************************************************** + * CM_Get_Device_Interface_PropertyW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( LPCWSTR device_interface, const DEVPROPKEY *property_key, + DEVPROPTYPE *property_type, BYTE *property_buffer, + ULONG *property_buffer_size, ULONG flags ) +{ + SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; + HDEVINFO set; + DWORD err; + BOOL ret; + + TRACE( "%s %p %p %p %p %ld.\n", debugstr_w(device_interface), property_key, property_type, property_buffer, + property_buffer_size, flags); + + if (!property_key) return CR_FAILURE; + if (!device_interface || !property_type || !property_buffer_size) return CR_INVALID_POINTER; + if (*property_buffer_size && !property_buffer) return CR_INVALID_POINTER; + if (flags) return CR_INVALID_FLAG; + + set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL ); + if (set == INVALID_HANDLE_VALUE) return CR_OUT_OF_MEMORY; + if (!SetupDiOpenDeviceInterfaceW( set, device_interface, 0, &iface )) + { + SetupDiDestroyDeviceInfoList( set ); + TRACE( "No interface %s, err %lu.\n", debugstr_w( device_interface ), GetLastError()); + return CR_NO_SUCH_DEVICE_INTERFACE; + } + + ret = SetupDiGetDeviceInterfacePropertyW( set, &iface, property_key, property_type, property_buffer, + *property_buffer_size, property_buffer_size, 0 ); + err = ret ? 0 : GetLastError(); + SetupDiDestroyDeviceInfoList( set ); + switch (err) + { + case ERROR_SUCCESS: + return CR_SUCCESS; + case ERROR_INSUFFICIENT_BUFFER: + return CR_BUFFER_SMALL; + case ERROR_NOT_FOUND: + return CR_NO_SUCH_VALUE; + default: + return CR_FAILURE; + } +} diff --git a/dlls/cfgmgr32/cfgmgr32_private.h b/dlls/cfgmgr32/cfgmgr32_private.h new file mode 100644 index 00000000000..0aad9a3f247 --- /dev/null +++ b/dlls/cfgmgr32/cfgmgr32_private.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Mohamad Al-Jaf + * Copyright (C) 2025 Vibhav Pant + * + * 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 + */ + +#include <stddef.h> +#include <stdarg.h> +#include <assert.h> + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winuser.h" + +#include "cfgmgr32.h" +#include "setupapi.h" +#include "dbt.h" +#include "devfiltertypes.h" +#include "devquery.h" + +#include "wine/plugplay.h" +#include "wine/rbtree.h" +#include "wine/debug.h" + +static inline const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) +{ + if (!key) return "(null)"; + return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); +} + +static inline const char *debugstr_DEVPROPCOMPKEY( const DEVPROPCOMPKEY *key ) +{ + if (!key) return "(null)"; + return wine_dbg_sprintf( "{%s, %d, %s}", debugstr_DEVPROPKEY( &key->Key ), key->Store, + debugstr_w( key->LocaleName ) ); +} diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/devobject.c similarity index 80% rename from dlls/cfgmgr32/main.c rename to dlls/cfgmgr32/devobject.c index 8ae08e7c7e8..c4934f6590a 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/devobject.c @@ -17,289 +17,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <assert.h> - -#include "wine/debug.h" -#include "wine/rbtree.h" -#include "winreg.h" -#include "winternl.h" -#include "cfgmgr32.h" -#include "winuser.h" -#include "dbt.h" -#include "wine/plugplay.h" -#include "setupapi.h" -#include "devfiltertypes.h" -#include "devquery.h" - -#include "initguid.h" +#include "cfgmgr32_private.h" #include "devpkey.h" WINE_DEFAULT_DEBUG_CHANNEL(setupapi); -/*********************************************************************** - * CM_MapCrToWin32Err (cfgmgr32.@) - */ -DWORD WINAPI CM_MapCrToWin32Err( CONFIGRET code, DWORD default_error ) -{ - TRACE( "code: %#lx, default_error: %ld\n", code, default_error ); - - switch (code) - { - case CR_SUCCESS: return ERROR_SUCCESS; - case CR_OUT_OF_MEMORY: return ERROR_NOT_ENOUGH_MEMORY; - case CR_INVALID_POINTER: return ERROR_INVALID_USER_BUFFER; - case CR_INVALID_FLAG: return ERROR_INVALID_FLAGS; - case CR_INVALID_DEVNODE: - case CR_INVALID_DEVICE_ID: - case CR_INVALID_MACHINENAME: - case CR_INVALID_PROPERTY: - case CR_INVALID_REFERENCE_STRING: return ERROR_INVALID_DATA; - case CR_NO_SUCH_DEVNODE: - case CR_NO_SUCH_VALUE: - case CR_NO_SUCH_DEVICE_INTERFACE: return ERROR_NOT_FOUND; - case CR_ALREADY_SUCH_DEVNODE: return ERROR_ALREADY_EXISTS; - case CR_BUFFER_SMALL: return ERROR_INSUFFICIENT_BUFFER; - case CR_NO_REGISTRY_HANDLE: return ERROR_INVALID_HANDLE; - case CR_REGISTRY_ERROR: return ERROR_REGISTRY_CORRUPT; - case CR_NO_SUCH_REGISTRY_KEY: return ERROR_FILE_NOT_FOUND; - case CR_REMOTE_COMM_FAILURE: - case CR_MACHINE_UNAVAILABLE: - case CR_NO_CM_SERVICES: return ERROR_SERVICE_NOT_ACTIVE; - case CR_ACCESS_DENIED: return ERROR_ACCESS_DENIED; - case CR_CALL_NOT_IMPLEMENTED: return ERROR_CALL_NOT_IMPLEMENTED; - } - - return default_error; -} - -struct cm_notify_context -{ - HDEVNOTIFY notify; - void *user_data; - PCM_NOTIFY_CALLBACK callback; -}; - -CALLBACK DWORD devnotify_callback( HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header ) -{ - struct cm_notify_context *ctx = handle; - CM_NOTIFY_EVENT_DATA *event_data; - CM_NOTIFY_ACTION action; - DWORD size, ret; - - TRACE( "(%p, %#lx, %p)\n", handle, flags, header ); - - switch (flags) - { - case DBT_DEVICEARRIVAL: - action = CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL; - break; - case DBT_DEVICEREMOVECOMPLETE: - FIXME( "CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE not implemented\n" ); - action = CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL; - break; - case DBT_CUSTOMEVENT: - action = CM_NOTIFY_ACTION_DEVICECUSTOMEVENT; - break; - default: - FIXME( "Unexpected flags value: %#lx\n", flags ); - return 0; - } - - switch (header->dbch_devicetype) - { - case DBT_DEVTYP_DEVICEINTERFACE: - { - const DEV_BROADCAST_DEVICEINTERFACE_W *iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)header; - UINT data_size = wcslen( iface->dbcc_name ) + 1; - - size = offsetof( CM_NOTIFY_EVENT_DATA, u.DeviceInterface.SymbolicLink[data_size] ); - if (!(event_data = calloc( 1, size ))) return 0; - - event_data->FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE; - event_data->u.DeviceInterface.ClassGuid = iface->dbcc_classguid; - memcpy( event_data->u.DeviceInterface.SymbolicLink, iface->dbcc_name, data_size * sizeof(WCHAR) ); - break; - } - case DBT_DEVTYP_HANDLE: - { - const DEV_BROADCAST_HANDLE *handle = (DEV_BROADCAST_HANDLE *)header; - UINT data_size = handle->dbch_size - 2 * sizeof(WCHAR) - offsetof( DEV_BROADCAST_HANDLE, dbch_data ); - - size = offsetof( CM_NOTIFY_EVENT_DATA, u.DeviceHandle.Data[data_size] ); - if (!(event_data = calloc( 1, size ))) return 0; - - event_data->FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE; - event_data->u.DeviceHandle.EventGuid = handle->dbch_eventguid; - event_data->u.DeviceHandle.NameOffset = handle->dbch_nameoffset; - event_data->u.DeviceHandle.DataSize = data_size; - memcpy( event_data->u.DeviceHandle.Data, handle->dbch_data, data_size ); - break; - } - default: - FIXME( "Unexpected devicetype value: %#lx\n", header->dbch_devicetype ); - return 0; - } - - ret = ctx->callback( ctx, ctx->user_data, action, event_data, size ); - free( event_data ); - return ret; -} - -static const char *debugstr_CM_NOTIFY_FILTER( const CM_NOTIFY_FILTER *filter ) -{ - if (!filter) return "(null)"; - switch (filter->FilterType) - { - case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE: - return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE %lu {{%s}}}", filter->cbSize, - filter->Flags, filter->Reserved, - debugstr_guid( &filter->u.DeviceInterface.ClassGuid ) ); - case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE: - return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%p}}}", filter->cbSize, - filter->Flags, filter->Reserved, filter->u.DeviceHandle.hTarget ); - case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE: - return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE %lu {{%s}}}", filter->cbSize, - filter->Flags, filter->Reserved, debugstr_w( filter->u.DeviceInstance.InstanceId ) ); - default: - return wine_dbg_sprintf( "{%#lx %lx (unknown FilterType %d) %lu}", filter->cbSize, filter->Flags, - filter->FilterType, filter->Reserved ); - } -} - -static CONFIGRET create_notify_context( const CM_NOTIFY_FILTER *filter, HCMNOTIFICATION *notify_handle, - PCM_NOTIFY_CALLBACK callback, void *user_data ) -{ - union - { - DEV_BROADCAST_HDR header; - DEV_BROADCAST_DEVICEINTERFACE_W iface; - DEV_BROADCAST_HANDLE handle; - } notify_filter = {0}; - struct cm_notify_context *ctx; - static const GUID GUID_NULL; - - switch (filter->FilterType) - { - case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE: - notify_filter.iface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - if (filter->Flags & CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES) - { - if (!IsEqualGUID( &filter->u.DeviceInterface.ClassGuid, &GUID_NULL )) return CR_INVALID_DATA; - notify_filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_classguid ); - } - else - { - notify_filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name ); - notify_filter.iface.dbcc_classguid = filter->u.DeviceInterface.ClassGuid; - } - break; - case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE: - notify_filter.handle.dbch_devicetype = DBT_DEVTYP_HANDLE; - notify_filter.handle.dbch_size = sizeof(notify_filter.handle); - notify_filter.handle.dbch_handle = filter->u.DeviceHandle.hTarget; - break; - case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE: - FIXME( "CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE is not supported!\n" ); - return CR_CALL_NOT_IMPLEMENTED; - default: - return CR_INVALID_DATA; - } - - if (!(ctx = calloc( 1, sizeof(*ctx) ))) return CR_OUT_OF_MEMORY; - - ctx->user_data = user_data; - ctx->callback = callback; - if (!(ctx->notify = I_ScRegisterDeviceNotification( ctx, ¬ify_filter.header, devnotify_callback ))) - { - free( ctx ); - switch (GetLastError()) - { - case ERROR_NOT_ENOUGH_MEMORY: return CR_OUT_OF_MEMORY; - case ERROR_INVALID_PARAMETER: return CR_INVALID_DATA; - default: return CR_FAILURE; - } - } - *notify_handle = ctx; - return CR_SUCCESS; -} - -/*********************************************************************** - * CM_Register_Notification (cfgmgr32.@) - */ -CONFIGRET WINAPI CM_Register_Notification( CM_NOTIFY_FILTER *filter, void *context, - PCM_NOTIFY_CALLBACK callback, HCMNOTIFICATION *notify_context ) -{ - TRACE( "(%s %p %p %p)\n", debugstr_CM_NOTIFY_FILTER( filter ), context, callback, notify_context ); - - if (!notify_context) return CR_FAILURE; - if (!filter || !callback || filter->cbSize != sizeof(*filter)) return CR_INVALID_DATA; - - return create_notify_context( filter, notify_context, callback, context ); -} - -/*********************************************************************** - * CM_Unregister_Notification (cfgmgr32.@) - */ -CONFIGRET WINAPI CM_Unregister_Notification( HCMNOTIFICATION notify ) -{ - struct cm_notify_context *ctx = notify; - - TRACE( "(%p)\n", notify ); - - if (!notify) return CR_INVALID_DATA; - - I_ScUnregisterDeviceNotification( ctx->notify ); - free( ctx ); - - return CR_SUCCESS; -} - -/*********************************************************************** - * CM_Get_Device_Interface_PropertyW (cfgmgr32.@) - */ -CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( LPCWSTR device_interface, const DEVPROPKEY *property_key, - DEVPROPTYPE *property_type, BYTE *property_buffer, - ULONG *property_buffer_size, ULONG flags ) -{ - SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; - HDEVINFO set; - DWORD err; - BOOL ret; - - TRACE( "%s %p %p %p %p %ld.\n", debugstr_w(device_interface), property_key, property_type, property_buffer, - property_buffer_size, flags); - - if (!property_key) return CR_FAILURE; - if (!device_interface || !property_type || !property_buffer_size) return CR_INVALID_POINTER; - if (*property_buffer_size && !property_buffer) return CR_INVALID_POINTER; - if (flags) return CR_INVALID_FLAG; - - set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL ); - if (set == INVALID_HANDLE_VALUE) return CR_OUT_OF_MEMORY; - if (!SetupDiOpenDeviceInterfaceW( set, device_interface, 0, &iface )) - { - SetupDiDestroyDeviceInfoList( set ); - TRACE( "No interface %s, err %lu.\n", debugstr_w( device_interface ), GetLastError()); - return CR_NO_SUCH_DEVICE_INTERFACE; - } - - ret = SetupDiGetDeviceInterfacePropertyW( set, &iface, property_key, property_type, property_buffer, - *property_buffer_size, property_buffer_size, 0 ); - err = ret ? 0 : GetLastError(); - SetupDiDestroyDeviceInfoList( set ); - switch (err) - { - case ERROR_SUCCESS: - return CR_SUCCESS; - case ERROR_INSUFFICIENT_BUFFER: - return CR_BUFFER_SMALL; - case ERROR_NOT_FOUND: - return CR_NO_SUCH_VALUE; - default: - return CR_FAILURE; - } -} - static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, const DEVPROPKEY *key, DEVPROPTYPE type, ULONG buf_size, void *buf ) { @@ -321,19 +43,6 @@ static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, c return TRUE; } -static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key ) -{ - if (!key) return "(null)"; - return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid ); -} - -static const char *debugstr_DEVPROPCOMPKEY( const DEVPROPCOMPKEY *key ) -{ - if (!key) return "(null)"; - return wine_dbg_sprintf( "{%s, %d, %s}", debugstr_DEVPROPKEY( &key->Key ), key->Store, - debugstr_w( key->LocaleName ) ); -} - static const char *debugstr_DEV_OBJECT( const DEV_OBJECT *obj ) { if (!obj) return "(null)"; diff --git a/dlls/cfgmgr32/notification.c b/dlls/cfgmgr32/notification.c new file mode 100644 index 00000000000..e0b3893430f --- /dev/null +++ b/dlls/cfgmgr32/notification.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2023 Mohamad Al-Jaf + * Copyright (C) 2025 Vibhav Pant + * + * 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 + */ + +#include "cfgmgr32_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +static const char *debugstr_CM_NOTIFY_FILTER( const CM_NOTIFY_FILTER *filter ) +{ + if (!filter) return "(null)"; + switch (filter->FilterType) + { + case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE: + return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE %lu {{%s}}}", filter->cbSize, + filter->Flags, filter->Reserved, debugstr_guid( &filter->u.DeviceInterface.ClassGuid ) ); + case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE: + return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%p}}}", filter->cbSize, + filter->Flags, filter->Reserved, filter->u.DeviceHandle.hTarget ); + case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE: + return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE %lu {{%s}}}", filter->cbSize, + filter->Flags, filter->Reserved, debugstr_w( filter->u.DeviceInstance.InstanceId ) ); + default: + return wine_dbg_sprintf( "{%#lx %lx (unknown FilterType %d) %lu}", filter->cbSize, filter->Flags, + filter->FilterType, filter->Reserved ); + } +} + +struct cm_notify_context +{ + HDEVNOTIFY notify; + void *user_data; + PCM_NOTIFY_CALLBACK callback; +}; + +CALLBACK DWORD devnotify_callback( HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header ) +{ + struct cm_notify_context *ctx = handle; + CM_NOTIFY_EVENT_DATA *event_data; + CM_NOTIFY_ACTION action; + DWORD size, ret; + + TRACE( "(%p, %#lx, %p)\n", handle, flags, header ); + + switch (flags) + { + case DBT_DEVICEARRIVAL: + action = CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL; + break; + case DBT_DEVICEREMOVECOMPLETE: + FIXME( "CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE not implemented\n" ); + action = CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL; + break; + case DBT_CUSTOMEVENT: + action = CM_NOTIFY_ACTION_DEVICECUSTOMEVENT; + break; + default: + FIXME( "Unexpected flags value: %#lx\n", flags ); + return 0; + } + + switch (header->dbch_devicetype) + { + case DBT_DEVTYP_DEVICEINTERFACE: + { + const DEV_BROADCAST_DEVICEINTERFACE_W *iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)header; + UINT data_size = wcslen( iface->dbcc_name ) + 1; + + size = offsetof( CM_NOTIFY_EVENT_DATA, u.DeviceInterface.SymbolicLink[data_size] ); + if (!(event_data = calloc( 1, size ))) return 0; + + event_data->FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE; + event_data->u.DeviceInterface.ClassGuid = iface->dbcc_classguid; + memcpy( event_data->u.DeviceInterface.SymbolicLink, iface->dbcc_name, data_size * sizeof(WCHAR) ); + break; + } + case DBT_DEVTYP_HANDLE: + { + const DEV_BROADCAST_HANDLE *handle = (DEV_BROADCAST_HANDLE *)header; + UINT data_size = handle->dbch_size - 2 * sizeof(WCHAR) - offsetof( DEV_BROADCAST_HANDLE, dbch_data ); + + size = offsetof( CM_NOTIFY_EVENT_DATA, u.DeviceHandle.Data[data_size] ); + if (!(event_data = calloc( 1, size ))) return 0; + + event_data->FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE; + event_data->u.DeviceHandle.EventGuid = handle->dbch_eventguid; + event_data->u.DeviceHandle.NameOffset = handle->dbch_nameoffset; + event_data->u.DeviceHandle.DataSize = data_size; + memcpy( event_data->u.DeviceHandle.Data, handle->dbch_data, data_size ); + break; + } + default: + FIXME( "Unexpected devicetype value: %#lx\n", header->dbch_devicetype ); + return 0; + } + + ret = ctx->callback( ctx, ctx->user_data, action, event_data, size ); + free( event_data ); + return ret; +} + +static CONFIGRET create_notify_context( const CM_NOTIFY_FILTER *filter, HCMNOTIFICATION *notify_handle, + PCM_NOTIFY_CALLBACK callback, void *user_data ) +{ + union + { + DEV_BROADCAST_HDR header; + DEV_BROADCAST_DEVICEINTERFACE_W iface; + DEV_BROADCAST_HANDLE handle; + } notify_filter = {0}; + struct cm_notify_context *ctx; + static const GUID GUID_NULL; + + switch (filter->FilterType) + { + case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE: + notify_filter.iface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + if (filter->Flags & CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES) + { + if (!IsEqualGUID( &filter->u.DeviceInterface.ClassGuid, &GUID_NULL )) return CR_INVALID_DATA; + notify_filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_classguid ); + } + else + { + notify_filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name ); + notify_filter.iface.dbcc_classguid = filter->u.DeviceInterface.ClassGuid; + } + break; + case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE: + notify_filter.handle.dbch_devicetype = DBT_DEVTYP_HANDLE; + notify_filter.handle.dbch_size = sizeof(notify_filter.handle); + notify_filter.handle.dbch_handle = filter->u.DeviceHandle.hTarget; + break; + case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE: + FIXME( "CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE is not supported!\n" ); + return CR_CALL_NOT_IMPLEMENTED; + default: + return CR_INVALID_DATA; + } + + if (!(ctx = calloc( 1, sizeof(*ctx) ))) return CR_OUT_OF_MEMORY; + + ctx->user_data = user_data; + ctx->callback = callback; + if (!(ctx->notify = I_ScRegisterDeviceNotification( ctx, ¬ify_filter.header, devnotify_callback ))) + { + free( ctx ); + switch (GetLastError()) + { + case ERROR_NOT_ENOUGH_MEMORY: return CR_OUT_OF_MEMORY; + case ERROR_INVALID_PARAMETER: return CR_INVALID_DATA; + default: return CR_FAILURE; + } + } + *notify_handle = ctx; + return CR_SUCCESS; +} + +/*********************************************************************** + * CM_Register_Notification (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Register_Notification( CM_NOTIFY_FILTER *filter, void *context, + PCM_NOTIFY_CALLBACK callback, HCMNOTIFICATION *notify_context ) +{ + TRACE( "(%s %p %p %p)\n", debugstr_CM_NOTIFY_FILTER( filter ), context, callback, notify_context ); + + if (!notify_context) return CR_FAILURE; + if (!filter || !callback || filter->cbSize != sizeof(*filter)) return CR_INVALID_DATA; + + return create_notify_context( filter, notify_context, callback, context ); +} + +/*********************************************************************** + * CM_Unregister_Notification (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Unregister_Notification( HCMNOTIFICATION notify ) +{ + struct cm_notify_context *ctx = notify; + + TRACE( "(%p)\n", notify ); + + if (!notify) return CR_INVALID_DATA; + + I_ScUnregisterDeviceNotification( ctx->notify ); + free( ctx ); + + return CR_SUCCESS; +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10078
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/setupapi/Makefile.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/setupapi/Makefile.in b/dlls/setupapi/Makefile.in index 4a2d1109707..8fbd4a9046c 100644 --- a/dlls/setupapi/Makefile.in +++ b/dlls/setupapi/Makefile.in @@ -3,8 +3,10 @@ MODULE = setupapi.dll IMPORTLIB = setupapi IMPORTS = uuid advapi32 rpcrt4 kernelbase DELAYIMPORTS = cabinet shell32 wintrust ole32 comdlg32 user32 +PARENTSRC = ../cfgmgr32 SOURCES = \ + cfgmgr32.c \ devinst.c \ dialog.c \ dirid.c \ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10078
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 58 ++++++++++++++++++ dlls/cfgmgr32/cfgmgr32.spec | 8 +-- dlls/cfgmgr32/tests/cfgmgr32.c | 105 ++++++++++++++++++++++++++++++++- dlls/setupapi/setupapi.spec | 8 +-- 4 files changed, 169 insertions(+), 10 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index 7362069490a..f01b2aabd3e 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -23,6 +23,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(setupapi); +static const WCHAR *guid_string( const GUID *guid, WCHAR *buffer, UINT length ) +{ + swprintf( buffer, length, L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], + guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7] ); + return buffer; +} + /*********************************************************************** * CM_MapCrToWin32Err (cfgmgr32.@) */ @@ -59,6 +67,56 @@ DWORD WINAPI CM_MapCrToWin32Err( CONFIGRET code, DWORD default_error ) return default_error; } +/*********************************************************************** + * CM_Get_Class_Key_Name_ExW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Class_Key_Name_ExW( GUID *guid, WCHAR *name, ULONG *len, ULONG flags, HMACHINE machine ) +{ + UINT capacity; + + TRACE( "guid %s, name %p, len %p, flags %#lx, machine %p\n", debugstr_guid(guid), name, len, flags, machine ); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + if (flags) FIXME( "flags %#lx not implemented!\n", flags ); + + if (!guid || !len) return CR_INVALID_POINTER; + if ((capacity = *len) && !name) return CR_INVALID_POINTER; + + *len = 39; + if (capacity < *len) return CR_BUFFER_SMALL; + guid_string( guid, name, capacity ); + return CR_SUCCESS; +} + +/*********************************************************************** + * CM_Get_Class_Key_Name_ExA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Class_Key_Name_ExA( GUID *class, char *nameA, ULONG *len, ULONG flags, HMACHINE machine ) +{ + WCHAR nameW[39]; + CONFIGRET ret; + + if ((ret = CM_Get_Class_Key_Name_ExW( class, nameA ? nameW : NULL, len, flags, machine ))) return ret; + if (nameA) WideCharToMultiByte( CP_ACP, 0, nameW, 39, nameA, 39, NULL, NULL ); + + return CR_SUCCESS; +} + +/*********************************************************************** + * CM_Get_Class_Key_NameW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Class_Key_NameW( GUID *class, WCHAR *name, ULONG *len, ULONG flags ) +{ + return CM_Get_Class_Key_Name_ExW( class, name, len, flags, NULL ); +} + +/*********************************************************************** + * CM_Get_Class_Key_NameA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Get_Class_Key_NameA( GUID *class, char *name, ULONG *len, ULONG flags ) +{ + return CM_Get_Class_Key_Name_ExA( class, name, len, flags, NULL ); +} + /*********************************************************************** * CM_Get_Device_Interface_PropertyW (cfgmgr32.@) */ diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 862045663b6..c5b6107e736 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -64,10 +64,10 @@ @ stub CM_Free_Resource_Conflict_Handle @ stdcall CM_Get_Child(ptr long long) setupapi.CM_Get_Child @ stdcall CM_Get_Child_Ex(ptr long long ptr) setupapi.CM_Get_Child_Ex -@ stub CM_Get_Class_Key_NameA -@ stub CM_Get_Class_Key_NameW -@ stub CM_Get_Class_Key_Name_ExA -@ stub CM_Get_Class_Key_Name_ExW +@ stdcall CM_Get_Class_Key_NameA(ptr ptr ptr long) +@ stdcall CM_Get_Class_Key_NameW(ptr ptr ptr long) +@ stdcall CM_Get_Class_Key_Name_ExA(ptr ptr ptr long ptr) +@ stdcall CM_Get_Class_Key_Name_ExW(ptr ptr ptr long ptr) @ stub CM_Get_Class_NameA @ stub CM_Get_Class_NameW @ stub CM_Get_Class_Name_ExA diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 7f529e04269..271c5d515ef 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -17,11 +17,17 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "wine/test.h" -#include "winreg.h" +#include <stddef.h> +#include <stdarg.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" + +#include "winreg.h" #include "winuser.h" +#include "winternl.h" #include "objbase.h" #include "devguid.h" #include "initguid.h" @@ -32,6 +38,38 @@ #include "ntddvdeo.h" #include "devfiltertypes.h" #include "devquery.h" +#include "ddk/hidclass.h" + +#include "wine/test.h" + +static const char *debugstr_ok( const char *cond ) +{ + int c, n = 0; + /* skip possible casts */ + while ((c = *cond++)) + { + if (c == '(') n++; + if (!n) break; + if (c == ')') n--; + } + if (!strchr( cond - 1, '(' )) return wine_dbg_sprintf( "got %s", cond - 1 ); + return wine_dbg_sprintf( "%.*s returned", (int)strcspn( cond - 1, "( " ), cond - 1 ); +} + +#define ok_wcs( e, r ) \ + do \ + { \ + const WCHAR *v = (r); \ + ok( !wcscmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_w(v) ); \ + } while (0) +#define ok_ex( r, op, e, t, f, ... ) \ + do \ + { \ + t v = (r); \ + ok( v op (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ + } while (0) +#define ok_u4( r, op, e ) ok_ex( r, op, e, UINT, "%u" ) +#define ok_x4( r, op, e ) ok_ex( r, op, e, UINT, "%#x" ) static void test_CM_MapCrToWin32Err(void) { @@ -1862,6 +1900,68 @@ static void test_DevFindProperty_invalid( void ) ok( !prop, "got prop %p\n", prop ); } +static void test_CM_Get_Class_Key_Name(void) +{ + GUID guid = GUID_DEVCLASS_DISPLAY; + WCHAR buffer[MAX_PATH]; + CONFIGRET ret; + ULONG len; + + len = ARRAY_SIZE(buffer); + ret = CM_Get_Class_Key_NameW( NULL, buffer, &len, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( len, ==, ARRAY_SIZE(buffer) ); + + ret = CM_Get_Class_Key_NameW( &guid, NULL, NULL, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( len, ==, ARRAY_SIZE(buffer) ); + + ret = CM_Get_Class_Key_NameW( &guid, buffer, NULL, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( len, ==, ARRAY_SIZE(buffer) ); + + ret = CM_Get_Class_Key_NameW( &guid, NULL, &len, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( len, ==, ARRAY_SIZE(buffer) ); + + len = 0; + ret = CM_Get_Class_Key_NameW( &guid, NULL, &len, 0 ); + ok_x4( ret, ==, CR_BUFFER_SMALL ); + ok_u4( len, ==, 39 ); + len = 1; + ret = CM_Get_Class_Key_NameW( &guid, NULL, &len, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + ok_u4( len, ==, 1 ); + + len = 2; + memset( buffer, 0xcd, sizeof(buffer) ); + ret = CM_Get_Class_Key_NameW( &guid, buffer, &len, 0 ); + ok_x4( ret, ==, CR_BUFFER_SMALL ); + ok_u4( len, ==, 39 ); + ok( *buffer == 0xcdcd, "got %s\n", debugstr_wn(buffer, 2) ); + + len = ARRAY_SIZE(buffer); + ret = CM_Get_Class_Key_NameW( &guid, buffer, &len, 0 ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( len, ==, 39 ); + ok_wcs( L"{4d36e968-e325-11ce-bfc1-08002be10318}", buffer ); + + /* doesn't really check anything, it works with any GUID */ + guid = GUID_DEVINTERFACE_DISPLAY_ADAPTER; + len = ARRAY_SIZE(buffer); + ret = CM_Get_Class_Key_NameW( &guid, buffer, &len, 0 ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( len, ==, 39 ); + ok_wcs( L"{5b45201d-f2f2-4f3b-85bb-30ff1f953599}", buffer ); + + memset( &guid, 0xcd, sizeof(guid) ); + len = ARRAY_SIZE(buffer); + ret = CM_Get_Class_Key_NameW( &guid, buffer, &len, 0 ); + ok_x4( ret, ==, CR_SUCCESS ); + ok_u4( len, ==, 39 ); + ok_wcs( L"{cdcdcdcd-cdcd-cdcd-cdcd-cdcdcdcdcdcd}", buffer ); +} + START_TEST(cfgmgr32) { HMODULE mod = GetModuleHandleA("cfgmgr32.dll"); @@ -1874,6 +1974,7 @@ START_TEST(cfgmgr32) pDevFindProperty = (void *)GetProcAddress(mod, "DevFindProperty"); test_CM_MapCrToWin32Err(); + test_CM_Get_Class_Key_Name(); test_CM_Get_Device_ID_List(); test_CM_Register_Notification(); test_CM_Get_Device_Interface_List(); diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec index 9ac1c6c9831..5e68bb1c929 100644 --- a/dlls/setupapi/setupapi.spec +++ b/dlls/setupapi/setupapi.spec @@ -55,10 +55,10 @@ @ stub CM_Free_Res_Des_Handle @ stdcall CM_Get_Child(ptr long long) @ stdcall CM_Get_Child_Ex(ptr long long ptr) -@ stub CM_Get_Class_Key_NameA -@ stub CM_Get_Class_Key_NameW -@ stub CM_Get_Class_Key_Name_ExA -@ stub CM_Get_Class_Key_Name_ExW +@ stdcall CM_Get_Class_Key_NameA(ptr ptr ptr long) +@ stdcall CM_Get_Class_Key_NameW(ptr ptr ptr long) +@ stdcall CM_Get_Class_Key_Name_ExA(ptr ptr ptr long ptr) +@ stdcall CM_Get_Class_Key_Name_ExW(ptr ptr ptr long ptr) @ stub CM_Get_Class_NameA @ stub CM_Get_Class_NameW @ stub CM_Get_Class_Name_ExA -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10078
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 84 ++++++++++++++++++++++++++++++++++ dlls/cfgmgr32/cfgmgr32.spec | 8 ++-- dlls/cfgmgr32/tests/cfgmgr32.c | 73 +++++++++++++++++++++++++++++ dlls/setupapi/setupapi.spec | 8 ++-- include/cfgmgr32.h | 4 ++ 5 files changed, 169 insertions(+), 8 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index f01b2aabd3e..9b144af0052 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -31,6 +31,39 @@ static const WCHAR *guid_string( const GUID *guid, WCHAR *buffer, UINT length ) return buffer; } +static const WCHAR control_classW[] = L"System\\CurrentControlSet\\Control\\Class\\"; +static const WCHAR device_classesW[] = L"System\\CurrentControlSet\\Control\\DeviceClasses\\"; + +static LSTATUS open_key( HKEY root, const WCHAR *key, REGSAM access, BOOL open, HKEY *hkey ) +{ + if (open) return RegOpenKeyExW( root, key, 0, access, hkey ); + return RegCreateKeyExW( root, key, 0, NULL, 0, access, NULL, hkey, NULL ); +} + +static LSTATUS open_class_key( HKEY root, const WCHAR *key, REGSAM access, BOOL open, HKEY *hkey ) +{ + WCHAR path[MAX_PATH]; + swprintf( path, ARRAY_SIZE(path), L"%s%s", control_classW, key ); + return open_key( root, path, access, open, hkey ); +} + +static LSTATUS open_device_classes_key( HKEY root, const WCHAR *key, REGSAM access, BOOL open, HKEY *hkey ) +{ + WCHAR path[MAX_PATH]; + swprintf( path, ARRAY_SIZE(path), L"%s%s", device_classesW, key ); + return open_key( root, path, access, open, hkey ); +} + +static CONFIGRET map_error( LSTATUS err ) +{ + switch (err) + { + case ERROR_FILE_NOT_FOUND: return CR_NO_SUCH_REGISTRY_KEY; + case ERROR_SUCCESS: return CR_SUCCESS; + default: WARN( "unmapped error %lu\n", err ); return CR_FAILURE; + } +} + /*********************************************************************** * CM_MapCrToWin32Err (cfgmgr32.@) */ @@ -117,6 +150,57 @@ CONFIGRET WINAPI CM_Get_Class_Key_NameA( GUID *class, char *name, ULONG *len, UL return CM_Get_Class_Key_Name_ExA( class, name, len, flags, NULL ); } +/*********************************************************************** + * CM_Open_Class_Key_ExW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Open_Class_Key_ExW( GUID *class, const WCHAR *name, REGSAM access, REGDISPOSITION disposition, HKEY *hkey, ULONG flags, HMACHINE machine ) +{ + BOOL open = disposition == RegDisposition_OpenExisting; + WCHAR buffer[39]; + + TRACE( "class %s, name %s, access %#lx, disposition %#lx, hkey %p, flags %#lx\n", debugstr_guid(class), debugstr_w(name), access, disposition, hkey, flags ); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + + if (name) return CR_INVALID_DATA; + if (!hkey) return CR_INVALID_POINTER; + if (flags & ~CM_OPEN_CLASS_KEY_BITS) return CR_INVALID_FLAG; + + if (!class) *buffer = 0; + else guid_string( class, buffer, ARRAY_SIZE(buffer) ); + + if (flags == CM_OPEN_CLASS_KEY_INSTALLER) return map_error( open_class_key( HKEY_LOCAL_MACHINE, buffer, access, open, hkey ) ); + return map_error( open_device_classes_key( HKEY_LOCAL_MACHINE, buffer, access, open, hkey ) ); +} + +/*********************************************************************** + * CM_Open_Class_Key_ExA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Open_Class_Key_ExA( GUID *class, const char *nameA, REGSAM access, REGDISPOSITION disposition, HKEY *hkey, ULONG flags, HMACHINE machine ) +{ + WCHAR nameW[MAX_PATH]; + + TRACE( "guid %s, nameA %s, access %#lx, disposition %#lx, hkey %p, flags %#lx\n", debugstr_guid(class), debugstr_a(nameA), access, disposition, hkey, flags ); + + if (nameA) MultiByteToWideChar( CP_ACP, 0, nameA, -1, nameW, ARRAY_SIZE(nameW) ); + return CM_Open_Class_Key_ExW( class, nameA ? nameW : NULL, access, disposition, hkey, flags, machine ); +} + +/*********************************************************************** + * CM_Open_Class_KeyW (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Open_Class_KeyW( GUID *class, const WCHAR *name, REGSAM access, REGDISPOSITION disposition, HKEY *hkey, ULONG flags ) +{ + return CM_Open_Class_Key_ExW( class, name, access, disposition, hkey, flags, NULL ); +} + +/*********************************************************************** + * CM_Open_Class_KeyA (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Open_Class_KeyA( GUID *class, const char *name, REGSAM access, REGDISPOSITION disposition, HKEY *hkey, ULONG flags ) +{ + return CM_Open_Class_Key_ExA( class, name, access, disposition, hkey, flags, NULL ); +} + /*********************************************************************** * CM_Get_Device_Interface_PropertyW (cfgmgr32.@) */ diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index c5b6107e736..4bafdae1ed1 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -176,10 +176,10 @@ @ stub CM_Move_DevNode @ stub CM_Move_DevNode_Ex @ stub CM_Next_Range -@ stub CM_Open_Class_KeyA -@ stub CM_Open_Class_KeyW -@ stub CM_Open_Class_Key_ExA -@ stub CM_Open_Class_Key_ExW +@ stdcall CM_Open_Class_KeyA(ptr str long long ptr long) +@ stdcall CM_Open_Class_KeyW(ptr wstr long long ptr long) +@ stdcall CM_Open_Class_Key_ExA(ptr str long long ptr long ptr) +@ stdcall CM_Open_Class_Key_ExW(ptr wstr long long ptr long ptr) @ stdcall CM_Open_DevNode_Key(long long long long ptr long) setupapi.CM_Open_DevNode_Key @ stub CM_Open_DevNode_Key_Ex @ stub CM_Open_Device_Interface_KeyA diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 271c5d515ef..0f47bdd1fa8 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -1962,6 +1962,78 @@ static void test_CM_Get_Class_Key_Name(void) ok_wcs( L"{cdcdcdcd-cdcd-cdcd-cdcd-cdcdcdcdcdcd}", buffer ); } +static BOOL compare_unicode_string( const UNICODE_STRING *string, const WCHAR *expect ) +{ + return string->Length == wcslen( expect ) * sizeof(WCHAR) && + !wcsnicmp( string->Buffer, expect, string->Length / sizeof(WCHAR) ); +} + +#define check_object_name( a, b ) _check_object_name( __LINE__, a, b ) +static void _check_object_name( unsigned line, HANDLE handle, const WCHAR *expected_name ) +{ + char buffer[1024]; + UNICODE_STRING *str = (UNICODE_STRING *)buffer, expect; + ULONG len = 0; + NTSTATUS status; + + RtlInitUnicodeString( &expect, expected_name ); + + memset( buffer, 0, sizeof(buffer) ); + status = NtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); + ok_(__FILE__, line)( status == STATUS_SUCCESS, "NtQueryObject failed %lx\n", status ); + ok_(__FILE__, line)( len >= sizeof(OBJECT_NAME_INFORMATION) + str->Length, "unexpected len %lu\n", len ); + ok_(__FILE__, line)( compare_unicode_string( str, expected_name ), "got %s, expected %s\n", + debugstr_w(str->Buffer), debugstr_w(expected_name) ); +} + +static void test_CM_Open_Class_Key(void) +{ + CONFIGRET ret; + GUID guid; + HKEY hkey; + + ret = CM_Open_Class_KeyW( NULL, NULL, KEY_QUERY_VALUE, RegDisposition_OpenExisting, &hkey, CM_OPEN_CLASS_KEY_INSTALLER ); + ok_x4( ret, ==, CR_SUCCESS ); + check_object_name( hkey, L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Control\\Class" ); + RegCloseKey( hkey ); + + ret = CM_Open_Class_KeyW( NULL, NULL, KEY_QUERY_VALUE, RegDisposition_OpenExisting, &hkey, CM_OPEN_CLASS_KEY_INTERFACE ); + ok_x4( ret, ==, CR_SUCCESS ); + check_object_name( hkey, L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Control\\DeviceClasses" ); + RegCloseKey( hkey ); + + guid = GUID_DEVCLASS_DISPLAY; + ret = CM_Open_Class_KeyW( &guid, NULL, KEY_QUERY_VALUE, RegDisposition_OpenExisting, &hkey, CM_OPEN_CLASS_KEY_INSTALLER ); + ok_x4( ret, ==, CR_SUCCESS ); + check_object_name( hkey, L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}" ); + RegCloseKey( hkey ); + + guid = GUID_DEVINTERFACE_DISPLAY_ADAPTER; + ret = CM_Open_Class_KeyW( &guid, NULL, KEY_QUERY_VALUE, RegDisposition_OpenExisting, &hkey, CM_OPEN_CLASS_KEY_INTERFACE ); + ok_x4( ret, ==, CR_SUCCESS ); + check_object_name( hkey, L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Control\\DeviceClasses\\{5b45201d-f2f2-4f3b-85bb-30ff1f953599}" ); + RegCloseKey( hkey ); + + memset( &guid, 0xcd, sizeof(guid) ); + ret = CM_Open_Class_KeyW( &guid, NULL, KEY_QUERY_VALUE, RegDisposition_OpenExisting, &hkey, CM_OPEN_CLASS_KEY_INSTALLER ); + ok_x4( ret, ==, CR_NO_SUCH_REGISTRY_KEY ); + ret = CM_Open_Class_KeyW( &guid, NULL, KEY_QUERY_VALUE, RegDisposition_OpenAlways, &hkey, CM_OPEN_CLASS_KEY_INSTALLER ); + ok_x4( ret, ==, CR_SUCCESS ); + check_object_name( hkey, L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Control\\Class\\{cdcdcdcd-cdcd-cdcd-cdcd-cdcdcdcdcdcd}" ); + RegCloseKey( hkey ); + ret = RegDeleteKeyW( HKEY_LOCAL_MACHINE, L"SYSTEM\\ControlSet001\\Control\\Class\\{cdcdcdcd-cdcd-cdcd-cdcd-cdcdcdcdcdcd}" ); + ok_x4( ret, ==, ERROR_SUCCESS ); + + ret = CM_Open_Class_KeyW( &guid, NULL, KEY_QUERY_VALUE, RegDisposition_OpenExisting, &hkey, CM_OPEN_CLASS_KEY_INTERFACE ); + ok_x4( ret, ==, CR_NO_SUCH_REGISTRY_KEY ); + ret = CM_Open_Class_KeyW( &guid, NULL, KEY_QUERY_VALUE, RegDisposition_OpenAlways, &hkey, CM_OPEN_CLASS_KEY_INTERFACE ); + ok_x4( ret, ==, CR_SUCCESS ); + check_object_name( hkey, L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Control\\DeviceClasses\\{cdcdcdcd-cdcd-cdcd-cdcd-cdcdcdcdcdcd}" ); + RegCloseKey( hkey ); + ret = RegDeleteKeyW( HKEY_LOCAL_MACHINE, L"SYSTEM\\ControlSet001\\Control\\DeviceClasses\\{cdcdcdcd-cdcd-cdcd-cdcd-cdcdcdcdcdcd}" ); + ok_x4( ret, ==, ERROR_SUCCESS ); +} + START_TEST(cfgmgr32) { HMODULE mod = GetModuleHandleA("cfgmgr32.dll"); @@ -1975,6 +2047,7 @@ START_TEST(cfgmgr32) test_CM_MapCrToWin32Err(); test_CM_Get_Class_Key_Name(); + test_CM_Open_Class_Key(); test_CM_Get_Device_ID_List(); test_CM_Register_Notification(); test_CM_Get_Device_Interface_List(); diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec index 5e68bb1c929..c6dbf897f14 100644 --- a/dlls/setupapi/setupapi.spec +++ b/dlls/setupapi/setupapi.spec @@ -142,10 +142,10 @@ @ stub CM_Move_DevNode @ stub CM_Move_DevNode_Ex @ stub CM_Next_Range -@ stub CM_Open_Class_KeyA -@ stub CM_Open_Class_KeyW -@ stub CM_Open_Class_Key_ExA -@ stub CM_Open_Class_Key_ExW +@ stdcall CM_Open_Class_KeyA(ptr str long long ptr long) +@ stdcall CM_Open_Class_KeyW(ptr wstr long long ptr long) +@ stdcall CM_Open_Class_Key_ExA(ptr str long long ptr long ptr) +@ stdcall CM_Open_Class_Key_ExW(ptr wstr long long ptr long ptr) @ stdcall CM_Open_DevNode_Key(long long long long ptr long) @ stub CM_Open_DevNode_Key_Ex @ stub CM_Query_And_Remove_SubTreeA diff --git a/include/cfgmgr32.h b/include/cfgmgr32.h index e1f417a93a7..29a3673dbe9 100644 --- a/include/cfgmgr32.h +++ b/include/cfgmgr32.h @@ -201,6 +201,10 @@ typedef DWORD CONFIGRET; #define CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES 0x00000001 #define CM_GET_DEVICE_INTERFACE_LIST_BITS 0x00000001 +#define CM_OPEN_CLASS_KEY_INSTALLER 0x00000000 +#define CM_OPEN_CLASS_KEY_INTERFACE 0x00000001 +#define CM_OPEN_CLASS_KEY_BITS 0x00000001 + typedef DWORD DEVINST, *PDEVINST; typedef DWORD DEVNODE, *PDEVNODE; typedef HANDLE HMACHINE, *PHMACHINE; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10078
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index 9b144af0052..2a2e9f96d33 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -34,8 +34,40 @@ static const WCHAR *guid_string( const GUID *guid, WCHAR *buffer, UINT length ) static const WCHAR control_classW[] = L"System\\CurrentControlSet\\Control\\Class\\"; static const WCHAR device_classesW[] = L"System\\CurrentControlSet\\Control\\DeviceClasses\\"; +static struct key_cache +{ + HKEY root; + const WCHAR *prefix; + UINT prefix_len; + HKEY hkey; +} cache[] = +{ + { HKEY_LOCAL_MACHINE, control_classW, ARRAY_SIZE(control_classW) - 1, (HKEY)-1 }, + { HKEY_LOCAL_MACHINE, device_classesW, ARRAY_SIZE(device_classesW) - 1, (HKEY)-1 }, +}; + +static HKEY cache_root_key( HKEY root, const WCHAR *key, const WCHAR **path ) +{ + HKEY hkey; + + for (struct key_cache *entry = cache; entry < cache + ARRAY_SIZE(cache); entry++) + { + if (entry->root != root) continue; + if (wcsnicmp( key, entry->prefix, entry->prefix_len )) continue; + if (path) *path = key + entry->prefix_len; + + if (entry->hkey != (HKEY)-1 || RegOpenKeyExW( root, entry->prefix, 0, KEY_ALL_ACCESS, &hkey )) return entry->hkey; + if (InterlockedCompareExchangePointer( (void *)&entry->hkey, hkey, (HKEY)-1 ) != (HKEY)-1) RegCloseKey( hkey ); + return entry->hkey; + } + + if (path) *path = key; + return root; +} + static LSTATUS open_key( HKEY root, const WCHAR *key, REGSAM access, BOOL open, HKEY *hkey ) { + if ((root = cache_root_key( root, key, &key )) == (HKEY)-1) return ERROR_FILE_NOT_FOUND; if (open) return RegOpenKeyExW( root, key, 0, access, hkey ); return RegCreateKeyExW( root, key, 0, NULL, 0, access, NULL, hkey, NULL ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10078
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/cfgmgr32/cfgmgr32.c | 40 ++++++++++++++++++++++++++++++++++ dlls/cfgmgr32/cfgmgr32.spec | 4 ++-- dlls/cfgmgr32/tests/cfgmgr32.c | 33 ++++++++++++++++++++++++++++ dlls/setupapi/setupapi.spec | 2 +- dlls/setupapi/stubs.c | 9 -------- include/cfgmgr32.h | 4 ++++ 6 files changed, 80 insertions(+), 12 deletions(-) diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c index 2a2e9f96d33..2d85662b47a 100644 --- a/dlls/cfgmgr32/cfgmgr32.c +++ b/dlls/cfgmgr32/cfgmgr32.c @@ -23,6 +23,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(setupapi); +static LSTATUS guid_from_string( const WCHAR *str, GUID *guid ) +{ + UNICODE_STRING guid_str; + RtlInitUnicodeString( &guid_str, str ); + if (RtlGUIDFromString( &guid_str, guid )) return ERROR_INVALID_DATA; + return ERROR_SUCCESS; +} + static const WCHAR *guid_string( const GUID *guid, WCHAR *buffer, UINT length ) { swprintf( buffer, length, L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", @@ -91,6 +99,7 @@ static CONFIGRET map_error( LSTATUS err ) switch (err) { case ERROR_FILE_NOT_FOUND: return CR_NO_SUCH_REGISTRY_KEY; + case ERROR_NO_MORE_ITEMS: return CR_NO_SUCH_VALUE; case ERROR_SUCCESS: return CR_SUCCESS; default: WARN( "unmapped error %lu\n", err ); return CR_FAILURE; } @@ -132,6 +141,37 @@ DWORD WINAPI CM_MapCrToWin32Err( CONFIGRET code, DWORD default_error ) return default_error; } +/*********************************************************************** + * CM_Enumerate_Classes_Ex (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Enumerate_Classes_Ex( ULONG index, GUID *class, ULONG flags, HMACHINE machine ) +{ + WCHAR buffer[39]; + LSTATUS err; + HKEY root; + + TRACE( "index %lu, class %s, flags %#lx, machine %p\n", index, debugstr_guid(class), flags, machine ); + if (machine) FIXME( "machine %p not implemented!\n", machine ); + + if (!class) return CR_INVALID_POINTER; + if (flags & ~CM_ENUMERATE_CLASSES_BITS) return CR_INVALID_FLAG; + + if (flags == CM_ENUMERATE_CLASSES_INSTALLER) root = cache_root_key( HKEY_LOCAL_MACHINE, control_classW, NULL ); + else root = cache_root_key( HKEY_LOCAL_MACHINE, device_classesW, NULL ); + if (root == (HKEY)-1) return CR_NO_SUCH_REGISTRY_KEY; + + if ((err = RegEnumKeyW( root, index, buffer, ARRAY_SIZE(buffer) ))) return map_error( err ); + return map_error( guid_from_string( buffer, class ) ); +} + +/*********************************************************************** + * CM_Enumerate_Classes (cfgmgr32.@) + */ +CONFIGRET WINAPI CM_Enumerate_Classes( ULONG index, GUID *class, ULONG flags ) +{ + return CM_Enumerate_Classes_Ex( index, class, flags, NULL ); +} + /*********************************************************************** * CM_Get_Class_Key_Name_ExW (cfgmgr32.@) */ diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 4bafdae1ed1..4865cadaf53 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -46,8 +46,8 @@ @ stub CM_Duplicate_PowerScheme @ stub CM_Enable_DevNode @ stub CM_Enable_DevNode_Ex -@ stdcall CM_Enumerate_Classes(long ptr long) setupapi.CM_Enumerate_Classes -@ stub CM_Enumerate_Classes_Ex +@ stdcall CM_Enumerate_Classes(long ptr long) +@ stdcall CM_Enumerate_Classes_Ex(long ptr long ptr) @ stub CM_Enumerate_EnumeratorsA @ stub CM_Enumerate_EnumeratorsW @ stub CM_Enumerate_Enumerators_ExA diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 0f47bdd1fa8..c2d9d4d7c34 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -1900,6 +1900,38 @@ static void test_DevFindProperty_invalid( void ) ok( !prop, "got prop %p\n", prop ); } +static void test_CM_Enumerate_Classes(void) +{ + CONFIGRET ret; + GUID guid; + + ret = CM_Enumerate_Classes( 0, NULL, 0 ); + ok_x4( ret, ==, CR_INVALID_POINTER ); + for (UINT flag = 2; flag; flag <<= 1) + { + winetest_push_context( "%#x", flag ); + ret = CM_Enumerate_Classes( 0, &guid, flag ); + ok_x4( ret, ==, CR_INVALID_FLAG ); + winetest_pop_context(); + } + ret = CM_Enumerate_Classes( -1, &guid, 0 ); + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + + for (UINT i = 0; !(ret = CM_Enumerate_Classes( i, &guid, CM_ENUMERATE_CLASSES_INSTALLER )); i++) + if (IsEqualGUID( &guid, &GUID_DEVINTERFACE_HID )) break; + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); + for (UINT i = 0; !(ret = CM_Enumerate_Classes( i, &guid, CM_ENUMERATE_CLASSES_INSTALLER )); i++) + if (IsEqualGUID( &guid, &GUID_DEVCLASS_HIDCLASS )) break; + ok_x4( ret, ==, CR_SUCCESS ); + + for (UINT i = 0; !(ret = CM_Enumerate_Classes( i, &guid, CM_ENUMERATE_CLASSES_INTERFACE )); i++) + if (IsEqualGUID( &guid, &GUID_DEVINTERFACE_HID )) break; + ok_x4( ret, ==, CR_SUCCESS ); + for (UINT i = 0; !(ret = CM_Enumerate_Classes( i, &guid, CM_ENUMERATE_CLASSES_INTERFACE )); i++) + if (IsEqualGUID( &guid, &GUID_DEVCLASS_HIDCLASS )) break; + ok_x4( ret, ==, CR_NO_SUCH_VALUE ); +} + static void test_CM_Get_Class_Key_Name(void) { GUID guid = GUID_DEVCLASS_DISPLAY; @@ -2046,6 +2078,7 @@ START_TEST(cfgmgr32) pDevFindProperty = (void *)GetProcAddress(mod, "DevFindProperty"); test_CM_MapCrToWin32Err(); + test_CM_Enumerate_Classes(); test_CM_Get_Class_Key_Name(); test_CM_Open_Class_Key(); test_CM_Get_Device_ID_List(); diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec index c6dbf897f14..e32ec1b9c09 100644 --- a/dlls/setupapi/setupapi.spec +++ b/dlls/setupapi/setupapi.spec @@ -39,7 +39,7 @@ @ stub CM_Enable_DevNode @ stub CM_Enable_DevNode_Ex @ stdcall CM_Enumerate_Classes(long ptr long) -@ stub CM_Enumerate_Classes_Ex +@ stdcall CM_Enumerate_Classes_Ex(long ptr long ptr) @ stub CM_Enumerate_EnumeratorsA @ stub CM_Enumerate_EnumeratorsW @ stub CM_Enumerate_Enumerators_ExA diff --git a/dlls/setupapi/stubs.c b/dlls/setupapi/stubs.c index 96b12cb8a44..12cf85ad5a6 100644 --- a/dlls/setupapi/stubs.c +++ b/dlls/setupapi/stubs.c @@ -344,15 +344,6 @@ CONFIGRET WINAPI CM_Get_DevNode_Status_Ex(ULONG *status, ULONG *problem, DEVINST return CR_SUCCESS; } -/*********************************************************************** - * CM_Enumerate_Classes (SETUPAPI.@) - */ -CONFIGRET WINAPI CM_Enumerate_Classes(ULONG index, LPGUID class, ULONG flags) -{ - FIXME("%lu %p 0x%08lx: stub\n", index, class, flags); - return CR_NO_SUCH_VALUE; -} - /*********************************************************************** * CM_Get_Class_Registry_PropertyA (SETUPAPI.@) */ diff --git a/include/cfgmgr32.h b/include/cfgmgr32.h index 29a3673dbe9..43a2157f1cf 100644 --- a/include/cfgmgr32.h +++ b/include/cfgmgr32.h @@ -205,6 +205,10 @@ typedef DWORD CONFIGRET; #define CM_OPEN_CLASS_KEY_INTERFACE 0x00000001 #define CM_OPEN_CLASS_KEY_BITS 0x00000001 +#define CM_ENUMERATE_CLASSES_INSTALLER 0x00000000 +#define CM_ENUMERATE_CLASSES_INTERFACE 0x00000001 +#define CM_ENUMERATE_CLASSES_BITS 0x00000001 + typedef DWORD DEVINST, *PDEVINST; typedef DWORD DEVNODE, *PDEVNODE; typedef HANDLE HMACHINE, *PHMACHINE; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10078
v2: Make the implemented functions available in setupapi.spec too. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10078#note_129591
Could you elaborate on why we need to share the source? I assume that the end goal is to have setupapi forward to cfgmgr32 instead of duplicating the implementation, so why can't we do that already for new functions? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10078#note_129594
Yes, ultimately setupapi would import cfgmgr32, but right now it's the other way around. However, cfgmgr32 already has some of its specific functions implemented (which aren't exported from setupapi) over the higher level setupapi functions and there's a dependency circle that's more easily broken by sharing the sources. For instance `CM_Get_Device_Interface_Property` is a lower level function, that only exists in cfgmgr32, and which needs the underlying registry access helpers to be implemented. It cannot even use `CM_Open_Device_Interface_Key` because that one opens `<iface>\\Device Parameters` while `CM_Get_Device_Interface_Property` needs to access `<iface>\\Properties` and other sibling keys. Some other like the `DevObject` API will also need to be re-implemented using the CM functions, which might be possible, but it's actually better and more efficient to using the internal helpers for them too. The one thing that will need to be properly handled without sharing sources is the `CM_Get_Device_ID` API, which is stateful, and that can implemented in setupapi only (cfgmgr32 forwarding calls to it), then moved at once later. I also didn't and still don't understand why it was such a big deal with source sharing when it actually allows use the same helpers in both modules, and avoids having to move code at all. Moving functions from one module to another can be done entirely through the spec file entry mapping: stateless functions can safely be exported from both modules at any time, and cfgmgr32 can import stateful functions from setupapi until we move them all at once by swapping the spec forwards around. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10078#note_129596
I also didn't and still don't understand why it was such a big deal with source sharing when it actually allows use the same helpers in both modules, and avoids having to move code at all. Moving functions from one module to another can be done entirely through the spec file entry mapping: stateless functions can safely be exported from both modules at any time, and cfgmgr32 can import stateful functions from setupapi until we move them all at once by swapping the spec forwards around.
Sure, but note that it would be fine for setupapi to already forward to cfgmgr32 for any function that's properly implemented there, we don't have to do it all at once at the end. Sharing the source works of course, but it's cleaner to stick to the exported API when possible, since it guarantees we are not sharing internal helpers that would have to be removed later. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10078#note_129605
But I don't think that works? I had tried to do that while working on this but it causes lockup (stack overflows IIRC?) on load because of the circular import? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10078#note_129607
Or maybe it does... well, it's been a while so I don't know for sure but I believe I had some trouble doing that. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10078#note_129609
participants (3)
-
Alexandre Julliard (@julliard) -
Rémi Bernon -
Rémi Bernon (@rbernon)