// Copyright 2009-2021 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 . #include "lib.h" #include "overlay.hex" #include #include D3D10Data *d3d10 = nullptr; static bool bHooked = false; static HardHook hhAddRef; static HardHook hhRelease; typedef HRESULT(__stdcall *CreateDXGIFactoryType)(REFIID, void **); typedef HRESULT(__stdcall *D3D10CreateDeviceAndSwapChainType)(IDXGIAdapter *, D3D10_DRIVER_TYPE, HMODULE, UINT, UINT, DXGI_SWAP_CHAIN_DESC *, IDXGISwapChain **, ID3D10Device **); typedef HRESULT(__stdcall *D3D10CreateStateBlockType)(ID3D10Device *, D3D10_STATE_BLOCK_MASK *, ID3D10StateBlock **); typedef HRESULT(__stdcall *D3D10StateBlockMaskEnableAllType)(D3D10_STATE_BLOCK_MASK *); typedef HRESULT(__stdcall *D3D10CreateEffectFromMemoryType)(void *, SIZE_T, UINT, ID3D10Device *, ID3D10EffectPool *, ID3D10Effect **); typedef ULONG(__stdcall *AddRefType)(ID3D10Device *); typedef ULONG(__stdcall *ReleaseType)(ID3D10Device *); #define HMODREF(mod, func) func##Type p##func = (func##Type) GetProcAddress(mod, #func) struct SimpleVec3 { FLOAT x; FLOAT y; FLOAT z; SimpleVec3(FLOAT _x, FLOAT _y, FLOAT _z) : x(_x), y(_y), z(_z) {} }; struct SimpleVec2 { FLOAT x; FLOAT y; SimpleVec2(FLOAT _x, FLOAT _y) : x(_x), y(_y) {} }; struct SimpleVertex { SimpleVec3 Pos; SimpleVec2 Tex; }; class D10State : protected Pipe { public: ULONG lHighMark; LONG initRefCount; LONG refCount; LONG myRefCount; D3D10_VIEWPORT vp; ID3D10Device *pDevice; IDXGISwapChain *pSwapChain; ID3D10StateBlock *pOrigStateBlock; ID3D10StateBlock *pMyStateBlock; ID3D10RenderTargetView *pRTV; ID3D10Effect *pEffect; ID3D10EffectTechnique *pTechnique; ID3D10EffectShaderResourceVariable *pDiffuseTexture; ID3D10InputLayout *pVertexLayout; ID3D10Buffer *pVertexBuffer; ID3D10Buffer *pIndexBuffer; ID3D10BlendState *pBlendState; ID3D10Texture2D *pTexture; ID3D10ShaderResourceView *pSRView; clock_t timeT; unsigned int frameCount; D10State(IDXGISwapChain *, ID3D10Device *); virtual ~D10State(); bool init(); void draw(); virtual void blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h); virtual void setRect(); virtual void newTexture(unsigned int w, unsigned int h); }; typedef map< IDXGISwapChain *, D10State * > SwapchainMap; SwapchainMap chains; typedef map< ID3D10Device *, D10State * > DeviceMap; DeviceMap devices; D10State::D10State(IDXGISwapChain *pSwapChain, ID3D10Device *pDevice) { this->pSwapChain = pSwapChain; this->pDevice = pDevice; lHighMark = initRefCount = refCount = myRefCount = 0; ZeroMemory(&vp, sizeof(vp)); pOrigStateBlock = nullptr; pMyStateBlock = nullptr; pRTV = nullptr; pEffect = nullptr; pTechnique = nullptr; pDiffuseTexture = nullptr; pVertexLayout = nullptr; pVertexBuffer = nullptr; pIndexBuffer = nullptr; pBlendState = nullptr; pTexture = nullptr; pSRView = nullptr; timeT = clock(); frameCount = 0; pDevice->AddRef(); initRefCount = pDevice->Release(); } void D10State::blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h) { HRESULT hr; ods("D3D10: Blit %d %d %d %d", x, y, w, h); if (!pTexture || !pSRView || uiLeft == uiRight) return; D3D10_MAPPED_TEXTURE2D mappedTex; hr = pTexture->Map(D3D10CalcSubresource(0, 0, 1), D3D10_MAP_WRITE_DISCARD, 0, &mappedTex); if (FAILED(hr)) { ods("D3D10: Failed map"); } UCHAR *pTexels = (UCHAR *) mappedTex.pData; for (unsigned int r = 0; r < uiHeight; ++r) { unsigned char *sptr = a_ucTexture + r * uiWidth * 4; unsigned char *dptr = reinterpret_cast< unsigned char * >(pTexels) + r * mappedTex.RowPitch; memcpy(dptr, sptr, uiWidth * 4); } pTexture->Unmap(D3D10CalcSubresource(0, 0, 1)); } void D10State::setRect() { HRESULT hr; ods("D3D10: SetRect"); float w = static_cast< float >(uiWidth); float h = static_cast< float >(uiHeight); float left = static_cast< float >(uiLeft) - 0.5f; float top = static_cast< float >(uiTop) - 0.5f; float right = static_cast< float >(uiRight) + 0.5f; float bottom = static_cast< float >(uiBottom) + 0.5f; float texl = (left) / w; float text = (top) / h; float texr = (right + 1.0f) / w; float texb = (bottom + 1.0f) / h; left = 2.0f * (left / vp.Width) - 1.0f; right = 2.0f * (right / vp.Width) - 1.0f; top = -2.0f * (top / vp.Height) + 1.0f; bottom = -2.0f * (bottom / vp.Height) + 1.0f; ods("D3D10: Vertex (%f %f) (%f %f)", left, top, right, bottom); // Create vertex buffer SimpleVertex vertices[] = { { SimpleVec3(left, top, 0.5f), SimpleVec2(texl, text) }, { SimpleVec3(right, top, 0.5f), SimpleVec2(texr, text) }, { SimpleVec3(right, bottom, 0.5f), SimpleVec2(texr, texb) }, { SimpleVec3(left, bottom, 0.5f), SimpleVec2(texl, texb) }, }; void *pData = nullptr; hr = pVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData); memcpy(pData, vertices, sizeof(vertices)); ods("D3D10: Map %lx %d", hr, sizeof(vertices)); pVertexBuffer->Unmap(); } void D10State::newTexture(unsigned int w, unsigned int h) { HRESULT hr; ods("D3D10: newTexture %d %d", w, h); if (pTexture) { pTexture->Release(); pTexture = nullptr; } if (pSRView) { pSRView->Release(); pSRView = nullptr; } D3D10_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); desc.Width = w; desc.Height = h; desc.MipLevels = desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = D3D10_USAGE_DYNAMIC; desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; hr = pDevice->CreateTexture2D(&desc, nullptr, &pTexture); if (FAILED(hr)) { pTexture = nullptr; ods("D3D10: Failed to create texture."); return; } D3D10_SHADER_RESOURCE_VIEW_DESC srvDesc; ZeroMemory(&srvDesc, sizeof(srvDesc)); srvDesc.Format = desc.Format; srvDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Texture2D.MipLevels = desc.MipLevels; hr = pDevice->CreateShaderResourceView(pTexture, &srvDesc, &pSRView); if (FAILED(hr)) { pSRView = nullptr; pTexture->Release(); pTexture = nullptr; ods("D3D10: Failed to create resource view."); return; } } bool D10State::init() { static HMODREF(GetModuleHandleW(L"D3D10.DLL"), D3D10CreateEffectFromMemory); static HMODREF(GetModuleHandleW(L"D3D10.DLL"), D3D10CreateStateBlock); static HMODREF(GetModuleHandleW(L"D3D10.DLL"), D3D10StateBlockMaskEnableAll); if (!pD3D10CreateEffectFromMemory || !pD3D10CreateStateBlock || !pD3D10StateBlockMaskEnableAll) { ods("D3D10: Could not get handles for all required D3D10 state initialization functions"); return false; } HRESULT hr; D3D10_STATE_BLOCK_MASK StateBlockMask; ZeroMemory(&StateBlockMask, sizeof(StateBlockMask)); hr = pD3D10StateBlockMaskEnableAll(&StateBlockMask); if (FAILED(hr)) { ods("D3D10: D3D10StateBlockMaskEnableAll failed"); return false; } hr = pD3D10CreateStateBlock(pDevice, &StateBlockMask, &pOrigStateBlock); if (FAILED(hr)) { ods("D3D10: D3D10CreateStateBlock for pOrigStateBlock failed"); return false; } hr = pD3D10CreateStateBlock(pDevice, &StateBlockMask, &pMyStateBlock); if (FAILED(hr)) { ods("D3D10: D3D10CreateStateBlock for pMyStateBlock failed"); return false; } hr = pOrigStateBlock->Capture(); if (FAILED(hr)) { ods("D3D10: Failed to store original state block during init"); return false; } ID3D10Texture2D *pBackBuffer = nullptr; hr = pSwapChain->GetBuffer(0, __uuidof(*pBackBuffer), (LPVOID *) &pBackBuffer); if (FAILED(hr)) { ods("D3D10: pSwapChain->GetBuffer failure!"); return false; } pDevice->ClearState(); D3D10_TEXTURE2D_DESC backBufferSurfaceDesc; pBackBuffer->GetDesc(&backBufferSurfaceDesc); ZeroMemory(&vp, sizeof(vp)); vp.Width = backBufferSurfaceDesc.Width; vp.Height = backBufferSurfaceDesc.Height; vp.MinDepth = 0; vp.MaxDepth = 1; vp.TopLeftX = 0; vp.TopLeftY = 0; pDevice->RSSetViewports(1, &vp); hr = pDevice->CreateRenderTargetView(pBackBuffer, nullptr, &pRTV); if (FAILED(hr)) { ods("D3D10: pDevice->CreateRenderTargetView failed!"); return false; } pDevice->OMSetRenderTargets(1, &pRTV, nullptr); // Settings for an "over" operation. // https://en.wikipedia.org/w/index.php?title=Alpha_compositing&oldid=580659153#Description D3D10_BLEND_DESC blend; ZeroMemory(&blend, sizeof(blend)); blend.BlendEnable[0] = TRUE; blend.SrcBlend = D3D10_BLEND_ONE; blend.DestBlend = D3D10_BLEND_INV_SRC_ALPHA; blend.BlendOp = D3D10_BLEND_OP_ADD; blend.SrcBlendAlpha = D3D10_BLEND_ONE; blend.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; blend.BlendOpAlpha = D3D10_BLEND_OP_ADD; blend.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; hr = pDevice->CreateBlendState(&blend, &pBlendState); if (FAILED(hr)) { ods("D3D10: pDevice->CreateBlendState failed!"); return false; } pDevice->OMSetBlendState(pBlendState, nullptr, 0xffffffff); hr = pD3D10CreateEffectFromMemory((void *) g_main, sizeof(g_main), 0, pDevice, nullptr, &pEffect); if (FAILED(hr)) { ods("D3D10: D3D10CreateEffectFromMemory failed!"); return false; } pTechnique = pEffect->GetTechniqueByName("Render"); if (!pTechnique) { ods("D3D10: Could not get technique for name 'Render'"); return false; } pDiffuseTexture = pEffect->GetVariableByName("txDiffuse")->AsShaderResource(); if (!pDiffuseTexture) { ods("D3D10: Could not get variable by name 'txDiffuse'"); return false; } pTexture = nullptr; pSRView = nullptr; // Define the input layout D3D10_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = sizeof(layout) / sizeof(layout[0]); // Create the input layout D3D10_PASS_DESC PassDesc; hr = pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc); if (FAILED(hr)) { ods("D3D10: Couldn't get pass description for technique"); return false; } hr = pDevice->CreateInputLayout(layout, numElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &pVertexLayout); if (FAILED(hr)) { ods("D3D10: pDevice->CreateInputLayout failure!"); return false; } pDevice->IASetInputLayout(pVertexLayout); D3D10_BUFFER_DESC bd; ZeroMemory(&bd, sizeof(bd)); bd.Usage = D3D10_USAGE_DYNAMIC; bd.ByteWidth = sizeof(SimpleVertex) * 4; bd.BindFlags = D3D10_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; bd.MiscFlags = 0; hr = pDevice->CreateBuffer(&bd, nullptr, &pVertexBuffer); if (FAILED(hr)) { ods("D3D10: pDevice->CreateBuffer failure!"); return false; } DWORD indices[] = { 0, 1, 3, 1, 2, 3, }; bd.Usage = D3D10_USAGE_DEFAULT; bd.ByteWidth = sizeof(DWORD) * 6; bd.BindFlags = D3D10_BIND_INDEX_BUFFER; bd.CPUAccessFlags = 0; bd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA InitData; ZeroMemory(&InitData, sizeof(InitData)); InitData.pSysMem = indices; hr = pDevice->CreateBuffer(&bd, &InitData, &pIndexBuffer); if (FAILED(hr)) { ods("D3D10: pDevice->CreateBuffer failure!"); return false; } // Set index buffer pDevice->IASetIndexBuffer(pIndexBuffer, DXGI_FORMAT_R32_UINT, 0); // Set primitive topology pDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); hr = pMyStateBlock->Capture(); if (FAILED(hr)) { ods("D3D10: Failed to capture newly created state block"); return false; } hr = pOrigStateBlock->Apply(); if (FAILED(hr)) { ods("D3D10: Failed to restore original state block during init"); return false; } pBackBuffer->Release(); return true; } D10State::~D10State() { if (pBlendState) pBlendState->Release(); if (pVertexBuffer) pVertexBuffer->Release(); if (pIndexBuffer) pIndexBuffer->Release(); if (pVertexLayout) pVertexLayout->Release(); if (pEffect) pEffect->Release(); if (pRTV) pRTV->Release(); if (pTexture) pTexture->Release(); if (pSRView) pSRView->Release(); if (pMyStateBlock) { pMyStateBlock->ReleaseAllDeviceObjects(); pMyStateBlock->Release(); } if (pOrigStateBlock) { pOrigStateBlock->ReleaseAllDeviceObjects(); pOrigStateBlock->Release(); } } void D10State::draw() { clock_t t = clock(); float elapsed = static_cast< float >(t - timeT) / CLOCKS_PER_SEC; ++frameCount; if (elapsed > OVERLAY_FPS_INTERVAL) { OverlayMsg om; om.omh.uiMagic = OVERLAY_MAGIC_NUMBER; om.omh.uiType = OVERLAY_MSGTYPE_FPS; om.omh.iLength = sizeof(OverlayMsgFps); om.omf.fps = frameCount / elapsed; sendMessage(om); frameCount = 0; timeT = t; } checkMessage(vp.Width, vp.Height); if (a_ucTexture && pSRView && (uiLeft != uiRight)) { HRESULT hr; pOrigStateBlock->Capture(); pMyStateBlock->Apply(); D3D10_TECHNIQUE_DESC techDesc; pTechnique->GetDesc(&techDesc); // Set vertex buffer UINT stride = sizeof(SimpleVertex); UINT offset = 0; pDevice->IASetVertexBuffers(0, 1, &pVertexBuffer, &stride, &offset); hr = pDiffuseTexture->SetResource(pSRView); if (FAILED(hr)) ods("D3D10: Failed to set resource"); for (UINT p = 0; p < techDesc.Passes; ++p) { pTechnique->GetPassByIndex(p)->Apply(0); pDevice->DrawIndexed(6, 0, 0); } pOrigStateBlock->Apply(); } } // D3D10 specific logic for the Present function. void presentD3D10(IDXGISwapChain *pSwapChain) { ID3D10Device *pDevice = nullptr; HRESULT hr = pSwapChain->GetDevice(__uuidof(ID3D10Device), (void **) &pDevice); if (SUCCEEDED(hr) && pDevice) { SwapchainMap::iterator it = chains.find(pSwapChain); D10State *ds = it != chains.end() ? it->second : nullptr; if (ds && ds->pDevice != pDevice) { ods("D3D10: SwapChain device changed"); devices.erase(ds->pDevice); delete ds; ds = nullptr; } if (!ds) { ods("D3D10: New state"); ds = new D10State(pSwapChain, pDevice); if (!ds->init()) { pDevice->Release(); delete ds; return; } chains[pSwapChain] = ds; devices[pDevice] = ds; } ds->draw(); pDevice->Release(); } else { #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT // DXGI is used for multiple D3D versions. Thus, it is possible a device // associated with the DXGISwapChain may very well not be a D3D10 one, // in which case we can safely ignore it. ods("D3D10: Could not draw because ID3D10Device could not be retrieved."); #endif } } void resizeD3D10(IDXGISwapChain *pSwapChain) { // Remove the D10State from our "cache" (= Invalidate) SwapchainMap::iterator it = chains.find(pSwapChain); if (it != chains.end()) { D10State *ds = it->second; devices.erase(ds->pDevice); chains.erase(it); delete ds; } } static ULONG __stdcall myAddRef(ID3D10Device *pDevice) { // TODO: Move logic to HardHook. // Call base without active hook in case of no trampoline. AddRefType oAddRef = (AddRefType) hhAddRef.call; hhAddRef.restore(); ULONG res = oAddRef(pDevice); hhAddRef.inject(); Mutex m; DeviceMap::iterator it = devices.find(pDevice); if (it != devices.end()) { it->second->lHighMark = res; } return res; } static ULONG __stdcall myRelease(ID3D10Device *pDevice) { // TODO: Move logic to HardHook. // Call base without active hook in case of no trampoline. ReleaseType oRelease = (ReleaseType) hhRelease.call; hhRelease.restore(); ULONG res = oRelease(pDevice); hhRelease.inject(); Mutex m; DeviceMap::iterator it = devices.find(pDevice); if (it != devices.end()) { D10State *ds = it->second; // If we are receiving a lot of subsequent releases lets eagerly drop // our state object. If the device presents something again a state // object is created again just fine anyway. if (res < ds->lHighMark / 2) { ods("D3D10: Deleting resources %u < 0.5 * %u", res, ds->lHighMark); devices.erase(it); chains.erase(ds->pSwapChain); delete ds; ods("D3D10: Deleted"); ds = nullptr; } } return res; } static void HookAddRelease(voidFunc vfAdd, voidFunc vfRelease) { ods("D3D10: Injecting device add/remove"); hhAddRef.setup(vfAdd, reinterpret_cast< voidFunc >(myAddRef)); hhRelease.setup(vfRelease, reinterpret_cast< voidFunc >(myRelease)); } static void hookD3D10(HMODULE hD3D10, bool preonly); void checkDXGI10Hook(bool preonly) { static bool bCheckHookActive = false; if (bCheckHookActive) { ods("D3D10: Recursion in checkDXGI10Hook"); return; } if (d3d10->offsetAddRef == 0 || d3d10->offsetRelease == 0) { return; } bCheckHookActive = true; HMODULE hD3D10 = GetModuleHandleW(L"D3D10CORE.DLL"); if (hD3D10) { if (!bHooked) { hookD3D10(hD3D10, preonly); } } else { #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT if (hDXGI) { ods("D3D10: No DXGI.DLL found as loaded. No hooking at this point."); } else { ods("D3D10: No D3D10CORE.DLL found as loaded. No hooking at this point."); } #endif } bCheckHookActive = false; } /// @param hD3D10 must be a valid module handle void hookD3D10(HMODULE hD3D10, bool preonly) { // Add a ref to ourselves; we do NOT want to get unloaded directly from this process. HMODULE hTempSelf = nullptr; GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast< LPCTSTR >(&hookD3D10), &hTempSelf); bHooked = true; wchar_t modulename[MODULEFILEPATH_BUFLEN]; GetModuleFileNameW(hD3D10, modulename, ARRAY_NUM_ELEMENTS(modulename)); if (_wcsicmp(d3d10->wcFileName, modulename) == 0) { unsigned char *raw = (unsigned char *) hD3D10; HookAddRelease((voidFunc)(raw + d3d10->offsetAddRef), (voidFunc)(raw + d3d10->offsetRelease)); } else if (!preonly) { ods("D3D10: Interface changed, can't rawpatch. Current: %ls ; Previously: %ls", modulename, d3d10->wcFileName); } else { bHooked = false; } } /// Prepares DXGI and D3D10 data by trying to determining the module filepath /// and function offsets in memory. /// (This data can later be used for hooking / code injection. /// /// Adjusts the data behind the global variables dxgi and d3d10. void PrepareDXGI10(IDXGIAdapter1 *pAdapter, bool initializeDXGIData) { if (!dxgi || !d3d10 || !pAdapter) return; ods("D3D10: Preparing static data for DXGI and D3D10 Injection"); d3d10->wcFileName[0] = 0; d3d10->offsetAddRef = 0; d3d10->offsetRelease = 0; HMODULE hD3D10 = LoadLibrary("D3D10.DLL"); if (hD3D10) { HWND hwnd = CreateWindowW(L"STATIC", L"Mumble DXGI Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, 0, nullptr, nullptr, 0); D3D10CreateDeviceAndSwapChainType pD3D10CreateDeviceAndSwapChain = reinterpret_cast< D3D10CreateDeviceAndSwapChainType >( GetProcAddress(hD3D10, "D3D10CreateDeviceAndSwapChain")); DXGI_SWAP_CHAIN_DESC desc; ZeroMemory(&desc, sizeof(desc)); RECT rcWnd; GetClientRect(hwnd, &rcWnd); desc.BufferDesc.Width = rcWnd.right - rcWnd.left; desc.BufferDesc.Height = rcWnd.bottom - rcWnd.top; ods("D3D10: Got ClientRect W %d H %d", desc.BufferDesc.Width, desc.BufferDesc.Height); desc.BufferDesc.RefreshRate.Numerator = 60; desc.BufferDesc.RefreshRate.Denominator = 1; desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; desc.BufferDesc.Scaling = DXGI_MODE_SCALING_CENTERED; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount = 2; desc.OutputWindow = hwnd; desc.Windowed = true; desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; IDXGISwapChain *pSwapChain = nullptr; ID3D10Device *pDevice = nullptr; HRESULT hr = pD3D10CreateDeviceAndSwapChain(pAdapter, D3D10_DRIVER_TYPE_HARDWARE, nullptr, 0, D3D10_SDK_VERSION, &desc, &pSwapChain, &pDevice); if (FAILED(hr)) ods("D3D10: pD3D10CreateDeviceAndSwapChain failure!"); if (pDevice && pSwapChain) { // For VC++ the vtable is located at the base addr. of the object and each function entry is a single // pointer. Since p.e. the base classes of IDXGISwapChain have a total of 8 functions the 8+Xth entry points // to the Xth added function in the derived interface. void ***vtbl = (void ***) pSwapChain; void *pPresent = (*vtbl)[8]; boost::optional< size_t > offset = GetFnOffsetInModule(reinterpret_cast< voidFunc >(pPresent), dxgi->wcFileName, ARRAY_NUM_ELEMENTS(dxgi->wcFileName), "D3D10", "Present"); if (offset) { if (initializeDXGIData) { dxgi->offsetPresent = *offset; ods("D3D10: Successfully found Present offset: %ls: %d", dxgi->wcFileName, dxgi->offsetPresent); } else { if (dxgi->offsetPresent == *offset) { ods("D3D10: Successfully verified Present offset: %ls: %d", dxgi->wcFileName, dxgi->offsetPresent); } else { ods("D3D10: Failed to verify Present offset for %ls. Found %d, but previously found %d.", dxgi->wcFileName, offset, dxgi->offsetPresent); } } } void *pResize = (*vtbl)[13]; offset = GetFnOffsetInModule(reinterpret_cast< voidFunc >(pResize), dxgi->wcFileName, ARRAY_NUM_ELEMENTS(dxgi->wcFileName), "D3D10", "ResizeBuffers"); if (offset) { if (initializeDXGIData) { dxgi->offsetResize = *offset; ods("D3D10: Successfully found ResizeBuffers offset: %ls: %d", dxgi->wcFileName, dxgi->offsetResize); } else { if (dxgi->offsetResize == *offset) { ods("D3D10: Successfully verified ResizeBuffers offset: %ls: %d", dxgi->wcFileName, dxgi->offsetResize); } else { ods("D3D10: Failed to verify ResizeBuffers offset for %ls. Found %d, but previously found %d.", dxgi->wcFileName, offset, dxgi->offsetResize); } } } vtbl = (void ***) pDevice; void *pAddRef = (*vtbl)[1]; offset = GetFnOffsetInModule(reinterpret_cast< voidFunc >(pAddRef), d3d10->wcFileName, ARRAY_NUM_ELEMENTS(d3d10->wcFileName), "D3D10", "AddRef"); if (offset) { d3d10->offsetAddRef = *offset; ods("D3D10: Successfully found AddRef offset: %ls: %d", d3d10->wcFileName, d3d10->offsetAddRef); } void *pRelease = (*vtbl)[2]; offset = GetFnOffsetInModule(reinterpret_cast< voidFunc >(pRelease), d3d10->wcFileName, ARRAY_NUM_ELEMENTS(d3d10->wcFileName), "D3D10", "Release"); if (offset) { d3d10->offsetRelease = *offset; ods("D3D10: Successfully found Release offset: %ls: %d", d3d10->wcFileName, d3d10->offsetRelease); } } if (pDevice) pDevice->Release(); if (pSwapChain) pSwapChain->Release(); DestroyWindow(hwnd); } else { FreeLibrary(hD3D10); } }