在MFC应用中实现IServiceProvider接口的技术咨询
Hey there! Let's walk through how to implement IServiceProvider for your MFC TTSApp to support IAccessibleApplication—without relying on ATL's CComObjectRootEx.
The Best Class to Inherit: Your CWinApp-Derived Class
First off, the application-level class (your CWinApp subclass) is the right place to implement these interfaces. Here's why:
IAccessibleApplicationis designed to expose metadata (app name, version) for the entire application, not just a single window/dialog.- Screen readers typically traverse the UI automation tree up to the application root object to request this service, which aligns perfectly with the lifecycle and scope of your
CWinAppinstance. - A
CDialogExsubclass would only provide this service when that specific dialog is in focus, which doesn't meet the requirement of exposing app-wide info.
Step-by-Step Implementation
Since you're avoiding ATL's COM root classes, we'll manually implement the COM interfaces while leveraging MFC's existing CCmdTarget base (which CWinApp already inherits from).
1. Update Your App Class Header
Modify your CTTSApp class to inherit from both IServiceProvider and IAccessibleApplication, and declare the required methods:
#include <oleacc.h> #include <objbase.h> class CTTSApp : public CWinApp, public IServiceProvider, public IAccessibleApplication { protected: LONG m_cRef; // COM reference count public: CTTSApp(); // MFC overrides virtual BOOL InitInstance(); virtual int ExitInstance(); // IUnknown methods STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject); // IServiceProvider method STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void** ppvObject); // IAccessibleApplication methods STDMETHOD(get_ApplicationName)(BSTR* pbstrName); STDMETHOD(get_ApplicationVersion)(BSTR* pbstrVersion); // MFC interface map (for optional COM integration) DECLARE_INTERFACE_MAP() };
2. Implement the Interface Methods in Your .cpp File
Start with initializing the reference count, then implement each COM method:
Reference Count & IUnknown Methods
CTTSApp::CTTSApp() : m_cRef(0) {} STDMETHODIMP_(ULONG) CTTSApp::AddRef() { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CTTSApp::Release() { // Note: MFC manages the CWinApp instance globally, so we don't delete it here return InterlockedDecrement(&m_cRef); } STDMETHODIMP CTTSApp::QueryInterface(REFIID riid, void** ppvObject) { if (!ppvObject) return E_POINTER; *ppvObject = nullptr; // Map requested IIDs to our interfaces if (riid == IID_IUnknown) *ppvObject = static_cast<IUnknown*>(this); else if (riid == IID_IServiceProvider) *ppvObject = static_cast<IServiceProvider*>(this); else if (riid == IID_IAccessibleApplication) *ppvObject = static_cast<IAccessibleApplication*>(this); else return E_NOINTERFACE; AddRef(); return S_OK; }
IServiceProvider's QueryService
This method is where we expose the IAccessibleApplication service to screen readers:
STDMETHODIMP CTTSApp::QueryService(REFGUID guidService, REFIID riid, void** ppvObject) { if (!ppvObject) return E_POINTER; *ppvObject = nullptr; // Check if the requested service is IAccessibleApplication if (guidService == __uuidof(IAccessibleApplication)) { return QueryInterface(riid, ppvObject); } return E_NOINTERFACE; }
IAccessibleApplication Methods
Return your app's name and version here:
STDMETHODIMP CTTSApp::get_ApplicationName(BSTR* pbstrName) { if (!pbstrName) return E_POINTER; *pbstrName = SysAllocString(L"TTSApp"); // Replace with your actual app name return *pbstrName ? S_OK : E_OUTOFMEMORY; } STDMETHODIMP CTTSApp::get_ApplicationVersion(BSTR* pbstrVersion) { if (!pbstrVersion) return E_POINTER; *pbstrVersion = SysAllocString(L"1.0.0"); // Replace with your actual app version return *pbstrVersion ? S_OK : E_OUTOFMEMORY; }
Optional: MFC Interface Map
If you want to integrate with MFC's existing COM support, add this to your .cpp file:
BEGIN_INTERFACE_MAP(CTTSApp, CWinApp) INTERFACE_PART(CTTSApp, IID_IServiceProvider, ServiceProvider) INTERFACE_PART(CTTSApp, IID_IAccessibleApplication, AccessibleApplication) END_INTERFACE_MAP() // Implement thunks for the interface map (if needed) // These are minimal wrappers to route calls to your methods ULONG FAR EXPORT CTTSApp::XServiceProvider::AddRef() { return ((CTTSApp*)this)->AddRef(); } ULONG FAR EXPORT CTTSApp::XServiceProvider::Release() { return ((CTTSApp*)this)->Release(); } HRESULT FAR EXPORT CTTSApp::XServiceProvider::QueryInterface(REFIID riid, void** ppvObject) { return ((CTTSApp*)this)->QueryInterface(riid, ppvObject); } HRESULT FAR EXPORT CTTSApp::XServiceProvider::QueryService(REFGUID guidService, REFIID riid, void** ppvObject) { return ((CTTSApp*)this)->QueryService(guidService, riid, ppvObject); } // Repeat similar thunks for XAccessibleApplication if needed
Key Notes
- No ATL Dependency: All code uses native MFC and Win32 COM APIs, so you don't need to link against ATL libraries.
- Reference Counting: Since
CWinAppis a global instance managed by MFC, we skip deleting the object in theReleasemethod. - Header Includes: Make sure to include
<oleacc.h>(forIAccessibleApplication) and<objbase.h>(for COM helper functions likeSysAllocString).
内容的提问来源于stack exchange,提问作者Benilda Key

