Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions nvdaHelper/COMProxy.manifest.subst
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>
19 changes: 17 additions & 2 deletions nvdaHelper/ISimpleDOM_sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@ Import('env')

env['MIDLCOM']=env['MIDLCOM'][:-6]

# Copy some secondary IDL files included by ISimpleDOMNode.idl
env.Command("ISimpleDOMText.idl","#/miscDeps/include/ISimpleDOM/ISimpleDOMText.idl",Copy("$TARGET","$SOURCE"))
env.Command("ISimpleDOMDocument.idl","#/miscDeps/include/ISimpleDOM/ISimpleDOMDocument.idl",Copy("$TARGET","$SOURCE"))
idlFile=env.Command("ISimpleDOMNode.idl","#/miscDeps/include/ISimpleDOM/ISimpleDOMNode.idl",Copy("$TARGET","$SOURCE"))
# copy ISimpleDOMNode.idl but changing imports of the secondary files to #includes
# This is necessary as midl will not build secondary header files. this way the primary header file will contain all secondary header file content
idlFile=env.Substfile(
target="iSimpleDOMNode.idl",
source="#/miscDeps/include/ISimpleDOM/ISimpleDOMNode.idl",
SUBST_DICT={
'import "ISimpleDOM':'#include "ISimpleDOM',
}
)

tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFile=env.TypeLibrary(
source=idlFile,
Expand All @@ -30,4 +39,10 @@ midl=env.WhereIs(env["MIDL"])
for target in (tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFile):
env.Ignore(target,midl)

Return(['tlbFile','headerFile','iidSourceFile','proxySourceFile','dlldataSourceFile'])
proxyDll=env.COMProxyDll(
target='ISimpleDOM',
source=[iidSourceFile,proxySourceFile,dlldataSourceFile],
proxyClsid="{435E0FC9-344B-41D4-88DD-4CAAD499ACE5}",
)

Return(['proxyDll','tlbFile','headerFile','iidSourceFile','proxySourceFile','dlldataSourceFile'])
55 changes: 54 additions & 1 deletion nvdaHelper/archBuild_sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,56 @@ Import(
'clientInstallDir',
)

# some utilities for COM proxies
def clsidStringToCLSIDDefine(clsidString):
"""
Converts a CLSID string of the form "{abcdef12-abcd-abcd-abcd-abcdef123456}"
Into a c-style struct initializer for initializing a GUID (I.e. "{0xabcdef12,0xabcd,0xabcd,{0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab}}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand this code right, I think the last part of this example result should be: {0xab, 0xcd, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56}

"""
d=clsidString[1:-1].replace('-','')
return "{%s,%s,%s,%s}"%(
"0x"+d[0:8],
"0x"+d[8:12],
"0x"+d[12:16],
"{%s}"%(",".join("0x"+d[x:x+2] for x in xrange(16,32,2)))
)

def COMProxyDllBuilder(env,target,source,proxyClsid):
"""
Builds a COM proxy dll from iid, proxy and dlldata c files generated from an IDL file with MIDL.
It provides the needed linker flags, and also embeds a manifest in the dll registering the given proxy CLSID for this dll's class object.
"""
proxyName=str(target)
manifestFile=env.Substfile(
target=proxyName+'.manifest',
source='COMProxy.manifest.subst',
SUBST_DICT={
'%proxyClsid%':proxyClsid,
'%proxyName%':proxyName,
}
)
proxyDll=env.SharedLibrary(
target=target,
source=source,
LIBS=['rpcrt4','oleaut32','ole32'],
CPPDEFINES=[
env['CPPDEFINES'],
'WIN32',
('PROXY_CLSID_IS',clsidStringToCLSIDDefine(proxyClsid)),
],
LINKFLAGS=[
env['LINKFLAGS'],
'/export:DllGetClassObject,private',
'/export:DllCanUnloadNow,private',
'/export:GetProxyDllInfo,private',
'/manifest:embed',
'/manifestinput:'+manifestFile[0].path,
],
)
env.Depends(proxyDll,manifestFile)
return proxyDll
env.AddMethod(COMProxyDllBuilder,'COMProxyDll')

# We only support compiling with MSVC 14 (2015)
if not env.get('MSVC_VERSION','').startswith('14.'):
raise RuntimeError("Microsoft Visual C++ 14 not found")
Expand Down Expand Up @@ -89,8 +139,11 @@ if TARGET_ARCH=='x86':
env.Install(sourceTypelibDir,ia2RPCStubs[1]) #typelib

iSimpleDomRPCStubs=env.SConscript('ISimpleDOM_sconscript')
if signExec:
env.AddPostAction(iSimpleDomRPCStubs[0],[signExec])
env.Install(libInstallDir,iSimpleDomRPCStubs[0]) #proxy dll
if TARGET_ARCH=='x86':
env.Install(sourceTypelibDir,iSimpleDomRPCStubs[0]) #typelib
env.Install(sourceTypelibDir,iSimpleDomRPCStubs[1]) #typelib

mathPlayerRPCStubs=env.SConscript('mathPlayer_sconscript')
if TARGET_ARCH=='x86':
Expand Down
9 changes: 4 additions & 5 deletions nvdaHelper/ia2_sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ Import('env')

idlFile=env.Command("ia2.idl","#/miscDeps/include/ia2/ia2.idl",Copy("$TARGET","$SOURCE"))


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra new line

tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFile=env.TypeLibrary(source=idlFile)

proxyDll=env.SharedLibrary(
target='IAccessible2Proxy',
proxyDll=env.COMProxyDll(
target='IAccessible2proxy',
source=[iidSourceFile,proxySourceFile,dlldataSourceFile],
LIBS=['rpcrt4','oleaut32','ole32'],
CPPDEFINES=[env['CPPDEFINES'],'WIN32','REGISTER_PROXY_DLL'],
LINKFLAGS=[env['LINKFLAGS'],'/export:DllGetClassObject,private','/export:DllCanUnloadNow,private'],
proxyClsid="{62d295fe-2062-4369-a010-4f59b5e32d5e}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this clsid come from?

)

Return(['proxyDll','tlbFile','headerFile','iidSourceFile','proxySourceFile','dlldataSourceFile'])
167 changes: 167 additions & 0 deletions nvdaHelper/remote/COMProxyRegistration.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
This file is a part of the NVDA project.
URL: http://www.nvda-project.org/
Copyright 2006-2010 NVDA contributers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The years listed are wrong. This also doesn't match the format we have on the wiki: https://github.com/nvaccess/nvda/wiki/Copyright-headers

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**);

const wchar_t* StringCLSID_StandardMarshaler=L"{00020424-0000-0000-C000-000000000046}";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps you could add a comment to this line? Is there a reference for this value?


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;
if((res=IIDFromString(StringCLSID_StandardMarshaler,&clsid_standardMarshaler))!=S_OK) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to see these kind of checks split into two lines:

res = IIDFromString(StringCLSID_StandardMarshaler,&clsid_standardMarshaler);
if( S_OK != res) {

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};
if((res=CoCreateGuid(&regClsid))!=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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullptr

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullptr
And there are a few more, I wont comment on all of them.

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;
actCtx.lpResourceName=MAKEINTRESOURCE(2);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is 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
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.
if((res=CoGetPSClsid(iid,&clsidBackup))!=S_OK) {
clsidBackup=clsid_standardMarshaler;
} else {
LOG_DEBUG(L"Backed up existing clsid for interface "<<name);
}
if((res=CoRegisterPSClsid(iid,regClsid))!=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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 pProxyInfo points to the start of a memory block containing ProxyFileInfo*. This memory block is null terminated. I cant find any documentation on Dll_GetProxyDllInfo How do you know the above is the case?

}
// 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) {
if((res=CoRegisterPSClsid(backup.iid,backup.clsid))!=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));
}
if((res=CoRevokeClassObject((DWORD)(reg->classObjectRegistrationCookie)))!=S_OK) {
LOG_ERROR(L"Error unregistering class object from "<<(reg->dllPath)<<L", code "<<res);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this (and the other error case on line 156) should the function exit early, why is it ok to continue to delete reg and return true

}
LOG_DEBUG(L"Done unregistering proxy "<<(reg->dllPath));
delete reg;
return true;
}

