Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ build_script:
foreach ($syms in
# We don't just include source\*.dll because that would include system dlls.
"source\liblouis.dll", "source\*.pdb",
"source\lib\*.dll", "source\lib\*.pdb",
"source\lib\$env:version\*.dll", "source\lib\$env:version\*.pdb",
# We include source\lib64\*.exe to cover nvdaHelperRemoteLoader.
"source\lib64\*.dll", "source\lib64\*.exe", "source\lib64\*.pdb",
"source\lib64\$env:version\*.dll", "source\lib64\$env:version\*.exe", "source\lib64\$env:version\*.pdb",
"source\synthDrivers\*.dll", "source\synthDrivers\*.pdb"
) {
& $env:symstore add /s symbols /compress -:NOREFS /t NVDA /f $syms
Expand Down
7 changes: 5 additions & 2 deletions appveyor/mozillaSyms.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@
import zipfile
import requests

NVDA_VERSION=os.getenv('version')
SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
DUMP_SYMS = os.path.join(SCRIPT_DIR, "dump_syms.exe")
NVDA_SOURCE = os.path.join(os.path.dirname(SCRIPT_DIR), "source")
NVDA_LIB = os.path.join(NVDA_SOURCE, "lib")
NVDA_LIB64 = NVDA_LIB + "64"
NVDA_LIB = os.path.join(NVDA_SOURCE, "lib",NVDA_VERSION)
NVDA_LIB64 = os.path.join(NVDA_SOURCE, "lib64",NVDA_VERSION)
ZIP_FILE = os.path.join(SCRIPT_DIR, "mozillaSyms.zip")
URL = 'https://crash-stats.mozilla.com/symbols/upload'

# The dlls for which symbols are to be uploaded to Mozilla.
# This only needs to include dlls injected into Mozilla products.
DLL_NAMES = [
"IAccessible2Proxy.dll",
"ISimpleDOM.dll",
"minHook.dll",
"nvdaHelperRemote.dll",
"VBufBackend_adobeFlash.dll",
Expand All @@ -51,6 +53,7 @@ def check_output(command):
return stdout

def processFile(path):
print("dump_syms %s"%path)
try:
stdout = check_output([DUMP_SYMS, path])
except ProcError as e:
Expand Down
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>
22 changes: 19 additions & 3 deletions nvdaHelper/ISimpleDOM_sconscript
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
###
#This file is a part of the NVDA project.
#URL: http://www.nvda-project.org/
#Copyright 2014-2017 NV Access Limited.
#Copyright (C) 2014-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.
Expand All @@ -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,11 @@ 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],
# This CLSID must be unique to this dll. A new one can be generated with import comtypes; comtypes.GUID.create_new()
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,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
11 changes: 5 additions & 6 deletions nvdaHelper/ia2_sconscript
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
###
#This file is a part of the NVDA project.
#URL: http://www.nvda-project.org/
#Copyright 2006-2010 NVDA contributers.
#Copyright (C) 2006-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.
Expand All @@ -18,12 +18,11 @@ idlFile=env.Command("ia2.idl","#/miscDeps/include/ia2/ia2.idl",Copy("$TARGET","$

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'],
# This CLSID must be unique to this dll. A new one can be generated with import comtypes; comtypes.GUID.create_new()
proxyClsid="{62d295fe-2062-4369-a010-4f59b5e32d5e}"
)

Return(['proxyDll','tlbFile','headerFile','iidSourceFile','proxySourceFile','dlldataSourceFile'])
187 changes: 187 additions & 0 deletions nvdaHelper/remote/COMProxyRegistration.cpp
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(&regClsid);
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) {

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

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) {
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;
}
Loading