mumble/overlay/lib.cpp
Mikkel Krautz 8272e4872f overlay: optimize blit() to not perform a blit if the active item rect is empty.
It seems that in EVE Online, if we update our overlay texture but do not
draw to the screen, the texture mapping is never freed, until we begin
drawing again.

I do not know enough D3D11 to know why.

Instead, this commit works around the issue by introducing a fully legal
optimization to the blit() method:

If the rect of active overlay elements is empty (that is, the screen is
empty), do not perform a blit at all.

The change also introduces an extra call to blit upon receiving
OVERLAY_MSG_ACTIVE. That message is the message that signals that
the rect of active elements has changed.
We need to blit here to ensure we redraw correctly once the rect
of active overlay elements changes.

Fixes mumble-voip/mumble#1123
2016-07-02 12:18:57 +02:00

810 lines
22 KiB
C++

// Copyright 2005-2016 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "lib.h"
#include "overlay_blacklist.h"
#include "overlay_exe/overlay_exe.h"
#undef max // for std::numeric_limits<T>::max()
static HANDLE hMapObject = NULL;
static HANDLE hHookMutex = NULL;
static HHOOK hhookWnd = 0;
BOOL bIsWin8 = FALSE;
static BOOL bMumble = FALSE;
static BOOL bDebug = FALSE;
static BOOL bBlackListed = FALSE;
static HardHook hhLoad;
static HardHook hhLoadW;
static SharedData *sd = NULL;
CRITICAL_SECTION Mutex::cs;
void Mutex::init() {
InitializeCriticalSection(&cs);
}
Mutex::Mutex() {
if (! TryEnterCriticalSection(&cs)) {
ods("Lib: Mutex: CritFail - blocking until able to enter critical section");
EnterCriticalSection(&cs);
}
}
Mutex::~Mutex() {
LeaveCriticalSection(&cs);
}
void __cdecl ods(const char *format, ...) {
#ifndef DEBUG
if (!bDebug)
return;
#endif
va_list args;
va_start(args, format);
_ods_out(format, &args);
va_end(args);
}
void __cdecl checkForWPF() {
if (!bBlackListed && (GetModuleHandleW(L"wpfgfx_v0300.dll") || GetModuleHandleW(L"wpfgfx_v0400.dll"))) {
ods("Lib: Blacklisted for loading WPF library");
bBlackListed = TRUE;
}
}
Pipe::Pipe() {
hSocket = INVALID_HANDLE_VALUE;
hMemory = NULL;
a_ucTexture = NULL;
omMsg.omh.iLength = -1;
dwAlreadyRead = 0;
uiWidth = uiHeight = 0;
uiLeft = uiRight = uiTop = uiBottom = 0;
}
Pipe::~Pipe() {
disconnect();
}
void Pipe::release() {
if (hMemory) {
CloseHandle(hMemory);
hMemory = NULL;
if (a_ucTexture) {
UnmapViewOfFile(a_ucTexture);
a_ucTexture = NULL;
}
uiLeft = uiRight = uiTop = uiBottom = 0;
}
}
void Pipe::disconnect() {
release();
if (hSocket != INVALID_HANDLE_VALUE) {
ods("Pipe: Disconnect");
CloseHandle(hSocket);
hSocket = INVALID_HANDLE_VALUE;
}
uiWidth = 0;
uiHeight = 0;
omMsg.omh.iLength = -1;
}
bool Pipe::sendMessage(const OverlayMsg &om) {
DWORD dwBytesToWrite = sizeof(OverlayMsgHeader) + om.omh.iLength;
DWORD dwBytesWritten = dwBytesToWrite;
if (WriteFile(hSocket, om.headerbuffer, sizeof(OverlayMsgHeader) + om.omh.iLength, &dwBytesWritten, NULL))
if (dwBytesToWrite == dwBytesWritten)
return true;
ods("Pipe: Short write");
disconnect();
return false;
}
void Pipe::checkMessage(unsigned int width, unsigned int height) {
if (!width || ! height)
return;
if (hSocket == INVALID_HANDLE_VALUE) {
hSocket = CreateFileW(L"\\\\.\\pipe\\MumbleOverlayPipe", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hSocket == INVALID_HANDLE_VALUE) {
ods("Pipe: Connection failed");
return;
}
ods("Pipe: Connected");
uiWidth = 0;
uiHeight = 0;
// initially, instantiate and send an OverlayMessage with the current process id
OverlayMsg om;
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
om.omh.uiType = OVERLAY_MSGTYPE_PID;
om.omh.iLength = sizeof(OverlayMsgPid);
om.omp.pid = GetCurrentProcessId();
if (!sendMessage(om))
return;
ods("Pipe: Process ID sent");
}
// if the passed width and height do not match the current overlays uiWidth and uiHeight, re-initialize
if ((uiWidth != width) || (uiHeight != height)) {
release();
uiWidth = width;
uiHeight = height;
// instantiate and send an initialization-OverlayMessage
OverlayMsg om;
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
om.omh.uiType = OVERLAY_MSGTYPE_INIT;
om.omh.iLength = sizeof(OverlayMsgInit);
om.omi.uiWidth = uiWidth;
om.omi.uiHeight = uiHeight;
if (!sendMessage(om))
return;
ods("Pipe: SentInitMsg with w h %d %d", uiWidth, uiHeight);
}
std::vector<RECT> blits;
while (1) {
DWORD dwBytesLeft;
DWORD dwBytesRead;
if (! PeekNamedPipe(hSocket, NULL, 0, NULL, &dwBytesLeft, NULL)) {
ods("Pipe: Could not peek");
disconnect();
return;
}
if (! dwBytesLeft)
break;
if (omMsg.omh.iLength == -1) {
if (! ReadFile(hSocket, reinterpret_cast<unsigned char *>(omMsg.headerbuffer) + dwAlreadyRead, sizeof(OverlayMsgHeader) - dwAlreadyRead, &dwBytesRead, NULL)) {
ods("Pipe: Read header fail");
disconnect();
return;
}
dwBytesLeft -= dwBytesRead;
dwAlreadyRead += dwBytesRead;
if (dwAlreadyRead != sizeof(OverlayMsgHeader)) {
break;
}
dwAlreadyRead = 0;
if (omMsg.omh.uiMagic != OVERLAY_MAGIC_NUMBER) {
ods("Pipe: Invalid magic number %x", omMsg.omh.uiMagic);
disconnect();
return;
}
if (static_cast<int>(dwBytesLeft) < omMsg.omh.iLength)
continue;
}
if (! ReadFile(hSocket, reinterpret_cast<unsigned char *>(omMsg.msgbuffer) + dwAlreadyRead, omMsg.omh.iLength - dwAlreadyRead, &dwBytesRead, NULL)) {
ods("Pipe: Read data fail");
disconnect();
return;
}
dwAlreadyRead += dwBytesRead;
if (static_cast<int>(dwBytesLeft) < omMsg.omh.iLength)
continue;
dwAlreadyRead = 0;
switch (omMsg.omh.uiType) {
case OVERLAY_MSGTYPE_SHMEM: {
wchar_t memname[2048];
memname[0] = 0;
MultiByteToWideChar(CP_UTF8, 0, omMsg.oms.a_cName, omMsg.omh.iLength, memname, 2048);
release();
hMemory = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, uiWidth * uiHeight * 4, memname);
if (GetLastError() != ERROR_ALREADY_EXISTS) {
ods("Pipe: Memory %s(%d) => %ls doesn't exist", omMsg.oms.a_cName, omMsg.omh.iLength, memname);
if (hMemory) {
CloseHandle(hMemory);
hMemory = NULL;
break;
}
}
if (! hMemory) {
ods("Pipe: CreateFileMapping failed");
break;
}
a_ucTexture = reinterpret_cast<unsigned char *>(MapViewOfFile(hMemory, FILE_MAP_ALL_ACCESS, 0, 0, 0));
if (a_ucTexture == NULL) {
ods("Pipe: Failed to map memory");
CloseHandle(hMemory);
hMemory = NULL;
break;
}
MEMORY_BASIC_INFORMATION mbi;
memset(&mbi, 0, sizeof(mbi));
if ((VirtualQuery(a_ucTexture, &mbi, sizeof(mbi)) == 0) || (mbi.RegionSize < (uiHeight * uiWidth * 4))) {
ods("Pipe: Memory too small");
UnmapViewOfFile(a_ucTexture);
CloseHandle(hMemory);
a_ucTexture = NULL;
hMemory = NULL;
break;
}
OverlayMsg om;
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
om.omh.uiType = OVERLAY_MSGTYPE_SHMEM;
om.omh.iLength = 0;
if (!sendMessage(om))
return;
newTexture(uiWidth, uiHeight);
}
break;
case OVERLAY_MSGTYPE_BLIT: {
RECT r = {omMsg.omb.x, omMsg.omb.y, omMsg.omb.x + omMsg.omb.w, omMsg.omb.y + omMsg.omb.h};
std::vector<RECT>::iterator i = blits.begin();
while (i != blits.end()) {
RECT is;
if (::IntersectRect(&is, &r, & *i)) {
::UnionRect(&is, &r, & *i);
r = is;
blits.erase(i);
i = blits.begin();
} else {
++i;
}
}
blits.push_back(r);
}
break;
case OVERLAY_MSGTYPE_ACTIVE: {
uiLeft = omMsg.oma.x;
uiTop = omMsg.oma.y;
uiRight = omMsg.oma.x + omMsg.oma.w;
uiBottom = omMsg.oma.y + omMsg.oma.h;
if (a_ucTexture) {
setRect();
blit(0, 0, uiWidth, uiHeight);
}
}
break;
default:
break;
}
omMsg.omh.iLength = -1;
}
if (!a_ucTexture)
return;
for (std::vector<RECT>::iterator i = blits.begin(); i != blits.end(); ++i)
blit((*i).left, (*i).top, (*i).right - (*i).left, (*i).bottom - (*i).top);
}
static void checkHooks(bool preonly) {
checkD3D9Hook(preonly);
checkDXGIHook(preonly);
checkDXGI10Hook(preonly);
checkDXGI11Hook(preonly);
checkOpenGLHook();
}
typedef HMODULE(__stdcall *LoadLibraryAType)(const char *);
static HMODULE WINAPI MyLoadLibrary(const char *lpFileName) {
//TODO: Move logic to HardHook.
// Call base without active hook in case of no trampoline.
LoadLibraryAType oLoadLibrary = (LoadLibraryAType) hhLoad.call;
hhLoad.restore();
HMODULE h = oLoadLibrary(lpFileName);
hhLoad.inject();
ods("Lib: Library %s loaded to %p", lpFileName, h);
if (! bBlackListed) {
checkHooks();
}
return h;
}
typedef HMODULE(__stdcall *LoadLibraryWType)(const wchar_t *);
static HMODULE WINAPI MyLoadLibraryW(const wchar_t *lpFileName) {
//TODO: Move logic to HardHook.
// Call base without active hook in case of no trampoline.
LoadLibraryWType oLoadLibrary = (LoadLibraryWType) hhLoadW.call;
hhLoadW.restore();
HMODULE h = oLoadLibrary(lpFileName);
hhLoadW.inject();
ods("Lib: Library %ls wloaded to %p", lpFileName, h);
checkForWPF();
if (! bBlackListed) {
checkHooks();
}
return h;
}
static LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
return CallNextHookEx(hhookWnd, nCode, wParam, lParam);
}
extern "C" __declspec(dllexport) void __cdecl RemoveHooks() {
DWORD dwWaitResult = WaitForSingleObject(hHookMutex, 1000L);
if (dwWaitResult == WAIT_OBJECT_0) {
if (sd != NULL && sd->bHooked) {
if (hhookWnd) {
UnhookWindowsHookEx(hhookWnd);
hhookWnd = NULL;
}
sd->bHooked = false;
}
ReleaseMutex(hHookMutex);
}
}
extern "C" __declspec(dllexport) void __cdecl InstallHooks() {
DWORD dwWaitResult = WaitForSingleObject(hHookMutex, 1000L);
if (dwWaitResult == WAIT_OBJECT_0) {
if (sd != NULL && ! sd->bHooked) {
HMODULE hSelf = NULL;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (char *) &InstallHooks, &hSelf);
if (hSelf == NULL) {
ods("Lib: Failed to find myself");
} else {
hhookWnd = SetWindowsHookEx(WH_CBT, CallWndProc, hSelf, 0);
if (hhookWnd == NULL)
ods("Lib: Failed to insert WNDProc hook");
}
sd->bHooked = true;
}
ReleaseMutex(hHookMutex);
}
}
extern "C" __declspec(dllexport) unsigned int __cdecl GetOverlayMagicVersion() {
return OVERLAY_MAGIC_NUMBER;
}
// Via d3d9.cpp
extern "C" __declspec(dllexport) void __cdecl PrepareD3D9();
// Via dxgi.cpp
extern "C" __declspec(dllexport) void __cdecl PrepareDXGI();
void __stdcall OverlayHelperProcessParentDeathThread(void *udata) {
HANDLE parent = reinterpret_cast<HANDLE>(udata);
DWORD status = WaitForSingleObject(parent, INFINITE);
if (status != WAIT_OBJECT_0) {
ExitProcess(OVERLAY_HELPER_ERROR_DLL_PDEATH_WAIT_FAIL);
}
ExitProcess(0);
}
extern "C" __declspec(dllexport) int __cdecl OverlayHelperProcessMain(unsigned int magic, HANDLE parent) {
int retval = 0;
if (GetOverlayMagicVersion() != magic) {
return OVERLAY_HELPER_ERROR_DLL_MAGIC_MISMATCH;
}
HANDLE pcheckHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) OverlayHelperProcessParentDeathThread,
reinterpret_cast<void *>(parent), 0, NULL);
if (pcheckHandle == 0) {
return OVERLAY_HELPER_ERROR_DLL_PDEATH_THREAD_ERROR;
}
PrepareD3D9();
PrepareDXGI();
InstallHooks();
while (1) {
MSG msg;
BOOL ret;
ret = GetMessage(&msg, NULL, 0, 0);
// The ret variable is set to 0 on WM_QUIT,
// and -1 on error.
if (ret == 0) {
retval = 0;
break;
} else if (ret == -1) {
retval = -1001;
break;
}
if (msg.message == WM_CLOSE) {
retval = 0;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
RemoveHooks();
return retval;
}
static bool dllmainProcAttachCheckProcessIsBlacklisted(char procname[], char *p);
static bool createSharedDataMap();
static void dllmainProcAttach(char *procname) {
Mutex::init();
char *p = strrchr(procname, '\\');
if (!p) {
// No blacklisting if the file has no path
} else if (GetProcAddress(NULL, "mumbleSelfDetection") != NULL) {
ods("Lib: Attached to overlay helper or Mumble process. Blacklisted - no overlay injection.");
bBlackListed = TRUE;
bMumble = TRUE;
} else {
if (dllmainProcAttachCheckProcessIsBlacklisted(procname, p)) {
ods("Lib: Process %s is blacklisted - no overlay injection.", procname);
return;
}
}
OSVERSIONINFOEX ovi;
memset(&ovi, 0, sizeof(ovi));
ovi.dwOSVersionInfoSize = sizeof(ovi);
GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&ovi));
bIsWin8 = (ovi.dwMajorVersion >= 7) || ((ovi.dwMajorVersion == 6) && (ovi.dwBuildNumber >= 9200));
ods("Lib: bIsWin8: %i", bIsWin8);
hHookMutex = CreateMutex(NULL, false, "MumbleHookMutex");
if (hHookMutex == NULL) {
ods("Lib: CreateMutex failed");
return;
}
if(!createSharedDataMap())
return;
if (! bMumble) {
// Hook our own LoadLibrary functions so we notice when a new library (like the d3d ones) is loaded.
hhLoad.setup(reinterpret_cast<voidFunc>(LoadLibraryA), reinterpret_cast<voidFunc>(MyLoadLibrary));
hhLoadW.setup(reinterpret_cast<voidFunc>(LoadLibraryW), reinterpret_cast<voidFunc>(MyLoadLibraryW));
checkHooks(true);
ods("Lib: Injected into %s", procname);
}
}
// Is the process black(listed)?
static bool dllmainProcAttachCheckProcessIsBlacklisted(char procname[], char *p) {
DWORD buffsize = MAX_PATH * 20; // Initial buffer size for registry operation
bool usewhitelist = false;
HKEY key = NULL;
char *buffer = new char[buffsize];
// check if we're using a whitelist or a blacklist
DWORD tmpsize = buffsize - 1;
bool success = (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Mumble\\Mumble\\overlay", NULL, KEY_READ, &key) == ERROR_SUCCESS) &&
(RegQueryValueExA(key, "usewhitelist", NULL, NULL, (LPBYTE)buffer, &tmpsize) == ERROR_SUCCESS);
if (success) {
buffer[tmpsize] = '\0';
usewhitelist = (_stricmp(buffer, "true") == 0);
// reset tmpsize to the buffers size (minus 1 char for str-termination), as it was changed by RegQuery
tmpsize = buffsize - 1;
// read the whitelist or blacklist (depending on which one we use)
DWORD ret;
while ((ret = RegQueryValueExA(key, usewhitelist ? "whitelist" : "blacklist", NULL, NULL, (LPBYTE)buffer, &tmpsize)) == ERROR_MORE_DATA) {
// Increase the buffsize according to the required size RegQuery wrote into tmpsize, so we can read the whole value
delete []buffer;
buffsize = tmpsize + 1;
buffer = new char[buffsize];
}
success = (ret == ERROR_SUCCESS);
}
if (key)
RegCloseKey(key);
if (success) {
buffer[tmpsize] = '\0';
unsigned int pos = 0;
if (usewhitelist) {
// check if process is whitelisted
bool onwhitelist = false;
while (pos < buffsize && buffer[pos] != 0) {
if (_stricmp(procname, buffer + pos) == 0 || _stricmp(p+1, buffer + pos) == 0) {
ods("Lib: Overlay enabled for whitelisted '%s'", buffer + pos);
onwhitelist = true;
break;
}
pos += static_cast<unsigned int>(strlen(buffer + pos)) + 1;
}
if (!onwhitelist) {
ods("Lib: No whitelist entry found for '%s', auto-blacklisted", procname);
bBlackListed = TRUE;
return true;
}
} else {
// check if process is blacklisted
while (pos < buffsize && buffer[pos] != 0) {
if (_stricmp(procname, buffer + pos) == 0 || _stricmp(p+1, buffer + pos) == 0) {
ods("Lib: Overlay blacklist entry found for '%s'", buffer + pos);
bBlackListed = TRUE;
return true;
}
pos += static_cast<unsigned int>(strlen(buffer + pos)) + 1;
}
}
} else {
ods("Lib: no blacklist/whitelist found in the registry");
}
// As a last resort, if we're using blacklisting, check the built-in blacklist.
//
// If the registry query failed this means we're guaranteed to check the
// built-in list.
//
// If the list in the registry is out of sync, for example because the built-
// in list in overlay_blacklist.h was updated got updated, we're also
// guaranteed that we include all built-in blacklisted items in our check.
if (!usewhitelist) {
ods("Lib: Overlay fallback to default blacklist");
int i = 0;
while (overlayBlacklist[i]) {
if (_stricmp(procname, overlayBlacklist[i]) == 0 || _stricmp(p+1, overlayBlacklist[i])==0) {
ods("Lib: Overlay default blacklist entry found for '%s'", overlayBlacklist[i]);
bBlackListed = TRUE;
return true;
}
i++;
}
}
// Make sure to always free/destroy buffer & heap
delete []buffer;
// if the processname is already found to be blacklisted, we can stop here
if (bBlackListed)
return true;
// check if there is a "nooverlay" file in the executables folder, which would disable/blacklist the overlay
// Same buffersize as procname; which we copy from.
char fname[PROCNAMEFILEPATH_EXTENDED_BUFFER_BUFLEN];
size_t pathlength = static_cast<size_t>(p - procname);
p = fname + pathlength;
strncpy_s(fname, sizeof(fname), procname, pathlength + 1);
strcpy_s(p+1, PROCNAMEFILEPATH_EXTENDED_EXTLEN, "nooverlay");
HANDLE h = CreateFile(fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h != INVALID_HANDLE_VALUE) {
CloseHandle(h);
ods("Lib: Overlay disable %s found", fname);
bBlackListed = TRUE;
return true;
}
// check for "debugoverlay" file, which would enable overlay debugging
strcpy_s(p+1, PROCNAMEFILEPATH_EXTENDED_EXTLEN, "debugoverlay");
h = CreateFile(fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h != INVALID_HANDLE_VALUE) {
CloseHandle(h);
ods("Lib: Overlay debug %s found", fname);
bDebug = TRUE;
}
// check for blacklisting for loading WPF library
checkForWPF();
if (bBlackListed)
return true;
return false;
}
static bool createSharedDataMap() {
DWORD dwSharedSize = sizeof(SharedData) + sizeof(Direct3D9Data) + sizeof(DXGIData) + sizeof(D3D10Data) + sizeof(D3D11Data);
#if defined(_M_IX86)
const char *name = "MumbleOverlayPrivate-x86";
#elif defined(_M_X64)
const char *name = "MumbleOverlayPrivate-x64";
#else
# error Unsupported architecture
#endif
hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, dwSharedSize, name);
if (hMapObject == NULL) {
ods("Lib: CreateFileMapping failed");
return false;
}
//Note: If the mapping exists dwSharedSize value will be ignored and existing handle returned
bool bInit = (GetLastError() != ERROR_ALREADY_EXISTS);
unsigned char *rawSharedPointer = static_cast<unsigned char *>(
MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, dwSharedSize));
if (rawSharedPointer == NULL) {
ods("Lib: MapViewOfFile failed");
return false;
}
if (bInit)
memset(rawSharedPointer, 0, dwSharedSize);
sd = reinterpret_cast<SharedData *>(rawSharedPointer);
rawSharedPointer += sizeof(SharedData);
d3dd = reinterpret_cast<Direct3D9Data *>(rawSharedPointer);
rawSharedPointer += sizeof(Direct3D9Data);
dxgi = reinterpret_cast<DXGIData *>(rawSharedPointer);
rawSharedPointer += sizeof(DXGIData);
d3d10 = reinterpret_cast<D3D10Data *>(rawSharedPointer);
rawSharedPointer += sizeof(D3D10Data);
d3d11 = reinterpret_cast<D3D11Data *>(rawSharedPointer);
rawSharedPointer += sizeof(D3D11Data);
return true;
}
static void dllmainProcDetach() {
hhLoad.restore(true);
hhLoad.reset();
hhLoadW.restore(true);
hhLoadW.reset();
if (sd)
UnmapViewOfFile(sd);
if (hMapObject)
CloseHandle(hMapObject);
if (hHookMutex)
CloseHandle(hHookMutex);
}
static void dllmainThreadAttach() {
static bool bTriedHook = false;
if (!bBlackListed && sd && ! bTriedHook) {
bTriedHook = true;
checkForWPF();
if (!bBlackListed) {
ods("Lib: Checking for hooks, potentially injecting");
checkHooks();
}
}
}
extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID) {
char procname[PROCNAMEFILEPATH_EXTENDED_BUFFER_BUFLEN];
GetModuleFileNameA(NULL, procname, ARRAY_NUM_ELEMENTS(procname));
// Fix for windows XP; on length nSize does not include null-termination
// @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683197%28v=vs.85%29.aspx
procname[ARRAY_NUM_ELEMENTS(procname) - 1] = '\0';
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
ods("Lib: ProcAttach: %s", procname);
dllmainProcAttach(procname);
break;
case DLL_PROCESS_DETACH:
ods("Lib: ProcDetach: %s", procname);
dllmainProcDetach();
break;
case DLL_THREAD_ATTACH:
ods("Lib: ThreadAttach: %s", procname);
dllmainThreadAttach();
break;
default:
break;
}
return TRUE;
}
bool IsFnInModule(voidFunc fnptr, wchar_t *refmodulepath, const std::string &logPrefix, const std::string &fnName) {
HMODULE hModule = NULL;
BOOL success = GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCSTR>(fnptr), &hModule);
if (!success) {
ods((logPrefix + ": Failed to get module for " + fnName).c_str());
} else {
wchar_t modulename[MODULEFILEPATH_BUFLEN];
GetModuleFileNameW(hModule, modulename, ARRAY_NUM_ELEMENTS(modulename));
return _wcsicmp(modulename, refmodulepath) == 0;
}
return false;
}
int GetFnOffsetInModule(voidFunc fnptr, wchar_t *refmodulepath, unsigned int refmodulepathLen, const std::string &logPrefix, const std::string &fnName) {
HMODULE hModule = NULL;
if (! GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (char *) fnptr, &hModule)) {
ods((logPrefix + ": Failed to get module for " + fnName).c_str());
return -1;
}
const bool bInit = refmodulepath[0] == '\0';
if (bInit) {
GetModuleFileNameW(hModule, refmodulepath, refmodulepathLen);
} else {
wchar_t modulename[MODULEFILEPATH_BUFLEN];
GetModuleFileNameW(hModule, modulename, ARRAY_NUM_ELEMENTS(modulename));
if (_wcsicmp(modulename, refmodulepath) != 0) {
ods((logPrefix + ": " + fnName + " functions module path does not match previously found. Now: '%ls', Previously: '%ls'").c_str(), modulename, refmodulepath);
return -2;
}
}
unsigned char *fn = reinterpret_cast<unsigned char *>(fnptr);
unsigned char *base = reinterpret_cast<unsigned char *>(hModule);
unsigned long off = static_cast<unsigned long>(fn - base);
// XXX: convert this function to use something other than int.
// Issue mumble-voip/mumble#1924.
if (off > static_cast<unsigned long>(std::numeric_limits<int>::max())) {
ods("Internal overlay error: GetFnOffsetInModule() offset greater than return type can hold.");
return -1;
}
return static_cast<int>(off);
}