61 changes: 61 additions & 0 deletions nvdaHelper/remote/COMProxyRegistration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
This file is a part of the NVDA project.
URL: http://www.nvda-project.org/
Copyright 2006-2010 NVDA contributers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
*/

#ifndef NVDAHELPER_COMPROXYREGISTRATION
#define NVDAHELPER_COMPROXYREGISTRATION

#include <string>
#include <locale>
#include <codecvt>
#include <vector>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <objbase.h>

// Tracks information about a COM interface registered in-process
typedef struct {
// The name of the interface (for debugging)
std::wstring name;
// The unique identifier of the interface
IID iid;
// The CLSID of the original class object that handled creating / proxying of this interface.
// Used when unregistering, so we can put things back the way they were
CLSID clsid;
} PSClsidBackup_t;

// Represents the registration of a COM proxy dll and its interfaces.
// this can be used for later unregistration of the COM proxy dll
typedef struct {
// The path to the dll (for debugging)
std::wstring dllPath;
// The cookie returned by CoRegisterClassObject, for later unregistration via CoRevokeClassObject
ULONG_PTR classObjectRegistrationCookie;
// Information for all the interfaces registered for this proxy dll via CoRegisterPSClsid
std::vector<PSClsidBackup_t> psClsidBackups;
} COMProxyRegistration_t;

/* Registers a COM proxy dll and all its interfaces for this process so that they can be marshalled to/from other processes
@param dllPath the relative path to the proxy dll (relative from this dll)
@return registration data which can be later passed to UnregisterCOMProxy.
*/
COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath);

/* Unregisters a COM proxy dll originally registered with registerCOMProxy
@param reg the registration data returned by registerCOMProxy.
@return true if successful, false otherwise
*/
bool unregisterCOMProxy(COMProxyRegistration_t* reg);

#endif

Loading