WindowsXP-SP1/windows/oleacc/oleacc/oleacc.cpp
2020-09-30 16:53:49 +02:00

629 lines
19 KiB
C++
Raw Permalink Blame History

// Copyright (c) 1996-2000 Microsoft Corporation
// --------------------------------------------------------------------------
//
// OLEACC
//
// DllMain and other entry points.
//
// --------------------------------------------------------------------------
#include "oleacc_p.h"
#include "classmap.h"
#include "w95trace.h"
#include "memchk.h"
#include "fwd_macros.h"
#include "PropMgr_Client.h"
// Define this locally instead of #including "PropMgr_Impl", since that
// drags in lots of ATL goo.
void PropMgrImpl_Uninit();
//
// Shared Globals
//
// Note that these are only used when running on 9x; and are not even present
// on builds which are specifically for W2K.
//
#ifndef NTONLYBUILD
#pragma data_seg("Shared")
LONG g_cProcessesMinus1 = -1; // number of attached processes minus 1 (the Interlocked APIs don't work well with 0 as a base)
HANDLE hheapShared = NULL; // handle to the shared heap (Win95 only)
#pragma data_seg()
#pragma comment(linker, "/Section:Shared,RWS")
#endif // NTONLYBUILD
//
// Globals
//
HANDLE g_hLoadedMutex; // Useful mutex - by using oh.exe to look for this,
// we can see who's loaded our .dll.
HINSTANCE g_hinstDll; // instance of this library - nothing actually uses this at the moment.
HINSTANCE hinstResDll; // instance of resource library
BOOL fCreateDefObjs; // running with new USER32?
BOOL g_fInitedOleacc = FALSE; // Set by InitOleacc()
#ifdef _X86_
BOOL fWindows95; // running on Windows '95?
#endif // _X86_
//
// Forward decls...
//
//
// DLL rountines that we chain - we combine:
// * Core oleacc (only DllMan, reg and unreg)
// * RemoteProxyFactory6432
// * Proxy/Stubs fpr the PropMgr
//
extern "C" {
BOOL WINAPI Oleacc_DllMain( HINSTANCE hinst, DWORD dwReason, LPVOID pvReserved );
HRESULT WINAPI Oleacc_DllRegisterServer();
HRESULT WINAPI Oleacc_DllUnregisterServer();
BOOL WINAPI ProxyStub_DllMain( HINSTANCE hinst, DWORD dwReason, LPVOID pvReserved );
HRESULT WINAPI ProxyStub_DllRegisterServer();
HRESULT WINAPI ProxyStub_DllUnregisterServer();
HRESULT WINAPI ProxyStub_DllCanUnloadNow();
HRESULT WINAPI ProxyStub_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
BOOL WINAPI ComATLMain_DllMain( HINSTANCE hinst, DWORD dwReason, LPVOID pvReserved );
HRESULT WINAPI ComATLMain_DllRegisterServer();
HRESULT WINAPI ComATLMain_DllUnregisterServer();
HRESULT WINAPI ComATLMain_DllCanUnloadNow();
HRESULT WINAPI ComATLMain_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
};
// This one is called from DLLMain... (not safe to use LoadLibrary etc.)
static BOOL InitOleaccDLL( HINSTANCE hInstance );
// And this one is called in DLLMain when exiting...
static void UninitOleacc();
// This displays a "oleacc version xxxxxx loading/unloading from process xxxx" message
#ifdef _DEBUG
static void DisplayLoadUnloadString( LPCTSTR pszAction );
#endif // _DEBUG
// if this W2K and not ALPHA enable 64b/32b interoperability
#if defined(UNICODE) && !defined(_M_AXP64)
#define ENABLE6432_INTEROP
#endif
BOOL WINAPI DllMain( HINSTANCE hinst, DWORD dwReason, LPVOID pvReserved )
{
#ifdef _WIN64
DBPRINTF(TEXT("******** Hello from 64-bit OLEACC! ******** \r\n"));
#else
DBPRINTF(TEXT("******** Hello from 32-bit OLEACC! ******** \r\n"));
#endif
if( ! Oleacc_DllMain( hinst, dwReason, pvReserved ) )
{
DBPRINTF(TEXT("Oleacc_DllMain FAILED\r\n"));
return FALSE;
}
// Call the remote proxy factory for 64/32 bit interop DllMain
// TODO Possible performance improvement: only call ProxyFactoryDllMain if loaded by DLLHost32/64
if( ! ComATLMain_DllMain( hinst, dwReason, pvReserved ) )
{
DBPRINTF(TEXT("ComATLMain_DllMain FAILED\r\n"));
return FALSE;
}
if( ! ProxyStub_DllMain( hinst, dwReason, pvReserved ) )
{
DBPRINTF(TEXT("ProxyStub_DllMain FAILED\r\n"));
return FALSE;
}
return TRUE;
}
//
// Note about what gets registered:
//
// Oleacc_DllRegisterServer regiters the main typelib - this registers the
// IAccessible interfaces, and sets them to use typelib marshalling.
//
// ProxyStub_DllRegisterServer re-registers the IAccessible interface, also
// registers the IAccProp* annotation interfaces, and sets all those
// interfaces to use proxy/stub marshalling, overwriting the previous
// typelib marshalling, if any.
//
// Oleacc's DLLMain calls Oleacc_DllRegisterServer, which again
// re-registers the interfaces, and sets IAccessible back to using
// typelib marshalling.
//
// Bottom line is that all the interfaces do get registered, though
// some may use typelib marshalling, some may use proxy/stub marshalling.
// Doesn't really matter which is used; both marshalling techniques
// have the same end result.
//
//
//
// When unregistering, Oleacc_DllUnregisterServer calls the typelib
// unregistration function, which removes the entries for all the IAccessible
// interfaces.
//
// ProxyStub_DllUnregister tries to unregister all the IAccessible and IAccProp
// interfaces - it will successfully remove all those that exist, but on 9x,
// it will return an error hresult because some of those have already been
// unregistered by Oleacc_DllUnregisterServer. (W2K doesn't complain about
// this.) We can safely ignore this error.
//
//
// One possible alternative to all the above would be to just use the
// proxy/stub registration and marshalling, and remove the IAccessible
// typelib stuff completely. This would simplify this code, and simplify
// the data in the registry. However, the typelib approach was kept
// just in case there is some code or tool out there that expects to
// be able to find a typelib for IAccessible via the registry.
//
//
// (Note that the ComATL_* registers/unregisters the Win64 helper
// classes, and that always uses typelib marshalling - using the
// second typelib in oleacc.dll. See those functions for more info.)
//
//
HRESULT WINAPI DllRegisterServer()
{
HRESULT hrRet = Oleacc_DllRegisterServer();
HRESULT hr = ComATLMain_DllRegisterServer();
if( hrRet == S_OK )
hrRet = hr;
hr = ProxyStub_DllRegisterServer();
if( hrRet == S_OK )
hrRet = hr;
return hrRet;
}
HRESULT WINAPI DllUnregisterServer()
{
HRESULT hrRet = Oleacc_DllUnregisterServer();
HRESULT hr = ComATLMain_DllUnregisterServer();
if( hrRet == S_OK )
hrRet = hr;
// We ignore the 'file not found' error returned by the proxy/stub unregistration
// code. It tries to unreg all interfaces mentioned in oleacc.idl - but most of those
// have already been unregd by Oleacc_DllUnregisterServer above. On 9x, it returns
// this error, which we must ignore. (The w2k version doesn't return this error -
// it looks like it ignores it internally.)
hr = ProxyStub_DllUnregisterServer();
if( hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) && hrRet == S_OK )
hrRet = hr;
return hrRet;
}
HRESULT WINAPI DllCanUnloadNow()
{
HRESULT hr = ComATLMain_DllCanUnloadNow();
if( hr != S_OK )
return hr;
return ProxyStub_DllCanUnloadNow();
}
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
HRESULT hr = ComATLMain_DllGetClassObject(rclsid, riid, ppv);
if( hr == S_OK )
return hr;
return ProxyStub_DllGetClassObject(rclsid, riid, ppv);
}
//
// DLLMain
//
BOOL WINAPI Oleacc_DllMain( HINSTANCE hinst, DWORD dwReason, LPVOID unused( pvReserved ) )
{
if( dwReason == DLL_PROCESS_ATTACH )
{
// We don't need DLL_THREAD_XXX notifications - more efficient to not have them...
DisableThreadLibraryCalls( hinst );
if( ! InitOleaccDLL( hinst ) )
return FALSE;
}
else if (dwReason == DLL_PROCESS_DETACH)
{
UninitOleacc();
}
return TRUE;
}
//
// Oleacc_DllRegisterServer
//
#define TYPELIB_MAJORVER 1
#define TYPELIB_MINORVER 1
HRESULT WINAPI Oleacc_DllRegisterServer()
{
ITypeLib *pTypeLib = NULL;
HRESULT hr;
OLECHAR wszOleAcc[] = L"oleacc.dll";
hr = LoadTypeLib( wszOleAcc, &pTypeLib );
if ( SUCCEEDED(hr) )
{
hr = RegisterTypeLib( pTypeLib, wszOleAcc, NULL );
if ( FAILED(hr) )
DBPRINTF (TEXT("OLEACC: DllRegisterServer could not register type library hr=%lX\r\n"),hr);
pTypeLib->Release();
}
else
{
DBPRINTF (TEXT("OLEACC: DllRegisterServer could not load type library hr=%lX\r\n"),hr);
}
return S_OK;
}
HRESULT WINAPI Oleacc_DllUnregisterServer()
{
// The major/minor typelib version number determine
// which regisered version of OLEACC.DLL will get
// unregistered.
return UnRegisterTypeLib( LIBID_Accessibility, TYPELIB_MAJORVER, TYPELIB_MINORVER, 0, SYS_WIN32 );
}
// --------------------------------------------------------------------------
//
// Stubs for the exported APIs.
//
// The .def file references these (instead of referencing the exported APIs
// directly) - and these currently just call through to the API code.
// We can change these to pre/post process or jump elsewhere if necessary.
//
// --------------------------------------------------------------------------
#define FORWARD( ret, name, c, params ) /**/\
extern "C" ret WINAPI EXTERNAL_ ## name AS_DECL( c, params )\
{\
if( ! g_fInitedOleacc )\
InitOleacc();\
return name AS_CALL( c, params );\
}
#define FORWARD_VOID( name, c, params ) /**/\
extern "C" VOID WINAPI EXTERNAL_ ## name AS_DECL( c, params )\
{\
if( ! g_fInitedOleacc )\
InitOleacc();\
name AS_CALL( c, params );\
}
FORWARD( LRESULT, LresultFromObject, 3, ( REFIID, riid, WPARAM, wParam, LPUNKNOWN, punk ) )
FORWARD( HRESULT, ObjectFromLresult, 4, ( LRESULT, lResult, REFIID, riid, WPARAM, wParam, void**, ppvObject ) )
FORWARD( HRESULT, WindowFromAccessibleObject, 2, ( IAccessible *, ppAcc, HWND *, phwnd ) )
FORWARD( HRESULT, AccessibleObjectFromWindow, 4, ( HWND, hwnd, DWORD, dwId, REFIID, riid, void **, ppvObject ) )
FORWARD( HRESULT, AccessibleObjectFromEvent, 5, ( HWND, hwnd, DWORD, dwId, DWORD, dwChildId, IAccessible **, ppacc, VARIANT *, pvarChild ) )
FORWARD( HRESULT, AccessibleObjectFromPoint, 3, ( POINT, ptScreen, IAccessible **, ppacc, VARIANT *, pvarChild ) )
FORWARD( HRESULT, AccessibleChildren, 5, ( IAccessible *, paccContainer, LONG, iChildStart, LONG, cChildren, VARIANT *, rgvarChildren, LONG *, pcObtained ) )
FORWARD( UINT, GetRoleTextA, 3, ( DWORD, lRole, LPSTR, lpszRole, UINT, cchRoleMax ) )
FORWARD( UINT, GetRoleTextW, 3, ( DWORD, lRole, LPWSTR, lpszRole, UINT, cchRoleMax ) )
FORWARD( UINT, GetStateTextA, 3, ( DWORD, lStateBit, LPSTR, lpszState, UINT, cchState ) )
FORWARD( UINT, GetStateTextW, 3, ( DWORD, lStateBit, LPWSTR, lpszState, UINT, cchState ) )
FORWARD( HRESULT, CreateStdAccessibleObject, 4, ( HWND, hwnd, LONG, idObject, REFIID, riid, void **, ppvObject ) )
FORWARD( HRESULT, CreateStdAccessibleProxyA, 5, ( HWND, hwnd, LPCSTR, pClassName, LONG, idObject, REFIID, riid, void **, ppvObject ) )
FORWARD( HRESULT, CreateStdAccessibleProxyW, 5, ( HWND, hwnd, LPCWSTR, pClassName, LONG, idObject, REFIID, riid, void **, ppvObject ) )
FORWARD_VOID( GetOleaccVersionInfo, 2, ( DWORD *, pVer, DWORD *, pBuild ) )
//
// InitOleaccDLL
//
// Called from DLLMain - only do bare minimum init here - leave proper initing
// to InitOleacc(), which is called the first time one of the APIs is called.
//
// Since this is called from DLLMain, we can't use LoadLibrary() (or anything
// that uses that) here. See the docs on DLLMain for additional restrictions.
//
// Anything that can affect the success of this DLL loading/unloading should
// also go here.
//
static
BOOL InitOleaccDLL( HINSTANCE hInstance )
{
g_hLoadedMutex = CreateMutex(NULL,NULL,__TEXT("oleacc-msaa-loaded"));
g_hinstDll = hInstance;
// check platform version information
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
// refuse to run on Win32s
if (osvi.dwPlatformId == VER_PLATFORM_WIN32s)
return FALSE;
#ifdef _X86_
fWindows95 = (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
#endif // _X86_
// Load the resource-only DLL. We're not supposed to use regular
// LoadLibary from within DLLMain, but LoadLibraryEx is safe,
// since it only maps the file, and doesn't exec any code (that
// lib's DLLMain).
hinstResDll = LoadLibraryEx( TEXT("OLEACCRC.DLL"), NULL, LOAD_LIBRARY_AS_DATAFILE );
if( ! hinstResDll )
{
// Refuse to load if oleaccrc isn't present
return FALSE;
}
// Use the counter g_cProcessesMinus1 to figure out if this is the
// first time this DLL is loaded. If so, do any necessary once-off
// setup - eg. setting up values in the (9x-only) shared segment.
// InterlockedIncrement() and Decrement() return 1 if the result is
// positive, 0 if zero, and -1 if negative. Therefore, the only
// way to use them practically is to start with a counter at -1.
// Then incrementing from -1 to 0, the first time, will give you
// a unique value of 0. And decrementing the last time from 0 to -1
// will give you a unique value of -1.
#ifndef NTONLYBUILD
#ifdef _X86_
if( InterlockedIncrement( & g_cProcessesMinus1 ) == 0 )
{
// if on Win95, create a shared heap to use when
// communicating with controls in other processes
if( fWindows95 )
{
hheapShared = HeapCreate( HEAP_SHARED, 0, 0 );
if( hheapShared == NULL )
return FALSE;
}
}
#endif // _X86_
#endif // NTONLYBUILD
return TRUE;
}
//
// InitOleacc
//
// Called when any of our APIs is called for the first time. Proper initialization
// happens here. Also called in DllGetClassObject.
//
BOOL InitOleacc()
{
if (g_fInitedOleacc)
return TRUE;
g_fInitedOleacc = TRUE;
// set the flag that causes proxies to work.
fCreateDefObjs = TRUE;
// Init any GetProcAddress'd imports
InitImports();
#ifdef _DEBUG
// Initialize the new/delete checker...
InitMemChk();
DisplayLoadUnloadString( TEXT("loading") );
#ifndef NTONLYBUILD
// On first load, show dialog with names of any imports not found...
if( g_cProcessesMinus1 == 0 )
{
TCHAR szErrorMsg[1024];
lstrcpy( szErrorMsg,TEXT("WARNING: the following functions were not found:\r\n") );
LPTSTR pEnd = szErrorMsg + lstrlen( szErrorMsg );
ReportMissingImports( pEnd );
if( *pEnd )
{
MessageBeep( MB_ICONEXCLAMATION );
MessageBox( NULL, szErrorMsg, TEXT("OLEACC.DLL"), MB_OK | MB_ICONEXCLAMATION );
DBPRINTF( szErrorMsg );
}
}
#endif // NTONLYBUILD
#endif // _DEBUG
// Everything that follows is done for every process attach.
// do a LoadLibrary of OLEAUT32.DLL to ensure that it stays
// loaded in every process that OLEACC is attched to.
// There is a slight chance of a crash. From Paul Vick:
// The problem occurs when oleaut32.dll is loaded into a process,
// used, unloaded and then loaded again. When you try to use
// oleaut32.dll in this case, you crash because of a problem
// manging state between us (oleaut32) and OLE. What happens is that when
// oleaut32.dll is loaded, we register some state with OLE so
// we are notified when OLE is uninitialized. In some cases
// (esp. multithreading), we are not able to clean up this
// state when we<77>re unloaded. When you reload oleaut32.dll,
// we try to set the state in OLE again and OLE tries to free
// the old (invalid) state, causing a crash later on.
LoadLibrary( TEXT("OLEAUT32.DLL") );
InitWindowClasses();
// Since Office 97 can mess up the registration,
// we'll call our self-registration function.
// may be slight perf hit on load, but not that big a deal,
// and this way we know we are always correctly registered..
Oleacc_DllRegisterServer();
// Initialize the property/annotation client
PropMgrClient_Init();
return TRUE;
}
//
// UninitOleacc
//
// Called on DLL Detach time from DLLMain. Any necessary cleanup happens here.
//
static
void UninitOleacc()
{
PropMgrImpl_Uninit();
PropMgrClient_Uninit();
// Release the resource DLL. Usually FreeLibrary isn't safe, but it's OK
// here since this is a resource-only DLL that was loaded _AS_DATAFILE.
if( hinstResDll )
FreeLibrary( hinstResDll );
#ifdef _DEBUG
// This reports the number of outstanding delete's, if any...
UninitMemChk();
DisplayLoadUnloadString( TEXT("unloading") );
#endif // _DEBUG
#ifndef NTONLYBUILD
// stuff that needs to be cleaned up on the last detach - clean
// up stuff in the shared data segment.
if (InterlockedDecrement(&g_cProcessesMinus1) == -1)
{
#ifdef _X86_
if (fWindows95)
{
if (hheapShared)
HeapDestroy(hheapShared);
hheapShared = NULL;
}
#endif // _X86_
UnInitWindowClasses();
}
#endif // NTONLYBUILD
if( g_hLoadedMutex )
CloseHandle( g_hLoadedMutex );
}
#ifdef _DEBUG
void DisplayLoadUnloadString( LPCTSTR pszAction )
{
TCHAR szModName[255];
TCHAR szVer[1024] = TEXT("???");
// Output to debug terminal - oleacc was attached to process x,
// show which oleacc version is running, what directory it was
// loaded from, etc.
// NULL -> get application's name (not oleacc)
MyGetModuleFileName( NULL, szModName, ARRAYSIZE( szModName ) );
DBPRINTF( TEXT("'%s' is %s "),szModName, pszAction );
MyGetModuleFileName( g_hinstDll, szModName, ARRAYSIZE( szModName ) );
DWORD dwUseless;
DWORD dwSize = GetFileVersionInfoSize( szModName, & dwUseless );
if( dwSize )
{
LPVOID lpVersionData = LocalAlloc( LPTR, (UINT) dwSize );
if( GetFileVersionInfo( szModName, dwUseless, dwSize, lpVersionData ) )
{
VS_FIXEDFILEINFO * lpVersionInfo;
DWORD dwBytes;
VerQueryValue( lpVersionData, TEXT("\\"), (void**)&lpVersionInfo, (UINT*)&dwBytes );
wsprintf( szVer, TEXT("%d.%d.%d.%d"), HIWORD( lpVersionInfo->dwFileVersionMS ),
LOWORD( lpVersionInfo->dwFileVersionMS ),
HIWORD( lpVersionInfo->dwFileVersionLS ),
LOWORD( lpVersionInfo->dwFileVersionLS ) );
}
LocalFree( (HLOCAL) lpVersionData );
}
DBPRINTF( TEXT("%s version %s\r\n"), szModName, szVer );
}
#endif // _DEBUG