Description
As far as I can tell, these functions are hooks: third-party code is meant to replace them.
Their implementation in ctypes (i.e. their default behaviour) is to import and call the same-named functions from a third-party library, comtypes.server.inprocserver. This is not good. comtypes should instead register their hook on import.
Here's a possible plan to make the API boundary better without breaking users.
DllCanUnloadNow
While the Python interpreter is running, it is not safe to unload the shared library that contains _ctypes. Therefore:
- The C function
DllCanUnloadNowexported from _ctypes should be changed to always returnS_FALSE. We should change that now, without a deprecation period. (Note that thecomtypeshook already does this.) - We should stop importing and calling
comtypes.server.inprocserver. I'm not sure about the necessary deprecation period, but I think that it should be a non-breaking change and can also be done immediately. Or is someone relying on it for side effects? O_o - Setting and getting the hook should be deprecated. In about Python 3.18 we should stop calling it, and remove it.
DllGetClassObject
This one, on the other hand, sounds like a useful hook. It also looks like an inprocess COM server need a special build so it's not useful to allow multiple hooks -- replacing a global one is enough. Is that so?
If yes:
ctypes.DllGetClassObject(the default implementation) should raise aDeprecationWarning. In about Python 3.18, it should be changed to do nothing, just, returnCLASS_E_CLASSNOTAVAILABLE.comtypesshould be changed: on import, it should replacectypes.DllGetClassObjectwith its own hook.
This should ensure that old versions of comtypes still work as before (until after the deprecation period).
Does that sound reasonable?
cc @junkmd
Activity
zooba commentedon Nov 28, 2024
I suspect these may be here because
comtypescalls back through ctypes, and so to COM it looks like all the calls are coming from_ctypes.pyd? I've never noticed them in our code before (all my COM work with Python has been in my own extension modules).No doubt it's a useful and important hack at some point, but without a more concrete use case they're probably not so necessary.
Changing default behaviour to not import
comtypesis a good move, but it may not be possible forcomtypesto hook them itself if it doesn't get imported before these are called.DllGetClassObjectin particular is meant as an entry point, but I'm not sure how much gets executed before it may be called. It probably depends on how Python is registered as a local server, which is something that's definitely outside of our upstream support, and so I think it's reasonable forcomtypes(or whoever) to figure out a preferred way to set that up.If
comtypesdoesn't need these, then I'd prefer to deprecate and remove. I guess that's what @junkmd can tell us.junkmd commentedon Nov 30, 2024
For those interested in this discussion, I would like to introduce some technical references related to
DllCanUnloadNowandDllGetClassObject.A notable reference book on OLE and related COM topics is "Inside OLE, 2nd Edition, Kraig Brockschmidt, Microsoft Press, 1995, ISBN: 1-55615-843-2".
The author, kraigb, has made the book publicly available under the MIT License.
Regarding
DllGetClassObject, page 183 of the PDF states the following:Regarding
DllCanUnloadNow, page 186 of the PDF states the following:junkmd commentedon Nov 30, 2024
Below is documentation for using
comtypes(as of version 0.5) ¹⁾ as a COM server:https://pythonhosted.org/comtypes/server.html
¹⁾ The
comtypesdocumentation has not been updated for many years. This is due to a combination of factors: pythonhosted can no longer be updated; I am not yet well-versed in Sphinx; I am unfamiliar with how to migrate documentation to hosting platforms like ReadTheDocs; and I lack case studies on how external collaborators without ownership, like myself, can work with other maintainers to maintain documentation. One reason I started contributing to thecpythondocumentation was to familiarize myself with modern Sphinx practices.junkmd commentedon Nov 30, 2024
junkmd commentedon Nov 30, 2024
Here are my current thoughts on this:
ctypesAPIs so they no longer depend oncomtypes.ctypeseasier for third-party libraries and applications to use.ctypesto verify its behavior during the process.I am not against changing
ctypesto remove its dependency oncomtypes. Considering the proper dependency relationship, the current implementation is suboptimal.However, just as COM interfaces can be implemented by projects other than
comtypes, changes toctypesshould leave room for implementing COM servers without relying oncomtypes.I think the hooks defined in
ctypesand the callbacks defined in_ctypes(CanUnloadNowandDllGetClassObject) should remain.I suspect these are essential when implementing a COM server using
ctypes.Regarding hook handling, I propose creating a relationship similar to
sys.excepthookandsys.__excepthook__.An implementation like below would allow application developers to specify the behavior invoked by hooks easily when combining
ctypesand COM packages such ascomtypes.It would also enable calling the pre-overloaded function.
The main challenge is that we have yet to find modern use cases for implementing COM servers using
ctypesorcomtypes.To make production changes, I believe comprehensive testing that covers actual use cases is essential.
The
comtypescommunity likely includes developers who have implemented COM servers usingcomtypesor even developers familiar with implementing COM servers in languages other than Python.If agreeable, I plan to reach out to them and invite them to participate in this discussion to provide their insights and cooperation.
I am also reaching out to developers interested in this topic in enthought/comtypes#671, but directly contacting developers who could be stakeholders in this change would be more effective.
cc: @encukou, @zooba
zooba commentedon Dec 2, 2024
Agreed. There are no shortage of use cases for using COM servers, but very few that require implementing them (especially when they're then going to be activated through COM's global interfaces, as opposed to being directly passed in).
I assume this only applies to in-process activation as well? If you are registering a COM server for out-of-proc activation then I'm pretty sure
ctypesisn't going to work anyway. So it's really only able to handle in-process activation, which is also not widely used.If anyone has any such cases for implementing/registering a COM server in Python, modern or legacy, it would be helpful to list them here.
If we can't find sufficient modern cases to motivate this (and I emphasise modern here because we need to justify users who can update their CPython to a later version but somehow can't modify their own code or update other parts of their system - legacy cases where nobody has touched it in 20 years don't count, because you couldn't update CPython in that case), then I think deprecation and complete removal is on the table.
encukou commentedon Dec 2, 2024
But, it's not safe to unload
_ctypes.pyd(or any other extension's DLL) while a Python interpreter is running. So, we should make theDllCanUnloadNowhook a no-op.IMO, that would make sense if
__dll_get_class_object__was complicated. But here the base implementation would be two lines:(There's a magic number because
ctypesdoesn't have a good way to work withHRESULTerror constant. But a library dealing with COM should make them easy to work with.)It could even be no lines -- the C function can do this in case the Python hook does not exist at all.
If we need something more complex than a replaceable hook, i was thinking about a list, named for example
ctypes.DllGetClassObject_hooks, to which users could add their hooks. The C functionDllGetClassObjectwould call Python functions in this list in order, until one returns something else thanCLASS_E_CLASSNOTAVAILABLE. It would return the result of the last call.(We could instead make the list internal, and add a “register” function, but then we'd also need “unregister” and “clear” and so on -- a list sounds more convenient.)
Unlike with
sys.excepthook, it makes sense to me that there could be multiple extensions that want to export COM objects. But, I have no idea if it's even something to keep in mind as a future possibility.bennyrowland commentedon Dec 2, 2024
I am one of the developers using comtypes to make servers rather than clients that @junkmd asked to weigh in on this discussion. I was going to say that I know absolutely nothing about this subject because I have only used local servers rather than inproc ones, but that felt a bit underwhelming, so I have done a bit of extra digging to try and understand at least a little bit that I might be able to contribute, apologies if none of this is new or interesting.
Local servers made intuitive sense to me because registering them just sets the Python executable and a script to run when the server is created, but it didn't make sense to me how an inproc server could work - how would the interpreter get set up, etc? So I looked at the comtypes registration code for inproc servers here which requires a
frozendllhandleattribute to exist to register as inproc.From somewhere in my distant memories that reminded me that comtypes had been associated with py2exe, so I checked that out and sure enough turned up this page on how to generate a COM DLL server. Some further digging through the code turned up the file
source/run_ctypes_dll.cand particularly these lines. Basically what Py2exe is doing is compiling this C file into a DLL and embedding a Python interpreter into it to run the comtypes server code. As the DLL being used to provide the inproc server it has to implementDllGetClassObject()andDllCanUnloadNow, but it does so by loading the implementations out ofctypes(which of course then goes on to get them fromcomtypes).I know that @theller was heavily involved in the early days of
ctypes,comtypes, andpy2exeand could probably add more insight into this situation, but I assume that the issue is that becausecomtypesis a pure Python package it doesn't have a DLL which could be imported byrun_ctypes_dll.c, so it was convenient to make use of the_ctypes.dllto provide exported C functions which call the Python functions in turn.From my perspective, there doesn't seem to be any reason to keep these functions in
ctypesat all. Although they are nominally standard COM functions as explained above by @junkmd, there is no process to invoke them except via Py2exe andrun_ctypes_dll.c. It would therefore seem much more logical to move the logic contained in the callbacks directly to Py2exe, but rather than invokingctypes.DllGetClassObjectit could directly invokecomtypes.DllGetClassObject(comtypes is guaranteed to be installed via Py2exe's freezing process anyway). Arguably, theinprocserver code incomtypesshould even be moved into Py2exe anyway given that it can only be used via Py2exe in the first place.I will confess that I personally have little appetite for taking on the responsibility of updating Py2exe to no longer depend on these functions from
types, I have never used Py2exe and it is sufficiently complex (and insufficiently documented) that I don't fancy setting up the necessary development environment to get it building. But it is actively being developed and I think that the actual fix is probably fairly simple for a maintainer to implement (mainly just copying code fromctypes/callbacks.c). I think that with a fairly long deprecation cycle before removing them fromctypes, combined with a bug report to Py2exe, anybody that is interested in using inproc servers in this way will have plenty of opportunity to implement the fix.encukou commentedon Dec 3, 2024
It seems that py2exe has special support for
comtypes.server.inprocserverthat it runs in a specialctypes_comdlltarget. It's not clear to me what an “inprocess COM server” is; is this it?If it is, looks like it will call
comtypes.server.register.registerbefore Windows will make any calls toDllGetClassObject, so that's a function wherecomtypescan ensure that its hook is installed.bennyrowland commentedon Dec 3, 2024
@encukou an inproc COM server is one which is contained in a DLL and so can be instantiated in the calling process, and COM objects so produced can be used directly via function pointers. In the more common case (at least in my experience) the COM server is a separate process and methods are called via RPC with Windows messages.
There are different hooks for different purposes in the DLL.
DllRegisterServer()andDllUnregisterServer()are called byregsvr32in order to create the relevant registry entries to allow the server to then be discoverable by potential clients (or to remove the entries, respectively). These functions would not normally be called when a registered server is being instantiated - that is whenDllGetClassObject()is called.I think the most important point to emphasise is that the
DllGetClassObjecthook which Windows calls is in the DLL which is registered for the inproc server. This is not the_ctypes.pydbut rather the custom DLL created by Py2exe. TheDllGetClassObjectinctypesis therefore not actually a hook. AFAICT there is no reason for any of this code to be inctypesat all - all of these functions inctypeswill only ever be called from Py2exe DLLs, there is no other mechanism to use them, so they really belong in Py2exe.You can see here that the Py2exe DLL implementations of
DllRegisterServer()andDllUnregisterServer()call into the Python interpreter that is provisioned inDllMain(), which has already importedboot_ctypes_com_server.pyand so can directly call the Python versions of the functions here. For reasons that are not clear to me, the Py2exe DLL does not do the same thing forDllGetClassObject(), but instead imports the C functions from_ctypes.pydthat then do use the same paradigm of calling in to the Python code in ctypes. I think it should be very easy to move the PythonDllGetClassObject()andDllCanUnloadNow()code fromctypesintoboot_ctypes_com_server.pyand then just change the way that the Py2exe DLL forwards the calls to matchDll(Un)RegisterServer()In fact, I think there is definitely a case to be made for moving all of
comtypes.server.inprocserverinto Py2exe. The implementation incomtypesis custom designed for Py2exe to use (and cannot be used in any other way), and there is plenty of messy stuff like this where Py2exe is overwriting a privatecomtypesmember because really all that logic belongs in Py2exe.junkmd commentedon Dec 4, 2024
Based on previous discussions, I think the ideal approach is to implement the hooks directly in freezing tools like
py2exe, withctypesandcomtypesproviding supporting utility functions as needed.Considering cases where implementing COM servers with
win32cominpywin32, it is neither reasonable nor feasible forctypesorcomtypesto take responsibility for properly handling inproc COM server hooks for every possible COM implementation.Moreover, as seen in py2exe/py2exe#24 (and mhammond/pywin32#868), it seems that registering COM servers with
py2exedoes not work well for eitherwin32comorcomtypes.It might also be worthwhile to invite the maintainers of
py2exeto participate in this discussion to help investigate the technical challenges and determine the required deprecation timeline (i.e., the duration of the deprecation period to be set).encukou commentedon Dec 4, 2024
I opened py2exe/py2exe#217.
4 remaining items
junkmd commentedon Dec 11, 2024
@bennyrowland
Thank you for your investigation.
Don't worry.
We are discussing features that are among the most mature functionalities in Windows, some of the oldest APIs in
ctypes, long-untested features incomtypes, and ones that we are no longer even sure work withpy2exe.Given this state, it's understandable that anyone could become confused or misinterpret things.
I appreciate your participation in this discussion.
As you mentioned, including a DLL in the build of
comtypeswould be a significant change.One of the key features and differentiators of
comtypesis that it is Pure Python, so I would like to maintain that concept.As an alternative, based on my earlier suggestion and encukou's advice, I currently think it would be better to implement a system where each project can optionally set hooks.
For example, a package like
comtypeswould register its hooks in the following manner.Of course, this implementation is merely an example, and I have no objections if a better implementation or operation is found that removes unnecessary dependencies from the standard library and preserving the concepts of third-party projects.
Also, I do NOT strongly desire to change the implementation of
ctypesas described above right away.I would like to wait for developers actively using this feature to notice the
DeprecationWarning(IsFutureWarningmore appropriate?), find this issue, and share their actual use cases by participating in this discussion.I appreciate everyone participating in this discussion.
encukou commentedon Dec 11, 2024
I'd be fine with
ctypesproviding a “trampoline” C functions that call user-defined callbacks, so that provided that:ctypesBut what's not clear to me is if, with current
ctypes,comtypes(or any other library) needs to build its own DLL. AreDllRegisterServerandDllUnregisterServeralso needed (as in AvmcIfc.cpp), or are they optional?If building a DLL is necessary, I think it's better to remove this functionality from
ctypes.I'd be fine with delaying the deprecation, though.
If building a DLL is not necessary for the third-party library to provide a useful COM server, let's
DllRegisterServertoctypesas well, if there's a small well-defined set of them. (Are there others? Is this it?)junkmd commentedon Dec 11, 2024
If APIs are needed to implement, the COM specification has remained unchanged for decades since its inception, so once the
*Dll*APIs are defined, there should be no additions beyond that.I plan to spend my free time this weekend writing tests to implement, register, and unregister a COM server using
ctypes.bennyrowland commentedon Dec 11, 2024
@encukou
DllRegisterServerandDllUnregisterServerare standard Windows entry points when using theregsvr32.exeapplication to register a DLL in the registry, they are not used during the creation and use of the COM objects.comtypesprovides its own mechanism for doing registrations for Python COM servers which I think is much more practical and convenient than passing_ctypes.pydtoregsvr32.exe, and it would also be impossible in that case to specify which server(s) you wished to register, there would have to be some kind of blanket registration of all discoverable servers, which would be complicated to define and set-up.The code in
comtypes/source(Avmclfc.cppetc.) is a C++ COM server that is (or was) being used as a test object forcomtypesCOM client code to be tested against. I believe thatDllGetClassObjectandDllCanUnloadNoware the only two functions that are required for a COM server to be created and used (I think they are the only ones implemented in current_ctypes.pydand Py2Exe's DLLs, and things do work like that).@junkmd the current
comtypestests intest_comserver.pyare exactly already registering, using, and unregistering the COM server implemented inTestComServer.py. They test both InprocServer and LocalServer options, and your tests posted above show that they work just fine with currentctypes.I do not see how the hook mechanism can work though, because the current mechanism for creating an InprocServer looks like:
ole32.CoCreateInstance()or similar to invoke the COM machineryclsidin the registry and finds out that it is an InprocServer and gets the path to the DLL (which in this case is_ctypes.pyd)DllGetClassObject()_ctypes.pydcallsPy_Initialize()_ctypes.pydimports thectypesPython version of the function (func = _PyImport_GetModuleAttrString("ctypes", "DllGetClassObject");) and calls it.ctypesDllGetClassObject()imports thecomtypesversion and runs that.So there is no point at which
comtypesor any other module gets imported where it can insert itself into that train of events. It only works because importingcomtypesis hard-coded intoctypes. I suppose that we could do something with entry points, but that would involve a lot of design work to create something that would almost certainly never be used except bycomtypes(it is very hard to see anyone creating a competing library, given the enormous complexity of working with COM, and how well, by and large,comtypesalready works).@junkmd if you want to keep
comtypesas a pure Python package (which I do understand) then perhaps a solution would be to create a separate package (e.g.comtypes-inproc-supportwhich would just contain the DLL (and install it somewhere in thecomtypesnamespace insite-packages) and which could be listed as an "extra" dependency ofcomtypesso people could dopip install comtypes[inproc](or similar) to get the extra DLL. Then when registering an InprocServercomtypescould check for the availability of the DLL and if it is not available then give an error message explaining the extra package is needed to register InprocServers. This package could surely use the limited API and wouldn't have any Python code of its own so should require very little maintenance. I would be happy to help out with creating such a package.junkmd commentedon Dec 15, 2024
As I mentioned earlier, I attempted to write tests this weekend to implement, register, and unregister an in-process COM server using only
ctypesduring my free time.First, I investigated the
winregfunction calls used bycomtypesto register an in-process server. For details about what gets registered in the registry, please refer to the test I added tocomtypes.In my PoC repository, I defined a simple
TestCtypesComServer.idlfile containing only a single custom interface. I then built it into a.tlbfile using themidlcommand and wrote a test to register and instantiate it.results of executing the unit tests
When
comtypesis not installed,DllGetClassObjectalways returnsCLASS_E_CLASSNOTAVAILABLE(-2147221231,0x80040111), causing anOSErrorwith the same error code to be raised (test_1).Here, if I patch
DllGetClassObjectto always returnS_OK, even whenrclsidmatchesCLSID_TestCtypesComServer,E_UNEXPECTED(-2147418113,0x8000FFFF) is raised. At this point, theriidthat is passed in matchesIID_IClassFactory(test_2).I believe it is necessary to perform actions similar to what
comtypesdoes, such as obtaining theVtblstructure mapped to theiidfor aCOMObjectand then callingIUnknown_QueryInterfaceas shown here.If you have any ideas for an easier way to implement an in-process COM server, please feel free to let me know.
I will continue investigating the minimal requirements for registering
_ctypes.__file__as a DLL, but please give me some more time.junkmd commentedon Dec 15, 2024
@bennyrowland
Thank you for your detailed explanation.
I understand the current mechanism and how hard it is to introduce any hook system cleanly.
I actually tried writing a minimal test and realized how challenging it is.
However, I want the functionality in
comtypesthat currently relies onctypesto continue working in the future.While it is true that the proposed hook mechanism might primarily be used by
comtypesfor now, I believe it is valuable to keep the door open for other packages to leverage it in the future.There are already multiple COM client packages out there, and increasing the flexibility could encourage more entrants or use cases.
That said, I also understand the challenges of continuing to maintain this functionality.
Therefore, I think it’s worthwhile to simultaneously consider launching a project to provide such DLLs or helpers to implement them, as well as modifying
comtypesso thatserver.register.RegistryEntriescan optionally specify a custom DLL.encukou commentedon Dec 16, 2024
As far as I can see, one way for CPython to avoid hardcoding
comtypeswould be to look in the registry for the requestedrclsid, and requirecomtypes(and others) to register Python classes in two places -- Windows itself (for_ctypes.pyd) and a Python-specific location (for the Python module).However, that's a lot of new functionality. If we put that functionality in CPython, it would be hard to iterate on it -- the release schedule is slow, and if we find an issue, users wouldn't be able to easily update only the COM support part.
@bennyrowland's idea of a helper binary extension sounds much better.
junkmd commentedon Dec 16, 2024
Breaking the dependency on
comtypesand providing a way forctypesto implement an inproc-server in pure Python certainly requires a significant and complex effort.I am inclined toward @bennyrowland's idea of a helper binary extension.
However, I believe that deprecating an API without first providing a recommended alternative would confuse users.
I have no objections to launching the
comtypes-outproc-server-supportproject, which will provide the necessary DLL for implementing inproc servers, with the help of @bennyrowland, @encukou, and others participating in this issue.I am also willing to allocate resources to modify
comtypesto make it easier to use this DLL.Once
comtypes-outproc-server-supporthas been tested and confirmed to work in conjunction withcomtypes, I will not oppose deprecating the hooks currently present inctypes.By ensuring that users encountering the
DeprecationWarningreceive clear messaging on how to migrate, we can avoid confusion.It is worth noting that even if these hooks are removed from
ctypes, there remains the option to implement a local server in pure Python, as demonstrated by @forderud.I also think that if users have the permissions to install Python,
comtypes, and register_ctypes.pydin the registry to implement and register an inproc COM server, replacing_ctypes.pydwith a third-party DLL might not pose any issues.junkmd commentedon Dec 22, 2024
Progress Update:
Regardless of the direction we choose to take, understanding what exactly is happening in
comtypes'DllGetClassObjectis essential.To achieve this, I have been refactoring to improve readability and adding unit tests to better understand its behavior and prevent regressions.
The test for
COMObject.IUnknown_QueryInterface, which is called internally byinprocserver.DllGetClassObject, is particularly noteworthy.When passing an instance created by the high-level
comtypes.client.CreateObjectinstead of an instance returned by the low-levelole32.CoCreateInstancefunction—without usingbyref—the test passes despite access violations during release hooked by the metaclass.In
comtypes, the reason why the dst argument ofCopyComPointeris specified asc_void_pin this particular case, despite being almost always a subclass ofPOINTER(IUnknown), is believed to be due to this behavior.Please allow me more time to investigate what is minimally required to implement a COM server.
junkmd commentedon Jan 19, 2025
Progress Update:
I have been analyzing the registry entries required for
comtypesto register non-frozen inproc servers.Up to now, the creation of these entries has been handled through the same method for both non-frozen and frozen servers, with complex internal conditional branches determining their content.
I have separated the methods for generating entries into distinct ones for non-frozen and frozen servers, clearly defining the necessary and sufficient entries for each.
Additionally, I made it possible to test third-party packages by rewriting
_ctypes.__file__to point to any inproc server DLL file path (this line).I also discovered extremely simple COM server examples in the
comtypesdocumentation, which should allow for minimal PoC based on these examples.Please allow me more time.
junkmd commentedon Feb 18, 2025
Progress Update:
I released
comtypes==1.4.10, which includes the refactoring of the previously mentioned registry entries.Since no bug reports have been filed after about a week, projects and ecosystems that depend on it are likely functioning without problems.
COMObjectis used when implementing a COM library or COM server withcomtypes.I hypothesize that understanding its behavior will help unravel the processing flow of a COM server.
Aside from defining the behavior of COM objects,
COMObjectis also used when defining event sinks. By investigating how methods defined in an event sink are triggered as callbacks, we might gain insight into how COM objects are defined in Python and how a server's behavior is implemented.Thanks to @moi15moi’s contribution (enthought/comtypes#769), I noticed that functions for registering, retrieving, and revoking active objects using
COMObjectinstances are implemented incomtypes/server/....This implementation includes a process where a
Vtblpointer is copied to a COM interface pointer.This might serve as another clue to understand the underlying mechanism.
Please give me more time.
Update README.md
PCigales commentedon Aug 9, 2025
If it can be useful to you, I have eventually made a DLL that can be used as a COM server (through the InProcServer32 subkey of the CLSID entry in the registry) and act as a bridge to a python module, calling its implementations of DllGetClassObject and DllCanUnloadNow. It currently loads a hard coded module (WICPy), which must be located in the same folder, but it would be possible to get the name/path of the module from a .cfg file or from a dedicated value in the InProcServer32 key. I have tested it with the IFileSystemBindData interface and its proxy/stub class factories (included in WICPy), in Python and PowerShell, and everything seems to work.
It can be found at https://github.com/PCigales/WICPy/blob/main/comserver.c and https://github.com/PCigales/WICPy/blob/main/comserver.dll. The source code has been compiled with 'gcc -shared comserver.c -o comserver.dll -I "C:\Program Files\Python\include" -L "C:\Program Files\Python\libs" -l python313'.
junkmd commentedon Aug 24, 2025
@PCigales
Thanks for your insight.
Knowing the GitHub Actions workflow for compiling and building the code you provided might offer some valuable hints.
junkmd commentedon Aug 24, 2025
My situation has changed, and I'm finding it difficult to allocate resources to this issue.
I think we might not delve into more detail than our current hypothesis-based discussion until we find a user who is actually using this feature.