This is a first try to generalize COM implementations. I find it useful to not repeat all that code for every new implementation.
Feedback appreciated, can we go that route?
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=40206 --- configure | 26 +- configure.ac | 1 + dlls/portabledeviceapi/Makefile.in | 7 + dlls/portabledeviceapi/portabledeviceapi.c | 138 ++++++++++ dlls/portabledeviceapi/portabledeviceapi.spec | 4 + .../portabledeviceapi_classes.idl | 21 ++ .../portabledeviceapi_private.h | 26 ++ include/Makefile.in | 1 + include/portabledeviceapi.idl | 71 +++++ include/wine/com.h | 256 ++++++++++++++++++ 10 files changed, 533 insertions(+), 18 deletions(-) create mode 100644 dlls/portabledeviceapi/Makefile.in create mode 100644 dlls/portabledeviceapi/portabledeviceapi.c create mode 100644 dlls/portabledeviceapi/portabledeviceapi.spec create mode 100644 dlls/portabledeviceapi/portabledeviceapi_classes.idl create mode 100644 dlls/portabledeviceapi/portabledeviceapi_private.h create mode 100644 include/portabledeviceapi.idl create mode 100644 include/wine/com.h
diff --git a/configure b/configure index 7f9786f6eb..b4f79e01cc 100755 --- a/configure +++ b/configure @@ -805,7 +805,6 @@ infodir docdir oldincludedir includedir -runstatedir localstatedir sharedstatedir sysconfdir @@ -1477,6 +1476,7 @@ enable_packager enable_pdh enable_photometadatahandler enable_pidgen +enable_portabledeviceapi enable_powrprof enable_printui enable_prntvpt @@ -1879,7 +1879,6 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -2132,15 +2131,6 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;;
- -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -2278,7 +2268,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir + libdir localedir mandir do eval ac_val=$$ac_var # Remove trailing slashes. @@ -2431,7 +2421,6 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -6822,7 +6811,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6868,7 +6857,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6892,7 +6881,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6937,7 +6926,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6961,7 +6950,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -19746,6 +19735,7 @@ wine_fn_config_makefile dlls/pdh enable_pdh wine_fn_config_makefile dlls/pdh/tests enable_tests wine_fn_config_makefile dlls/photometadatahandler enable_photometadatahandler wine_fn_config_makefile dlls/pidgen enable_pidgen +wine_fn_config_makefile dlls/portabledeviceapi enable_portabledeviceapi wine_fn_config_makefile dlls/powrprof enable_powrprof wine_fn_config_makefile dlls/printui enable_printui wine_fn_config_makefile dlls/prntvpt enable_prntvpt diff --git a/configure.ac b/configure.ac index efec221113..a3aa4cb839 100644 --- a/configure.ac +++ b/configure.ac @@ -3595,6 +3595,7 @@ WINE_CONFIG_MAKEFILE(dlls/pdh) WINE_CONFIG_MAKEFILE(dlls/pdh/tests) WINE_CONFIG_MAKEFILE(dlls/photometadatahandler) WINE_CONFIG_MAKEFILE(dlls/pidgen) +WINE_CONFIG_MAKEFILE(dlls/portabledeviceapi) WINE_CONFIG_MAKEFILE(dlls/powrprof) WINE_CONFIG_MAKEFILE(dlls/printui) WINE_CONFIG_MAKEFILE(dlls/prntvpt) diff --git a/dlls/portabledeviceapi/Makefile.in b/dlls/portabledeviceapi/Makefile.in new file mode 100644 index 0000000000..f57ef8b57b --- /dev/null +++ b/dlls/portabledeviceapi/Makefile.in @@ -0,0 +1,7 @@ +MODULE = portabledeviceapi.dll +IMPORTS = uuid ole32 + +C_SRCS = \ + portabledeviceapi.c + +IDL_SRCS = portabledeviceapi_classes.idl diff --git a/dlls/portabledeviceapi/portabledeviceapi.c b/dlls/portabledeviceapi/portabledeviceapi.c new file mode 100644 index 0000000000..da91af9aca --- /dev/null +++ b/dlls/portabledeviceapi/portabledeviceapi.c @@ -0,0 +1,138 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 "config.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(deviceapi); + +#define WINE_COM_MAIN +#include "wine/com.h" + +#include "portabledeviceapi_private.h" + +#include "initguid.h" +#include "portabledeviceapi.h" + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) +{ + return com_main(instance, reason, reserved); +} + +static const com_object_creation_info object_creation[] = +{ + { &CLSID_PortableDeviceManager, &PortableDeviceManager_create }, +}; + +HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv) +{ + return com_get_class_object(object_creation, ARRAY_SIZE(object_creation), rclsid, riid, ppv); +} + +HRESULT WINAPI DllCanUnloadNow(void) +{ + return S_FALSE; +} + +HRESULT WINAPI DllRegisterServer(void) +{ + return __wine_register_resources(instance_dll); +} + +HRESULT WINAPI DllUnregisterServer(void) +{ + return __wine_unregister_resources(instance_dll); +} + +COM_DEFINE_OBJECT_WITH_SINGLE_INTERFACE(PortableDeviceManager, IPortableDeviceManager) +COM_DEFINE_QueryInterface(PortableDeviceManager, IPortableDeviceManager) +COM_DEFINE_AddRef(PortableDeviceManager, IPortableDeviceManager) +COM_DEFINE_Release(PortableDeviceManager, IPortableDeviceManager) + +static HRESULT WINAPI IPortableDeviceManager_impl_GetDevices(IPortableDeviceManager *iface, + LPWSTR *pPnPDeviceIDs, DWORD *pcPnPDeviceIDs) +{ + FIXME("(%p, %p): stub!\n", pPnPDeviceIDs, pcPnPDeviceIDs); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IPortableDeviceManager_impl_RefreshDeviceList(IPortableDeviceManager *iface) +{ + FIXME("(): stub!\n"); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IPortableDeviceManager_impl_GetDeviceFriendlyName(IPortableDeviceManager *iface, + LPCWSTR pszPnPDeviceID, WCHAR *pDeviceFriendlyName, DWORD* pcchDeviceFriendlyName) +{ + FIXME("(%s, %p): stub!\n", debugstr_w(pszPnPDeviceID), pDeviceFriendlyName); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IPortableDeviceManager_impl_GetDeviceDescription(IPortableDeviceManager *iface, + LPCWSTR pszPnPDeviceID, WCHAR *pDeviceDescription, DWORD *pcchDeviceDescription) +{ + FIXME("(%s, %p, %p): stub!\n", debugstr_w(pszPnPDeviceID), pDeviceDescription, pcchDeviceDescription); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IPortableDeviceManager_impl_GetDeviceManufacturer(IPortableDeviceManager *iface, + LPCWSTR pszPnPDeviceID, WCHAR *pDeviceManufacturer, DWORD *pcchDeviceManufacturer) +{ + FIXME("(%s, %p, %p): stub!\n", debugstr_w(pszPnPDeviceID), pDeviceManufacturer, pcchDeviceManufacturer); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IPortableDeviceManager_impl_GetDeviceProperty(IPortableDeviceManager *iface, + LPCWSTR pszPnPDeviceID, LPCWSTR pszDevicePropertyName, BYTE *pData, DWORD *pcbData, DWORD *pdwType) +{ + FIXME("(%s, %s, %p, %p, %p): stub!\n", + debugstr_w(pszPnPDeviceID), debugstr_w(pszDevicePropertyName), pData, pcbData, pdwType); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IPortableDeviceManager_impl_GetPrivateDevices(IPortableDeviceManager *iface, + LPWSTR *pPnPDeviceIDs, DWORD *pcPnPDeviceIDs) +{ + FIXME("(%p, %p): stub!\n", pPnPDeviceIDs, pcPnPDeviceIDs); + + return E_NOTIMPL; +} + +static const IPortableDeviceManagerVtbl IPortableDeviceManager_vtbl = +{ + IPortableDeviceManager_impl_QueryInterface, + IPortableDeviceManager_impl_AddRef, + IPortableDeviceManager_impl_Release, + IPortableDeviceManager_impl_GetDevices, + IPortableDeviceManager_impl_RefreshDeviceList, + IPortableDeviceManager_impl_GetDeviceFriendlyName, + IPortableDeviceManager_impl_GetDeviceDescription, + IPortableDeviceManager_impl_GetDeviceManufacturer, + IPortableDeviceManager_impl_GetDeviceProperty, + IPortableDeviceManager_impl_GetPrivateDevices +}; + +COM_DEFINE_CREATE_FOR_OBJECT_WITH_SINGLE_INTERFACE(PortableDeviceManager, IPortableDeviceManager, IPortableDeviceManager_vtbl) diff --git a/dlls/portabledeviceapi/portabledeviceapi.spec b/dlls/portabledeviceapi/portabledeviceapi.spec new file mode 100644 index 0000000000..b16365d0c9 --- /dev/null +++ b/dlls/portabledeviceapi/portabledeviceapi.spec @@ -0,0 +1,4 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() diff --git a/dlls/portabledeviceapi/portabledeviceapi_classes.idl b/dlls/portabledeviceapi/portabledeviceapi_classes.idl new file mode 100644 index 0000000000..0da8f228da --- /dev/null +++ b/dlls/portabledeviceapi/portabledeviceapi_classes.idl @@ -0,0 +1,21 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#pragma makedep register + +#include "portabledeviceapi.idl" diff --git a/dlls/portabledeviceapi/portabledeviceapi_private.h b/dlls/portabledeviceapi/portabledeviceapi_private.h new file mode 100644 index 0000000000..0624a814af --- /dev/null +++ b/dlls/portabledeviceapi/portabledeviceapi_private.h @@ -0,0 +1,26 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#ifndef __PORTABLEDEVICEAPI_PRIVATE_INCLUDED__ +#define __PORTABLEDEVICEAPI_PRIVATE_INCLUDED__ + +#include "unknwn.h" + +HRESULT PortableDeviceManager_create(IUnknown *unk_outer, void **obj) DECLSPEC_HIDDEN; + +#endif /* __PORTABLEDEVICEAPI_PRIVATE_INCLUDED__ */ diff --git a/include/Makefile.in b/include/Makefile.in index cc78b1c154..cf4fa317f0 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -511,6 +511,7 @@ SOURCES = \ physicalmonitorenumerationapi.h \ pktdef.h \ poppack.h \ + portabledeviceapi.idl \ powrprof.h \ prntvpt.h \ profinfo.h \ diff --git a/include/portabledeviceapi.idl b/include/portabledeviceapi.idl new file mode 100644 index 0000000000..523da782f0 --- /dev/null +++ b/include/portabledeviceapi.idl @@ -0,0 +1,71 @@ +/* + * Copyright 2018 Fabian Maurer + * + * 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 "unknwn.idl" + +[ + object, + uuid(a1567595-4c2f-4574-a6fa-ecef917b9a40), + local +] +interface IPortableDeviceManager : IUnknown +{ + HRESULT GetDevices( + [in, out] LPWSTR *pPnPDeviceIDs, + [in, out] DWORD *pcPnPDeviceIDs + ); + + HRESULT RefreshDeviceList(); + + HRESULT GetDeviceFriendlyName( + [in] LPCWSTR pszPnPDeviceID, + [in, out] WCHAR *pDeviceFriendlyName, + [in, out] DWORD *pcchDeviceFriendlyName + ); + + HRESULT GetDeviceDescription( + [in] LPCWSTR pszPnPDeviceID, + [in, out] WCHAR *pDeviceDescription, + [in, out] DWORD *pcchDeviceDescription + ); + + HRESULT GetDeviceManufacturer( + [in] LPCWSTR pszPnPDeviceID, + [in, out] WCHAR *pDeviceManufacturer, + [in, out] DWORD *pcchDeviceManufacturer + ); + + HRESULT GetDeviceProperty( + [in] LPCWSTR pszPnPDeviceID, + [in] LPCWSTR pszDevicePropertyName, + [in, out] BYTE *pData, + [in, out] DWORD *pcbData, + [in, out] DWORD *pdwType + ); + + HRESULT GetPrivateDevices( + [in, out] LPWSTR *pPnPDeviceIDs, + [in, out] DWORD *pcPnPDeviceIDs + ); +}; + +[ + threading(both), + uuid(0af10cec-2ecd-4b92-9581-34f6ae0637f3) +] +coclass PortableDeviceManager { interface IPortableDeviceManager; } diff --git a/include/wine/com.h b/include/wine/com.h new file mode 100644 index 0000000000..2f73bfcbd0 --- /dev/null +++ b/include/wine/com.h @@ -0,0 +1,256 @@ +/* + * COM helper functions + * + * Copyright 2018 Fabian Maurer + * + * 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 + */ + +#ifndef __WINE_WINE_COM_H +#define __WINE_WINE_COM_H + +#define COBJMACROS +#include "unknwn.h" + +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "rpcproxy.h" + +#include "wine/debug.h" + +#ifdef __WINE_WINE_TEST_H +#error This file should not be used in Wine tests +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +static HINSTANCE instance_dll; + +#ifdef WINE_COM_MAIN + +typedef struct +{ + const CLSID *clsid; + HRESULT (*pfnCreateInstance)(IUnknown *unk_outer, void **ppobj); +} com_object_creation_info; + +typedef struct +{ + IClassFactory IClassFactory_iface; + + LONG ref; + HRESULT (*pfnCreateInstance)(IUnknown *unk_outer, void **ppobj); +} IClassFactoryImpl; + +static inline BOOL WINAPI com_main(HINSTANCE instance, DWORD reason, LPVOID reserved) +{ + TRACE("(0x%p, %d, %p)\n", instance, reason, reserved); + + switch (reason) + { + case DLL_WINE_PREATTACH: + return FALSE; /* prefer native version */ + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(instance); + instance_dll = instance; + break; + } + + return TRUE; +} + +static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface) +{ + return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface); +} + +static HRESULT WINAPI classfactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppobj) +{ + IClassFactoryImpl *impl = impl_from_IClassFactory(iface); + + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_IClassFactory)) + { + IClassFactory_AddRef(iface); + *ppobj = &impl->IClassFactory_iface; + return S_OK; + } + + WARN("(%p)->(%s,%p),not found\n", impl, debugstr_guid(riid), ppobj); + return E_NOINTERFACE; +} + +static ULONG WINAPI classfactory_AddRef(IClassFactory *iface) +{ + IClassFactoryImpl *This = impl_from_IClassFactory(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI classfactory_Release(IClassFactory *iface) +{ + IClassFactoryImpl *impl = impl_from_IClassFactory(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + if (ref == 0) + HeapFree(GetProcessHeap(), 0, impl); + + return ref; +} + +static HRESULT WINAPI classfactory_CreateInstance(IClassFactory *iface, IUnknown *outer_unk, REFIID riid, void **ppobj) +{ + IClassFactoryImpl *impl = impl_from_IClassFactory(iface); + HRESULT hres; + IUnknown *unk; + + TRACE("(%p)->(%p,%s,%p)\n", impl, outer_unk, debugstr_guid(riid), ppobj); + + *ppobj = NULL; + hres = impl->pfnCreateInstance(outer_unk, (void **) &unk); + if (SUCCEEDED(hres)) + { + hres = IUnknown_QueryInterface(unk, riid, ppobj); + IUnknown_Release(unk); + } + return hres; +} + +static HRESULT WINAPI classfactory_LockServer(IClassFactory *iface, BOOL dolock) +{ + IClassFactoryImpl *impl = impl_from_IClassFactory(iface); + FIXME("(%p)->(%d), stub!\n", impl, dolock); + return S_OK; +} + +static const IClassFactoryVtbl classfactory_Vtbl = +{ + classfactory_QueryInterface, + classfactory_AddRef, + classfactory_Release, + classfactory_CreateInstance, + classfactory_LockServer +}; + +HRESULT WINAPI com_get_class_object(const com_object_creation_info *object_creation, int object_creation_length, + REFCLSID rclsid, REFIID riid, void **ppv) +{ + unsigned int i; + IClassFactoryImpl *factory; + + TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); + + if (!IsEqualGUID(&IID_IClassFactory, riid) + && !IsEqualGUID( &IID_IUnknown, riid)) + return E_NOINTERFACE; + + for (i = 0; i < object_creation_length; i++) + { + if (IsEqualGUID(object_creation[i].clsid, rclsid)) + break; + } + + if (i == object_creation_length) + { + FIXME("%s: no class found.\n", debugstr_guid(rclsid)); + return CLASS_E_CLASSNOTAVAILABLE; + } + + factory = HeapAlloc(GetProcessHeap(), 0, sizeof(*factory)); + if (factory == NULL) + return E_OUTOFMEMORY; + + factory->IClassFactory_iface.lpVtbl = &classfactory_Vtbl; + factory->ref = 1; + + factory->pfnCreateInstance = object_creation[i].pfnCreateInstance; + + *ppv = &factory->IClassFactory_iface; + return S_OK; +} + +#endif /* WINE_COM_MAIN */ + +#define COM_DEFINE_OBJECT_WITH_SINGLE_INTERFACE(CLASS, INTERFACE) \ + typedef struct { \ + INTERFACE INTERFACE##_iface; \ + LONG ref; \ + } CLASS##_impl; \ + \ + static inline CLASS##_impl *impl_from_##INTERFACE(INTERFACE *iface) \ + { \ + return CONTAINING_RECORD(iface, CLASS##_impl, INTERFACE##_iface); \ + } \ + +#define COM_DEFINE_QueryInterface(CLASS, INTERFACE) \ + static HRESULT WINAPI INTERFACE##_impl_QueryInterface(INTERFACE *iface, \ + REFIID riid, void **obj) \ + { \ + CLASS##_impl *impl = impl_from_##INTERFACE(iface); \ + TRACE("(%p/%p)->(%s,%p)\n", iface, impl, debugstr_guid(riid), obj); \ + if (IsEqualGUID(riid, &IID_IUnknown) \ + || IsEqualGUID(riid, &IID_##INTERFACE)) \ + { \ + IUnknown_AddRef(iface); \ + *obj = &impl->INTERFACE##_iface; \ + return S_OK; \ + } \ + ERR("(%p)->(%s,%p),not found\n", impl, debugstr_guid(riid), obj); \ + return E_NOINTERFACE; \ + } + +#define COM_DEFINE_AddRef(CLASS, INTERFACE) \ + static ULONG WINAPI INTERFACE##_impl_AddRef(INTERFACE *iface) \ + { \ + CLASS##_impl *impl = impl_from_##INTERFACE(iface); \ + ULONG ref = InterlockedIncrement(&impl->ref); \ + TRACE("(%p/%p)->(): new ref %d\n", iface, impl, ref); \ + return ref; \ + } \ + +#define COM_DEFINE_Release(CLASS, INTERFACE) \ + static ULONG WINAPI INTERFACE##_impl_Release(INTERFACE *iface) \ + { \ + CLASS##_impl *impl = impl_from_##INTERFACE(iface); \ + ULONG ref = InterlockedDecrement(&impl->ref); \ + TRACE("(%p/%p)->(): new ref %d\n", iface, impl, ref); \ + if (!ref) \ + HeapFree(GetProcessHeap(), 0, impl); \ + return ref; \ + } + +#define COM_DEFINE_CREATE_FOR_OBJECT_WITH_SINGLE_INTERFACE(CLASS, INTERFACE, TABLE) \ + HRESULT CLASS##_create(IUnknown *unk_outer, void **obj) \ + { \ + CLASS##_impl *impl; \ + TRACE("(%p,%p)\n", unk_outer, obj); \ + impl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLASS##_impl)); \ + if (!impl) \ + return E_OUTOFMEMORY; \ + impl->INTERFACE##_iface.lpVtbl = &TABLE; \ + impl->ref = 1; \ + *obj = &impl->INTERFACE##_iface; \ + return S_OK; \ + } + +#ifdef __cplusplus +} +#endif + +#endif /* __WINE_WINE_COM_H */
Hi Fabian,
On 09/11/2018 18:54, Fabian Maurer wrote:
This is a first try to generalize COM implementations. I find it useful to not repeat all that code for every new implementation.
Feedback appreciated, can we go that route?
Sure, some thoughts are bellow. Since you sent widl version later, let me concentrate on class factories, as it might be useful in widl conversation as well.
diff --git a/include/wine/com.h b/include/wine/com.h new file mode 100644 index 0000000000..2f73bfcbd0 --- /dev/null +++ b/include/wine/com.h
I'm skeptical about this being general COM helper. A header just for class factory could be better.
@@ -0,0 +1,256 @@ +/*
- COM helper functions
- Copyright 2018 Fabian Maurer
- 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
- */
+#ifndef __WINE_WINE_COM_H +#define __WINE_WINE_COM_H
+#define COBJMACROS +#include "unknwn.h"
+#include <stdarg.h> +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "rpcproxy.h"
+#include "wine/debug.h"
+#ifdef __WINE_WINE_TEST_H +#error This file should not be used in Wine tests +#endif
+#ifdef __cplusplus +extern "C" { +#endif
+static HINSTANCE instance_dll;
+#ifdef WINE_COM_MAIN
+typedef struct +{
- const CLSID *clsid;
- HRESULT (*pfnCreateInstance)(IUnknown *unk_outer, void **ppobj);
+} com_object_creation_info;
+typedef struct +{
- IClassFactory IClassFactory_iface;
- LONG ref;
Class factories don't really need to be heap-based. If they are static, they don't even need ref count.
- HRESULT (*pfnCreateInstance)(IUnknown *unk_outer, void **ppobj);
This could be expressed directly in IClassFactoryVtbl by having different vtbls for each implemented class factory.
+} IClassFactoryImpl;
With both above combined, you don't really need IClassFactoryImpl struct, which simplifies implementation a bit.
+static inline BOOL WINAPI com_main(HINSTANCE instance, DWORD reason, LPVOID reserved) +{
- TRACE("(0x%p, %d, %p)\n", instance, reason, reserved);
- switch (reason)
- {
case DLL_WINE_PREATTACH:
return FALSE; /* prefer native version */
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(instance);
instance_dll = instance;
break;
- }
- return TRUE;
+}
I'm not convinced we need that. Even if we do, it has nothing to do with COM. This could be done by exposing module instance from winecrt0.
+static HRESULT WINAPI classfactory_LockServer(IClassFactory *iface, BOOL dolock) +{
- IClassFactoryImpl *impl = impl_from_IClassFactory(iface);
- FIXME("(%p)->(%d), stub!\n", impl, dolock);
Having FIXME in generic implementation is not nice. Most Wine DLLs just returns S_FALSE from DllCanUnloadNow anyway and for them doing nothing in LockServer is perfectly fine.
- return S_OK;
+}
+static const IClassFactoryVtbl classfactory_Vtbl = +{
- classfactory_QueryInterface,
- classfactory_AddRef,
- classfactory_Release,
- classfactory_CreateInstance,
- classfactory_LockServer
+};
+HRESULT WINAPI com_get_class_object(const com_object_creation_info *object_creation, int object_creation_length,
REFCLSID rclsid, REFIID riid, void **ppv)
+{
- unsigned int i;
- IClassFactoryImpl *factory;
- TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
- if (!IsEqualGUID(&IID_IClassFactory, riid)
&& !IsEqualGUID( &IID_IUnknown, riid))
return E_NOINTERFACE;
- for (i = 0; i < object_creation_length; i++)
- {
if (IsEqualGUID(object_creation[i].clsid, rclsid))
break;
- }
- if (i == object_creation_length)
- {
FIXME("%s: no class found.\n", debugstr_guid(rclsid));
return CLASS_E_CLASSNOTAVAILABLE;
- }
- factory = HeapAlloc(GetProcessHeap(), 0, sizeof(*factory));
- if (factory == NULL)
return E_OUTOFMEMORY;
- factory->IClassFactory_iface.lpVtbl = &classfactory_Vtbl;
- factory->ref = 1;
- factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
- *ppv = &factory->IClassFactory_iface;
- return S_OK;
+}
This is much more complicated than it needs to be. Even after removing dynamic allocation, I'm not convinced that CLSID lookup is worth the complication.
Here is an idea about how implementing class factory could look like:
#include "wine/factory."
extern WINAPI object_constructor(IClassFactory*, REFIID, void**);
CLASS_FACTORY_IMPL(example_cf, object_constructor);
HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv) { If (IsEqualGUID(GUID_Example, clsid)) return IClassFactory_QueryInterface(&example_cf, riid, ppv); return CLASS_E_CLASSNOTAVAILABLE; }
The actual code would be static functions inside the header and CLASS_FACTORY_IMPL macro would just fill the vtbl using supplied constructor for CreateInstance. How does that look to you?
Thanks, Jacek