-
-
Notifications
You must be signed in to change notification settings - Fork 826
Ensure IAccessible2 remains available in Firefox after restarting NVDA on Windows 10 Creaters update, register ISimpleDOM proxy to allow math in Google Chrome without Firefox installed, and Allow the user to get back a virtualBuffer when restarting NVDA with a Firefox document in focus #7535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7c0e258
70a88f5
d6ee83a
730fd5f
fe71f7c
2aed84c
2e6ad21
b8a9177
dbb9ab2
b42ffa6
952dfe3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
| <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||
| <assemblyIdentity type="win32" name="%proxyName%" version="1.0.0.0" /> | ||
| <file name="%proxyName%.dll"> | ||
| <comInterfaceProxyStub name="%proxyName%" proxyStubClsid32="%proxyClsid%" iid="%proxyClsid%" /> | ||
| </file> | ||
| </assembly> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| /* | ||
| This file is a part of the NVDA project. | ||
| URL: http://www.nvda-project.org/ | ||
| Copyright (C) 2017 NV Access Limited. | ||
| This program is free software: you can redistribute it and/or modify | ||
| it under the terms of the GNU General Public License version 2.0, as published by | ||
| the Free Software Foundation. | ||
| This program 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. | ||
| This license can be found at: | ||
| http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
| */ | ||
|
|
||
| #include <cstdio> | ||
| #include <cwchar> | ||
| #include <string> | ||
| #include <locale> | ||
| #include <codecvt> | ||
| #include <vector> | ||
| #define WIN32_LEAN_AND_MEAN | ||
| #define CINTERFACE | ||
| #include <windows.h> | ||
| #include <objbase.h> | ||
| #include <rpcproxy.h> | ||
| #include <common/log.h> | ||
| #include "dllmain.h" | ||
| #include "COMProxyRegistration.h" | ||
|
|
||
| using namespace std; | ||
|
|
||
| typedef void(RPC_ENTRY *LPFNGETPROXYDLLINFO)(ProxyFileInfo***, CLSID**); | ||
|
|
||
| // The CLSID representing the Windows COM standard marshaller | ||
| // Many built-in COM interfaces in Windows point to this class object to handle marshalling, however there does not seem to be a constant for it in the windows SDK. | ||
| // Some non-Microsoft sources: | ||
| // http://www.mazecomputer.com/sxs/help/proxy.htm | ||
| // http://thrysoee.dk/InsideCOM+/ch12d.htm | ||
| const wchar_t* StringCLSID_StandardMarshaler=L"{00020424-0000-0000-C000-000000000046}"; | ||
|
|
||
| COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { | ||
| LOG_DEBUG(L"Registering proxy "<<dllPath); | ||
| int res; | ||
| // Fetch the CLSID for the standard marshaler which will be used to unregister PS CLSIDs later | ||
| CLSID clsid_standardMarshaler; | ||
| res=IIDFromString(StringCLSID_StandardMarshaler,&clsid_standardMarshaler); | ||
| if(res!=S_OK) { | ||
| LOG_ERROR(L"Could not get clsid for standard marshaler"); | ||
| return nullptr; | ||
| } | ||
| // Generate a new unique CLSID to use for class object registration | ||
| CLSID regClsid={0}; | ||
| res=CoCreateGuid(®Clsid); | ||
| if(res!=S_OK) { | ||
| LOG_ERROR(L"Unable to generate registration CLSID"); | ||
| return nullptr; | ||
| } | ||
| // load the proxy dll | ||
| wchar_t absDllPath[MAX_PATH]={0}; | ||
| wsprintf(absDllPath,L"%s\\%s",dllDirectory,dllPath); | ||
| HMODULE dllHandle=LoadLibrary(absDllPath); | ||
| if(dllHandle==NULL) { | ||
| LOG_ERROR(L"LoadLibrary failed for "<<dllPath); | ||
| return nullptr; | ||
| } | ||
| // look up the GetProxyDllInfo function on the proxy dll | ||
| LPFNGETPROXYDLLINFO Dll_GetProxyDllInfo=(LPFNGETPROXYDLLINFO)GetProcAddress(dllHandle,"GetProxyDllInfo"); | ||
| if(Dll_GetProxyDllInfo==NULL) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nullptr |
||
| LOG_ERROR(L"GetProxyDllInfo function not found in "<<dllPath); | ||
| FreeLibrary(dllHandle); | ||
| return nullptr; | ||
| } | ||
| // Fetch the proxy information from the dll (interface IIDs and the proxy stub CLSID) | ||
| CLSID* pProxyClsid=NULL; | ||
| ProxyFileInfo** pProxyInfo=NULL; | ||
| Dll_GetProxyDllInfo(&pProxyInfo,&pProxyClsid); | ||
| if(!pProxyClsid||!pProxyInfo) { | ||
| LOG_ERROR(L"Could not fetch proxy information from "<<dllPath); | ||
| FreeLibrary(dllHandle); | ||
| return nullptr; | ||
| } | ||
| // Create and activate an activation context using the manifest in the proxy dll | ||
| // to temporarily register the proxy dll's class object | ||
| ACTCTX actCtx={0}; | ||
| actCtx.cbSize=sizeof(actCtx); | ||
| actCtx.dwFlags=ACTCTX_FLAG_HMODULE_VALID|ACTCTX_FLAG_RESOURCE_NAME_VALID; | ||
| // The resource ID for a dll must be 2. | ||
| // See the linker's /manifest argument stating where the manifest is placed in a dll: https://docs.microsoft.com/en-gb/cpp/build/reference/manifest-create-side-by-side-assembly-manifest | ||
| actCtx.lpResourceName=MAKEINTRESOURCE(2); | ||
| actCtx.hModule=dllHandle; | ||
| HANDLE hActCtx=CreateActCtx(&actCtx); | ||
| if(hActCtx==NULL) { | ||
| LOG_ERROR(L"Could not create activation context for "<<dllPath); | ||
| FreeLibrary(dllHandle); | ||
| return nullptr; | ||
| } | ||
| ULONG_PTR actCtxCookie; | ||
| if(!ActivateActCtx(hActCtx,&actCtxCookie)) { | ||
| LOG_ERROR(L"Error activating activation context for "<<dllPath); | ||
| ReleaseActCtx(hActCtx); | ||
| FreeLibrary(dllHandle); | ||
| return nullptr; | ||
| } | ||
| // Fetch the class object (which will come from the proxy dll) | ||
| IUnknown* ClassObjPunk=NULL; | ||
| res=CoGetClassObject(*pProxyClsid,CLSCTX_INPROC_SERVER,nullptr,IID_IUnknown,(void**)&ClassObjPunk); | ||
| // From here we no longer need the activation context | ||
| DeactivateActCtx(0,actCtxCookie); | ||
| ReleaseActCtx(hActCtx); | ||
| if(res!=S_OK) { | ||
| LOG_ERROR(L"Error fetching class object for "<<dllPath<<L", code "<<res); | ||
| FreeLibrary(dllHandle); | ||
| return nullptr; | ||
| } | ||
| // Re-register the class object with COM now that the activation context is gone. | ||
| // Keeping the class object available to COM, with COM also handling the life time of the proxy dll now | ||
| DWORD dwCookie; | ||
| res=CoRegisterClassObject(regClsid,ClassObjPunk,CLSCTX_INPROC_SERVER,REGCLS_MULTIPLEUSE,&dwCookie); | ||
| ClassObjPunk->lpVtbl->Release(ClassObjPunk); | ||
| if(res!=S_OK) { | ||
| LOG_ERROR(L"Error registering class object for "<<dllPath<<L", code "<<res); | ||
| FreeLibrary(dllHandle); | ||
| return nullptr; | ||
| } | ||
| COMProxyRegistration_t* reg= new COMProxyRegistration_t(); | ||
| reg->dllPath=dllPath; | ||
| reg->classObjectRegistrationCookie=dwCookie; | ||
| // For all interfaces the proxy dll supports, register its CLSID as their proxy stub CLSID | ||
| // pProxyInfo is a pointer to a list of ProxyFileInfo pointers. The last of them being NULL to denote the end of the list. | ||
| // There is no official documentation on this, but | ||
| // in dlldata.c generated by MIDL (E.g. for IAccessible2, ia2_data.c), you can see: | ||
| // PROXYFILE_LIST_START, followed by REFERENCE_PROXY_FILE(IA2), followed by PROXYFILE_LIST_END. | ||
| // In RPCProxy.h from the Windows SDK, PROXYFILE_LIST_START declairs an unsized array of ProxyFileInfo pointers, REFERENCE_PROXY_FILE fills in each ProxyFileInfo pointer, and PROXYFILE_LIST_END places a final 0 to terminate the list. | ||
| // The reason it is a list is that multiple IDLs may be compiled into one proxy, and each IDL file gets its own ProxyFileInfo and therefore its own call to REFERENCE_PROXY_FILE | ||
| // Also see a similar implementation in Mozilla Gecko: | ||
| // https://hg.mozilla.org/mozilla-central/raw-file/1b4c59eef820b46eb0037aca68f83a15088db45f/ipc/mscom/Registration.cpp | ||
| ProxyFileInfo** tempInfoPtr=pProxyInfo; | ||
| while(*tempInfoPtr) { | ||
| ProxyFileInfo& fileInfo=**tempInfoPtr; | ||
| for(unsigned short idx=0;idx<fileInfo.TableSize;++idx) { | ||
| IID iid=*(fileInfo.pStubVtblList[idx]->header.piid); | ||
| CLSID clsidBackup={0}; | ||
| wstring_convert<codecvt_utf8_utf16<wchar_t>> converter; | ||
| wstring name=converter.from_bytes(fileInfo.pNamesArray[idx]); | ||
| // Fetch the old CLSID for this interface if one is set, so we can replace it on deregistration. | ||
| // If not set, then we'll use the standard marshaler clsid on deregistration. | ||
| res=CoGetPSClsid(iid,&clsidBackup); | ||
| if(res!=S_OK) { | ||
| clsidBackup=clsid_standardMarshaler; | ||
| } else { | ||
| LOG_DEBUG(L"Backed up existing clsid for interface "<<name); | ||
| } | ||
| res=CoRegisterPSClsid(iid,regClsid); | ||
| if(res!=S_OK) { | ||
| LOG_ERROR(L"Unable to register interface "<<name<<L" with proxy stub "<<dllPath<<L", code "<<res); | ||
| continue; | ||
| } | ||
| reg->psClsidBackups.push_back({name,iid,clsidBackup}); | ||
| LOG_DEBUG(L"Registered interface "<<name); | ||
| } | ||
| ++tempInfoPtr; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there some docs you can point to on ProxyFileInfo? I'm a little nervous about this code. So |
||
| } | ||
| // We can now safely free the proxy dll. COM will keep it loaded or re-load it if needed | ||
| FreeLibrary(dllHandle); | ||
| LOG_DEBUG(L"Done registering proxy "<<dllPath); | ||
| return reg; | ||
| } | ||
|
|
||
| bool unregisterCOMProxy(COMProxyRegistration_t* reg) { | ||
| if(!reg) return false; | ||
| HRESULT res; | ||
| LOG_DEBUG(L"Unregistering proxy "<<(reg->dllPath)); | ||
| for(auto& backup: reg->psClsidBackups) { | ||
| res=CoRegisterPSClsid(backup.iid,backup.clsid); | ||
| if(res!=S_OK) { | ||
| LOG_ERROR(L"Error registering backup PSClsid for interface "<<(backup.name)<<L" from "<<(reg->dllPath)<<L", code "<<res); | ||
| } | ||
| LOG_DEBUG(L"Unregistered interface "<<(backup.name)); | ||
| } | ||
| res=CoRevokeClassObject((DWORD)(reg->classObjectRegistrationCookie)); | ||
| if(res!=S_OK) { | ||
| LOG_ERROR(L"Error unregistering class object from "<<(reg->dllPath)<<L", code "<<res); | ||
| } | ||
| LOG_DEBUG(L"Done unregistering proxy "<<(reg->dllPath)); | ||
| delete reg; | ||
| return true; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nullptr