Video Window does not respond, windowed mode, DirectShow - c++

I wanted to use Vmr 9 instead of default Vmr 7, so I created it with CoCreateInstance and added to the graph with AddFilter method. Next, I build graph with RenderFile method. Now renderer is connected, so I query for IVideoWindow interface and call put_Owner method with window handle created in child thread. When Run method is called, video is displayed in the window, but window doesn't handle messages, so you can't move it or resize. Windowless mode works fine. Why window behaves this way?
#include <dshow.h>
#include <D3D9.h>
#include <Vmr9.h>
#pragma comment(lib, "Strmiids.lib")
#pragma comment(lib, "D3d9.lib")
#define WM_GRAPHNOTIFY WM_APP + 1
DWORD HandleGraphEvent(IMediaEventEx* media_event)
{
long EvCode;
LONG_PTR param1, param2;
while (media_event->GetEvent(&EvCode, &param1, &param2, 0) == S_OK)
{
media_event->FreeEventParams(EvCode, param1, param2);
switch (EvCode)
{
case EC_COMPLETE:
printf("All data is rendered\n");
return 0;
case EC_USERABORT:
printf("User has closed the window\n");
return 0;
case EC_ERRORABORT:
printf("Error occured\n");
return 0;
}
}
return 1;
}
struct WindowThreadParam
{
HANDLE event;
HWND window;
IMediaEventEx* media_event;
};
LRESULT WINAPI WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
{
WindowThreadParam* param = (WindowThreadParam*)lp;
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_GRAPHNOTIFY:
HandleGraphEvent(param->media_event);
default:
return DefWindowProc(window, msg, wp, lp);
}
}
DWORD WINAPI WindowThread(WindowThreadParam* param)
{
LPCWSTR myclass = TEXT("myclass");
WNDCLASSEX wndclass =
{ sizeof(WNDCLASSEX), CS_DBLCLKS, WindowProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0, IDI_APPLICATION),
LoadCursor(0, IDC_ARROW), HBRUSH(COLOR_WINDOW + 1),
0, myclass, LoadIcon(0, IDI_APPLICATION) };
HWND window = NULL;
if (RegisterClassEx(&wndclass))
{
window = CreateWindowEx(0, myclass, TEXT("title"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0);
if (window)
{
ShowWindow(window, SW_SHOWDEFAULT);
param->window = window;
SetEvent(param->event);
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) DispatchMessage(&msg);
}
else SetEvent(param->event);
UnregisterClass(myclass, GetModuleHandle(0));
}
else SetEvent(param->event);
return 0;
}
void _tmain(int argc, _TCHAR* argv[])
{
HANDLE event_handle;
event_handle = CreateEvent(NULL, 0, 0, NULL); // auto, unsignaled
WindowThreadParam param;
ZeroMemory(&param, sizeof(param));
param.event = event_handle;
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WindowThread, &param, 0, NULL);
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
printf("ERROR - Could not initialize COM library");
return;
}
IGraphBuilder* graph_builder;
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&graph_builder);
IBaseFilter* Vrm;
hr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&Vrm);
hr = graph_builder->AddFilter(Vrm, L"Video Mixing Renderer");
hr = graph_builder->RenderFile(L"anim.avi", NULL);
hr = graph_builder->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = graph_builder->QueryInterface(IID_IMediaEventEx, (void **)&pEvent);
IVideoWindow* VideoWindow;
RECT window_client_area;
WaitForSingleObject(param.event, INFINITE);
if (param.window)
{
// set window
hr = graph_builder->QueryInterface(IID_IVideoWindow, (void**)&VideoWindow);
hr = VideoWindow->put_Owner((OAHWND)param.window);
hr = VideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
// set video position
GetClientRect(param.window, &window_client_area);
hr = VideoWindow->SetWindowPosition(0, 0, window_client_area.right, window_client_area.bottom);
param.media_event = pEvent;
hr = pEvent->SetNotifyWindow((OAHWND)param.window, WM_GRAPHNOTIFY, (LONG_PTR)&param);
}
hr = pControl->Run();
// allow child thread to run
getchar();
pControl->Release();
pEvent->Release();
graph_builder->Release();
CoUninitialize();
}

Your main thread sleeps in getchar(); when it is supposed to implement message pump and deliver window messages, both as COM requirement and for the helper window VMR creates in the main thread.
Your background thread is irrelevant here since your controlling DirectShow activity takes place in _tmain.

Related

Access violation executing location xxxxxxxx "when injecting into a process"

Hello I'm new to learning process injection, and having some trouble injecting a process with C++. I'm using CreateRemoteThread and WriteProcessMemory method. However I get Access violation executing location xxxx. the program breaks in my injected function with the said error. i checked through the disassembler and see a call that go to an invalid address, but i have no idea what's going on. i'm working with VS2019 on win10 64 bit, but the project build is x86. i've attached the screen shot of disassembler and error box, also below is my code. i will appreciate any assistant and help. thanks.
disassembly screen
disassembly screen_2
stack frame
the error
#include "stdafx.h"
//Declearations and structure defination
#if !defined INJCODE_H
#define INJCODE_H
int GetWindowTextRemoteA(HANDLE hProcess, HWND hWnd, LPSTR lpString);
int GetWindowTextRemoteW(HANDLE hProcess, HWND hWnd, LPWSTR lpString);
#ifdef UNICODE
#define GetWindowTextRemote GetWindowTextRemoteW
#else
#define GetWindowTextRemote GetWindowTextRemoteA
#endif // !UNICODE
#endif // !defined(INJCODE_H)
typedef LRESULT(WINAPI* SENDMESSAGE)(HWND, UINT, WPARAM, LPARAM);
typedef struct {
HWND hwnd;
SENDMESSAGE fnSendMessage; // pointer to user32!SendMessage
} INJDATA, * PINJDATA;
//---------------------------------------------------------------------
// ThreadFunc
// Notice: - the code being injected;
//it breaks in this function
static DWORD WINAPI ThreadFunc(INJDATA* pData)
{
int nXferred = 0; // number of chars retrieved by WM_GETTEXT
pData->fnSendMessage(pData->hwnd, WM_CLOSE, 0, 0);
return nXferred;
}
//---------------------------------------------------------------------
//Process injection routine
int GetTextRemote(HANDLE hProcess, HWND hWnd, BYTE* pbString, bool fUnicode)
{
HINSTANCE hUser32;
INJDATA* pDataRemote = 0; // the address (in the remote process) where INJDATA will be copied to;
DWORD* pCodeRemote = 0; // the address (in the remote process) where ThreadFunc will be copied to;
HANDLE hThread = NULL; // the handle to the thread executing the remote copy of ThreadFunc;
DWORD dwThreadId = 0;
int nCharsXferred = 0; // number of chars retrieved by WM_GETTEXT in the remote thread;
DWORD dwNumBytesXferred = 0; // number of bytes written/read to/from the remote process;
__try {
hUser32 = GetModuleHandle(__TEXT("user32"));
if (hUser32 == NULL)
__leave;
// Initialize INJDATA and then
// copy it to the remote process
INJDATA DataLocal = {
hWnd,
(SENDMESSAGE)GetProcAddress(hUser32, fUnicode ? "SendMessageW" : "SendMessageA")
};
if (DataLocal.fnSendMessage == NULL)
__leave;
// 1. Allocate memory in the remote process for INJDATA
// 2. Write a copy of DataLocal to the allocated memory
pDataRemote = (INJDATA*)VirtualAllocEx(hProcess, 0, sizeof(INJDATA), MEM_COMMIT, PAGE_READWRITE);
if (pDataRemote == NULL)
__leave;
//WriteProcessMemory( hProcess, pDataRemote, &DataLocal, sizeof(INJDATA), &dwNumBytesXferred );
if (WriteProcessMemory(hProcess, pDataRemote, &DataLocal, sizeof(INJDATA), &dwNumBytesXferred) == NULL) {
__leave;
}
// Calculate the number of bytes that ThreadFunc occupies
//const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc);
const int cbCodeSize = 0x5d; // derectly used
pCodeRemote = (PDWORD)VirtualAllocEx(hProcess, 0, cbCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pCodeRemote == NULL)
__leave;
if (WriteProcessMemory(hProcess, pCodeRemote, &ThreadFunc, cbCodeSize, &dwNumBytesXferred) == NULL) {
__leave;
}
// Start execution of remote ThreadFunc
hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pCodeRemote,
pDataRemote, 0, &dwThreadId);
if (hThread == NULL)
__leave;
WaitForSingleObject(hThread, INFINITE);
}
__finally {
if (pDataRemote != 0)
VirtualFreeEx(hProcess, pDataRemote, 0, MEM_RELEASE);
if (pCodeRemote != 0)
VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE);
if (hThread != NULL) {
GetExitCodeThread(hThread, (PDWORD)&nCharsXferred);
CloseHandle(hThread);
}
}
return nCharsXferred;
}
//---------------------------------------------------------------------
//winProc to create win32 process
#define ID_TIMER 1
int GetWindowTextRemoteA(HANDLE hProcess, HWND hWnd, LPSTR lpString)
{
return GetTextRemote(hProcess, hWnd, (BYTE*)lpString, false);
}
int GetWindowTextRemoteW(HANDLE hProcess, HWND hWnd, LPWSTR lpString)
{
return GetTextRemote(hProcess, hWnd, (BYTE*)lpString, true);
}
TCHAR szAppName[] = TEXT("MenuDemo");
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int idColor[5] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH, DKGRAY_BRUSH, BLACK_BRUSH };
static int iSelection = IDM_BKGND_WHITE;
HMENU hMenu;
switch (message)
{
case WM_COMMAND:
hMenu = GetMenu(hwnd);
switch (LOWORD(wParam))
{
case IDM_FILE_NEW:
_TCHAR ch[128];
DWORD PID, TID;
TID = ::GetWindowThreadProcessId(hwnd, &PID);
if (PID) {
MessageBeep(MB_OK);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
GetWindowTextRemote(hProcess, hwnd, ch);
}
case IDM_FILE_OPEN:
case IDM_FILE_SAVE:
case IDM_FILE_SAVE_AS:
MessageBeep(0);
return 0;
case IDM_APP_EXIT:
SendMessage(hwnd, WM_CLOSE, 0, 0);
return 0;
case IDM_EDIT_UNDO:
case IDM_EDIT_CUT:
case IDM_EDIT_COPY:
case IDM_EDIT_PASTE:
case IDM_EDIT_CLEAR:
MessageBeep(0);
return 0;
case IDM_BKGND_WHITE:
case IDM_BKGND_LTGRAY:
case IDM_BKGND_GRAY:
case IDM_BKGND_DKGRAY:
case IDM_BKGND_BLACK:
CheckMenuItem(hMenu, iSelection, MF_UNCHECKED);
iSelection = LOWORD(wParam);
CheckMenuItem(hMenu, iSelection, MF_CHECKED);
SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject
(idColor[LOWORD(wParam) - IDM_BKGND_WHITE]));
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case IDM_TIMER_START:
if (SetTimer(hwnd, ID_TIMER, 1000, NULL))
{
EnableMenuItem(hMenu, IDM_TIMER_START, MF_GRAYED);
EnableMenuItem(hMenu, IDM_TIMER_STOP, MF_ENABLED);
}
return 0;
case IDM_TIMER_STOP:
KillTimer(hwnd, ID_TIMER);
EnableMenuItem(hMenu, IDM_TIMER_START, MF_ENABLED);
EnableMenuItem(hMenu, IDM_TIMER_STOP, MF_GRAYED);
return 0;
case IDM_APP_HELP:
MessageBox(hwnd, TEXT("Help not yet implemented!"), szAppName, MB_ICONEXCLAMATION | MB_OK);
return 0;
case IDM_APP_ABOUT:
MessageBox(hwnd, TEXT("Menu Demonstration Program\n") TEXT("(c) Charles Petzold, 1998"),
szAppName, MB_ICONINFORMATION | MB_OK);
return 0;
}
break;
case WM_TIMER:
MessageBeep(0);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
//---------------------------------------------------------------------
//main window procedure
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = szAppName;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName,
TEXT("Menu Demonstration"),
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

Play FLAC via DirectShow

I would like write an simple (only one exe) music player.
The player is work if I want play mp3, but didn't work if I want use MadFLAC Filter.
I can't connect of the MadFLAC pins. Always drop 0x80040207 error.
(I think, the MadFLAC filter is loaded correctly, I can add to the GraphBuilder without error and see in the EnumFilters.)
I do not insist on using MadFLAC. All other options is interest me with which I can play a FLAC file via DirectShow without install codec pack to client pc.
Here is my simplified code:
#include <windows.h>
#include <strmif.h>
#include <control.h>
#include <uuids.h>
#pragma comment(lib, "strmiids.lib")
IPin* GetPin(IBaseFilter *bFilter, PIN_DIRECTION pindir)
{
IEnumPins *EnumPin;
bFilter->EnumPins(&EnumPin);
unsigned long int num;
IPin *TempPin = NULL;
do {
EnumPin->Next(1, &TempPin, &num);
if (num != 0)
{
PIN_INFO PinInfo;
TempPin->QueryPinInfo(&PinInfo);
if (PinInfo.dir == pindir) break;
}
} while (num != 0);
return TempPin;
}
typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid, REFIID riid, void **ppv);
HRESULT CreateFilterFromFile(HINSTANCE hLibInst, GUID TGUID, void **Filter) {
IClassFactory * ClassFactory;
HRESULT Result = S_FALSE;
FARPROC func;
func = GetProcAddress(hLibInst, "DllGetClassObject");
if (func != NULL)
{
IClassFactory *classFactory;
DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)func;
Result = dllGetClassObject(TGUID, IID_IClassFactory, (void**)&classFactory);
if (SUCCEEDED(Result))
{
Result = classFactory->CreateInstance(NULL, IID_IBaseFilter, Filter);
classFactory->Release();
}
}
return Result;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void Player(bool isFlac) {
IGraphBuilder * player;
IMediaControl * mcontrol;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&player);
player->QueryInterface(IID_IMediaControl, (void **)&mcontrol);
IBaseFilter *ARS = NULL;
HRESULT hr = CoCreateInstance(CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&ARS);
IBaseFilter * AudioDecoderFilter = NULL;
if (isFlac) {
hr = player->AddSourceFilter(L"D:\\test.flac", L"Async Reader Source", &ARS);
GUID CLSID_MadFlacAudioDecoder;
CLSIDFromString(L"{6B257121-CBB6-46B3-ABFA-B14DFA98C4A6}", &CLSID_MadFlacAudioDecoder);
HINSTANCE FHMPCAudioFilterInst = CoLoadLibrary(L"d:\\_MadFlac\\madFlac.ax", true);
if (FHMPCAudioFilterInst != 0) {
hr = CreateFilterFromFile(FHMPCAudioFilterInst, CLSID_MadFlacAudioDecoder, (void **)&AudioDecoderFilter);
if (AudioDecoderFilter != NULL) {
player->AddFilter(AudioDecoderFilter, L"MadFLAC Audio decoder (internal)");
}
}
}
else {
hr = player->AddSourceFilter(L"D:\\test.mp3", L"Async Reader Source", &ARS);
hr = CoCreateInstance(CLSID_MPEG1Splitter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&AudioDecoderFilter);
hr = player->AddFilter(AudioDecoderFilter, L"MPEG1Splitter");
}
IBaseFilter *PCM = NULL;
hr = CoCreateInstance(CLSID_ACMWrapper, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&PCM);
hr = player->AddFilter(PCM, L"PCM");
IBaseFilter *DSE = NULL;
hr = CoCreateInstance(CLSID_DSoundRender, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&DSE);
hr = player->AddFilter(DSE, L"Direct Renderer");
//ARS -> AdioDecoder
hr = GetPin(ARS, PINDIR_OUTPUT)->Connect(GetPin(AudioDecoderFilter, PINDIR_INPUT), NULL);
//If I use MadFLAC filter this hresult is 0x80040207
if (FAILED(hr)) MessageBoxA(NULL, "There is no common media type between these pins.", "Error", NULL);
//AudioDecoderFilter -> PCM
hr = GetPin(AudioDecoderFilter, PINDIR_OUTPUT)->Connect(GetPin(PCM, PINDIR_INPUT), NULL);
//PCM -> DSE
hr = GetPin(PCM, PINDIR_OUTPUT)->Connect(GetPin(DSE, PINDIR_INPUT), NULL);
//ListGraph(player);
mcontrol->Run();
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "Teszt";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) { MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; }
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "Teszt", "AppName", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, 460, 240, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) { MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; }
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
Player(false);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
I use VS2015, native C++.
I would have tried to use LibFLAC as well, but I was already stuck there that I couldn’t Build because it threw hundreds of bugs, which I eventually reduced to less than 20 but I had already given up there after several hours of suffering.
https://github.com/xiph/flac
https://ftp.osuosl.org/pub/xiph/releases/oggdsf/
Windows is not shipped with FLAC decoder for DirectShow. Hence, ability to play FLAC audio via DirectShow requires a third party decoder. If you don't want to reply on a codec pack, you need to either bundle certain FLAC decoder with DirectShow interface or implement a decoder in code. https://xiph.org/dshow looks like the best third party decoder option to me, implementing a DirectShow filter over FLAC library requires some development effort, which is probably an overkill for the task in question.

How can I draw correctly in a window with Direct2D?

I was making a program that draws two lines and a circle in the window, for this I used Direct2D.
I was doing a program that draws two lines and a circle in the window. I noticed a problem and that is that when drawing a line of coordinates (100,200), (200,200), that line is not drawn in the pixels (100,200) and ( 200,200). I checked this by reading the coordinates of the mouse in the window and seeing if the line actually has the coordinates that I mentioned.
Do a little more research, and according to Microsoft, direct2d works with dpi and not physical pixels.
So is there a way to work directly with pixels like in Processing or Win32 where the position of the controls is expressed in physical pixels of the window.
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d3d11_1.h>
#include <dxgi1_6.h>
#include <string>
#pragma comment(lib,"d3d11")
#pragma comment(lib,"d2d1")
#pragma comment(lib,"dxgi")
#pragma warning(disable : 4996)
using namespace D2D1;
using namespace std;
template<typename Interface> void SafeRelease(Interface**);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInst
, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInst);
UNREFERENCED_PARAMETER(lpCmdLine);
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINLOGO));
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = L"Demo_Wnd_Class";
wc.lpszMenuName = NULL;
wc.style = 0;
RegisterClass(&wc);
DWORD dwTopLevelWndStyle = WS_OVERLAPPEDWINDOW ^ (WS_THICKFRAME | WS_MAXIMIZEBOX);
RECT clientRc = { 0,0,640,480 };
AdjustWindowRect(&clientRc, dwTopLevelWndStyle, FALSE);
HWND hWnd = CreateWindow(L"Demo_Wnd_Class", L"Direct2DDemo", dwTopLevelWndStyle, CW_USEDEFAULT, CW_USEDEFAULT
, clientRc.right - clientRc.left, clientRc.bottom - clientRc.top, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, SW_SHOWDEFAULT);
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
ID3D11Device* d3dDevice;
IDXGIDevice* dxgiDevice = NULL;
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1,
};
HRESULT hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featureLevels
, 7, D3D11_SDK_VERSION, &d3dDevice, NULL, NULL);
if (SUCCEEDED(hr)) {
OutputDebugString(L"D3D11 Device created successful\n");
hr = d3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
}
ID2D1Device6* d2dDevice = NULL;
ID2D1DeviceContext6* d2dDeviceContext = NULL;
IDXGIFactory2* dxgiFactory = NULL;
IDXGISwapChain1* dxgiSwapChain = NULL;
IDXGISurface *dxgiBackBufferSurface = NULL; // Back buffer
ID2D1Bitmap1* bmpTarget = NULL;
ID2D1SolidColorBrush* shapeBr = NULL;
DXGI_SWAP_CHAIN_DESC1 dscd;
dscd.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
dscd.BufferCount = 2;
dscd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
dscd.Flags = 0;
dscd.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
dscd.Height = 480;
dscd.SampleDesc.Count = 1;
dscd.SampleDesc.Quality = 0;
dscd.Scaling = DXGI_SCALING_NONE;
dscd.Stereo = FALSE;
dscd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
dscd.Width = 640;
if (SUCCEEDED(hr)) {
OutputDebugString(L"DXGI Device created successful\n");
hr = D2D1CreateDevice(dxgiDevice, CreationProperties(D2D1_THREADING_MODE_SINGLE_THREADED, D2D1_DEBUG_LEVEL_NONE, D2D1_DEVICE_CONTEXT_OPTIONS_NONE), (ID2D1Device**)&d2dDevice);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"D2D Device created successful\n");
hr = d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, (ID2D1DeviceContext**)&d2dDeviceContext);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"D2D Device Context created successful\n");
hr = CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, __uuidof(IDXGIFactory2), (void**)&dxgiFactory);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"DXGI Factory created successful\n");
hr = dxgiFactory->CreateSwapChainForHwnd(d3dDevice, hWnd, &dscd, NULL, NULL, &dxgiSwapChain);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"DXGI Swap Chain created successful\n");
hr = dxgiSwapChain->GetBuffer(0, __uuidof(IDXGISurface), (void**)&dxgiBackBufferSurface);
}
if (SUCCEEDED(hr)) {
hr = d2dDeviceContext->CreateBitmapFromDxgiSurface(dxgiBackBufferSurface, BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),0,0), &bmpTarget);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"D2D Bitmap created successful\n");
d2dDeviceContext->SetTarget(bmpTarget);
hr = d2dDeviceContext->CreateSolidColorBrush(ColorF(ColorF::White), &shapeBr);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"D2D Solid Color Brush created successful\n");
d2dDeviceContext->SetUnitMode(D2D1_UNIT_MODE_PIXELS);
d2dDeviceContext->SetTransform(Matrix3x2F::Identity());
d2dDeviceContext->SetAntialiasMode(D2D1_ANTIALIAS_MODE::D2D1_ANTIALIAS_MODE_ALIASED);
}
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
d2dDeviceContext->BeginDraw();
d2dDeviceContext->Clear(ColorF(ColorF::ForestGreen));
d2dDeviceContext->DrawLine(Point2F(400, 0), Point2F(400, 400), shapeBr);
d2dDeviceContext->DrawLine(Point2F(200.0f, 320.0f), Point2F(400.0f, 320.0f), shapeBr);
d2dDeviceContext->DrawEllipse(D2D1::Ellipse(Point2F(300.0f, 300.0f), 10.0f, 10.0f), shapeBr);
POINT cursorPos;
GetCursorPos(&cursorPos);
ScreenToClient(hWnd,&cursorPos);
wstring dbgCursorPos = wstring(L"(MouseX: " + to_wstring(cursorPos.x) + L" MouseY: " + to_wstring(cursorPos.y) + L")\n");
OutputDebugString(dbgCursorPos.c_str());
d2dDeviceContext->EndDraw();
dxgiSwapChain->Present(1, 0);
}
}
SafeRelease(&d3dDevice);
SafeRelease(&dxgiBackBufferSurface);
SafeRelease(&dxgiDevice);
SafeRelease(&dxgiFactory);
SafeRelease(&dxgiSwapChain);
SafeRelease(&d2dDevice);
SafeRelease(&d2dDeviceContext);
SafeRelease(&bmpTarget);
SafeRelease(&shapeBr);
return 0;
}
template<typename Interface> void SafeRelease(Interface** ppInterface) {
if (*ppInterface) {
(*ppInterface)->Release();
(*ppInterface) = NULL;
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
if (Msg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}

How do I make TTS stop speaking IMMEDIATELY?

I am using ISpVoice to speak an input string. Now, even though I use SPF_ASYNC and SPF_PURGEBEFORESPEAK tags in the Speak method, the tts doesn't stop whenever Pause is called instead it continues until the tts finishes a word.
Here's how I do it:
void speakSentence()
{
pVoice->Pause();
pVoice->Speak(L"This is a sentence.", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
pVoice->Resume();
}
Whenever I try to call this function at the middle of the word "sentence", the tts doesn't pause and instead continues to say the word until the end.
From microsoft documentation:
ISpVoice::Pause pauses the voice at the nearest alert boundary and closes the output device, allowing access to pending speak requests from other voices.
I tried changing the alert boundary by:
pVoice->SetAlertBoundary(SPEI_PHONEME);
and it doesn't work.
There is NVDA Screen Reader that solved this problem but I don't know how they did it.
Is there anyway to solve my problem?
EDIT:
Here's my full code. I am creating a small screen reader program that uses both UIAutomation and MSAA.
The program may somewhat unstable when comparing UI objects but most times it works.
screeenreader.h:
#ifndef _SCREENREADER_H_
#define _SCREENREADER_H_
#define WIN32_LEAN_AND_MEAN
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <memory>
#include "speechsynthesis.h"
#include "uiautomator.h"
class ScreenReader
{
public:
explicit ScreenReader(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdShow);
virtual ~ScreenReader();
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int Exec();
private:
void InitializeWindows();
void InitRawInputDevices();
bool IsMouseMove();
private:
LPCWSTR m_applicationName;
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance;
PSTR m_pScmdline;
int m_iCmdShow;
HWND m_hWnd;
SpeechSynthesis *m_pSpeech;
UIAutomator *m_pAutomator;
RAWINPUTDEVICE rid[2];
LONG m_prevMouseX;
LONG m_prevMouseY;
BSTR currItem;
};
static ScreenReader *application;
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
#endif
screenreader.cpp: In this part I called ISpVoice at the messageloop section. At ScreenReader::MessageHandler() function at IsMouseMove condition.
#include "screenreader.h"
ScreenReader::ScreenReader(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdShow)
{
CoInitialize(NULL);
m_pSpeech = new SpeechSynthesis;
m_pAutomator = new UIAutomator;
// Get current Cursor position.
POINT pt;
GetCursorPos(&pt);
m_prevMouseX = pt.x;
m_prevMouseY = pt.y;
// Notify user the program is loading.
m_pSpeech->Speak(L"Loading Rescan. Please wait.", SPF_DEFAULT, NULL);
m_hInstance = hInstance;
m_hPrevInstance = hPrevInstance;
m_pScmdline = pScmdline;
m_iCmdShow = iCmdShow;
application = this;
InitializeWindows();
InitRawInputDevices();
}
ScreenReader::~ScreenReader()
{
if (m_pSpeech != nullptr)
{
delete m_pSpeech;
m_pSpeech = nullptr;
}
if (m_pAutomator != nullptr)
{
delete m_pAutomator;
m_pAutomator = nullptr;
}
if (currItem != NULL)
{
SysFreeString(currItem);
currItem = NULL;
}
CoUninitialize();
}
LRESULT CALLBACK ScreenReader::MessageHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INPUT:
{
UINT dwSize;
GetRawInputData(
(HRAWINPUT)lParam,
RID_INPUT,
NULL,
&dwSize,
sizeof(RAWINPUTHEADER)
);
std::unique_ptr<BYTE[]> lpb(new BYTE[dwSize]);
if (!lpb)
return 0;
if (GetRawInputData(
(HRAWINPUT)lParam,
RID_INPUT,
lpb.get(),
&dwSize,
sizeof(RAWINPUTHEADER)
) != dwSize)
OutputDebugString(L"GetRawInputData does not return correct size!\n");
RAWINPUT *raw = (RAWINPUT*)lpb.get();
if (raw->header.dwType == RIM_TYPEKEYBOARD)
{
UINT mess = raw->data.keyboard.Message;
UINT vKey = raw->data.keyboard.VKey;
if (mess == WM_KEYDOWN)
{
}
}
else if (raw->header.dwType == RIM_TYPEMOUSE)
{
if (IsMouseMove())
{
BSTR item;
HRESULT hr = m_pAutomator->GetUIAutomationItemNameAtMousePoint(&item);
if (item == NULL)
return 0;
if (currItem == NULL)
currItem = SysAllocString(item);
if (wcscmp(currItem, item) != 0)
{
m_pSpeech->Stop();
m_pSpeech->Speak(item);
if (currItem != NULL)
SysFreeString(currItem);
currItem = SysAllocString(item);
}
SysFreeString(item);
}
}
}
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
int ScreenReader::Exec()
{
MSG msg;
ShowWindow(m_hWnd, m_iCmdShow);
// Tell the user that the program is ready.
m_pSpeech->Speak(L"Rescan ready.", SPF_PURGEBEFORESPEAK);
// The message loop
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void ScreenReader::InitializeWindows()
{
// Create Window class.
WNDCLASSEX wc;
m_applicationName = L"Rescan Screen Reader";
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = m_hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = m_applicationName;
wc.hIconSm = wc.hIcon;
// Register the window class.
RegisterClassEx(&wc);
m_hWnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
m_applicationName,
L"Rescan Screen Reader",
WS_CAPTION | WS_MINIMIZEBOX | WS_OVERLAPPED | WS_SYSMENU,
(GetSystemMetrics(SM_CXSCREEN) - 500) / 2,
(GetSystemMetrics(SM_CYSCREEN) - 300) / 2,
500,
300,
NULL,
NULL,
m_hInstance,
NULL
);
}
void ScreenReader::InitRawInputDevices()
{
// Initialize Keyboard
rid[0].usUsagePage = 0x01;
rid[0].usUsage = 0x06;
rid[0].dwFlags = RIDEV_INPUTSINK;
rid[0].hwndTarget = m_hWnd;
// Initialize Mouse
rid[1].usUsagePage = 0x01;
rid[1].usUsage = 0x02;
rid[1].dwFlags = RIDEV_INPUTSINK;
rid[1].hwndTarget = m_hWnd;
// Register RIDs
RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE));
}
bool ScreenReader::IsMouseMove()
{
POINT pt;
GetCursorPos(&pt);
bool result = !(m_prevMouseX == pt.x && m_prevMouseY == pt.y);
m_prevMouseX = pt.x;
m_prevMouseY = pt.y;
return result;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_QUIT:
PostQuitMessage(0);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return application->MessageHandler(hWnd, message, wParam, lParam);
}
}
I wrapped ISpVoice into the SpeechSynthesis class.
speechsynthesis.h:
#ifndef _SPEECHSYNTHESIS_H_
#define _SPEECHSYNTHESIS_H_
#pragma warning(disable : 4996)
#define SPCAT_VOICES_ONECORE L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech_OneCore\\Voices"
#include <sapi.h>
#include <sphelper.h>
#include <atlbase.h>
class SpeechSynthesis
{
public:
SpeechSynthesis();
~SpeechSynthesis();
HRESULT Speak(LPCWSTR pwcs, DWORD dwFlags = SPF_PURGEBEFORESPEAK | SPF_ASYNC | SPF_IS_NOT_XML, ULONG *pulStreamNumber = NULL);
HRESULT Resume();
HRESULT Pause();
HRESULT Stop();
ISpVoice* getVoice();
private:
CComPtr<ISpObjectToken> cpVoiceToken;
CComPtr<IEnumSpObjectTokens> cpEnum;
ISpVoice* pVoice;
ULONG count;
};
#endif
speechsynthesis.cpp:
#include "speechsynthesis.h"
SpeechSynthesis::SpeechSynthesis()
{
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
if (SUCCEEDED(hr))
hr = SpEnumTokens(SPCAT_VOICES_ONECORE, NULL, NULL, &cpEnum);
if (SUCCEEDED(hr))
hr = cpEnum->GetCount(&count);
if (SUCCEEDED(hr))
{
cpEnum->Item(1, &cpVoiceToken);
pVoice->SetPriority(SPVPRIORITY::SPVPRI_ALERT);
pVoice->SetAlertBoundary(SPEI_PHONEME);
pVoice->SetOutput(NULL, TRUE);
pVoice->SetVoice(cpVoiceToken);
}
if (FAILED(hr))
{
MessageBox(NULL, "A fatal error has occured", "Error Message", MB_ABORTRETRYIGNORE);
}
}
SpeechSynthesis::~SpeechSynthesis()
{
pVoice->Release();
}
HRESULT SpeechSynthesis::Speak(LPCWSTR pwcs, DWORD dwFlags, ULONG *pulStreamNumber)
{
return pVoice->Speak(pwcs, dwFlags, pulStreamNumber);
}
HRESULT SpeechSynthesis::Resume()
{
return pVoice->Resume();
}
HRESULT SpeechSynthesis::Pause()
{
return pVoice->Pause();
}
HRESULT SpeechSynthesis::Stop()
{
return Speak(NULL);
}
ISpVoice * SpeechSynthesis::getVoice()
{
return pVoice;
}
uiautomator.h
#ifndef _UIAUTOMATOR_H_
#define _UIAUTOMATOR_H_
#include <windows.h>
#include <oleacc.h>
#include <uiautomation.h>
#pragma comment(lib, "oleacc.lib")
class UIAutomator
{
public:
UIAutomator();
~UIAutomator();
HRESULT GetItemNameAtMousePoint(BSTR *pStr);
HRESULT GetUIAutomationItemNameAtMousePoint(BSTR *pStr);
private:
HRESULT InitUIAutomation();
private:
IUIAutomation *m_automation;
};
#endif
uiautomator.cpp
#include "uiautomator.h"
UIAutomator::UIAutomator()
{
SetProcessDPIAware();
HRESULT hr = InitUIAutomation();
}
UIAutomator::~UIAutomator()
{
}
HRESULT UIAutomator::GetItemNameAtMousePoint(BSTR * pStr)
{
POINT pt;
GetPhysicalCursorPos(&pt);
VARIANT varItem;
IAccessible *pAcc;
HRESULT hr = AccessibleObjectFromPoint(pt, &pAcc, &varItem);
if (SUCCEEDED(hr))
{
hr = pAcc->get_accName(varItem, pStr);
VariantClear(&varItem);
pAcc->Release();
}
return hr;
}
HRESULT UIAutomator::GetUIAutomationItemNameAtMousePoint(BSTR * pStr)
{
CONTROLTYPEID id;
POINT pt;
IUIAutomationElement *elem;
VARIANT val;
GetCursorPos(&pt);
HRESULT hr = m_automation->ElementFromPoint(pt, &elem);
if (SUCCEEDED(hr))
{
hr = elem->get_CurrentControlType(&id);
if (SUCCEEDED(hr))
{
if (id == UIA_PaneControlTypeId)
GetItemNameAtMousePoint(pStr);
else if (id == UIA_EditControlTypeId)
{
hr = elem->GetCurrentPropertyValue(UIA_ValueValuePropertyId, &val);
if (SUCCEEDED(hr))
{
*pStr = SysAllocString(val.bstrVal);
VariantClear(&val);
}
}
else
{
hr = elem->get_CurrentName(pStr);
}
}
elem->Release();
}
return hr;
}
HRESULT UIAutomator::InitUIAutomation()
{
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)&m_automation);
return hr;
}
main.cpp
#include "vld.h"
#include "screenreader.h"
#include <memory>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdShow)
{
std::unique_ptr<ScreenReader> app(new ScreenReader(hInstance, hPrevInstance, pScmdline, iCmdShow));
return app->Exec();
}
If you don't have time to compile here's the program.
If you launch it and hover the mouse on the program window there is a lag when highlighting minimize and close button. Also sometimes the tts doesn't stop immediately when you hover at another object.
Compare this one to NVDA Screen Reader. You will notice the big difference.
It works for me with or without setting SetAlertBoundary(SPEI_PHONEME).
The following is my test code, you can have a try.
HRESULT hr = ::CoInitialize(nullptr);
if (FAILED(hr))
{
return EXIT_FAILURE;
}
std::wstring text;
text = L"This is a sentence.";
CComPtr<ISpVoice> cpVoice;
// Create a SAPI voice
hr = cpVoice.CoCreateInstance(CLSID_SpVoice);
//cpVoice->SetAlertBoundary(SPEI_PHONEME);
// set the output to the default audio device
if (SUCCEEDED(hr))
{
hr = cpVoice->SetOutput(NULL, TRUE);
}
// Speak the text
if (SUCCEEDED(hr))
{
hr = cpVoice->Speak(text.c_str(), SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
}
text = L"The third type, a logarithm of the unsigned fold change, is undoubtedly the most tractable.";
Sleep(600);
hr = cpVoice->Pause();
hr = cpVoice->Resume();
hr = cpVoice->Speak(text.c_str(), SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
Sleep(10000);
::CoUninitialize();
if (SUCCEEDED(hr))
{
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
I finally got it!
CComPtr<ISpAudio> audio;
CSpStreamFormat format;
format.AssignFormat(SPSF_11kHz8BitMono);
Initialize audio
SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOOUT, &audio);
Then, set its format and set it as output to pVoice.
audio->SetFormat(format.FormatId(), format.WaveFormatExPtr());
pVoice->SetOutput(audio, FALSE);
Now I have access to the audio stream!
Now to Immediately stop the audio, call:
audio->SetState(SPAS_STOP, 0);
Then speak again using:
audio->SetState(SPAS_RUN, 0);
pVoice->Speak(L"This is a sentence", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);

Get pixels from IDirect3DSurface9?

I want to get a fast access to the pixels of a game which uses directX9 or 10. Therefore I used the D3D9 library. Do I have to use the D3D10 library when it is a directX10 game? If this is true, the next part is irrelevant.
The following code works except I'm only get zeros in qDebug("%.2f, %.2f, %.2f, %.2f", pixel[0].r, pixel[0].g, pixel[0].b, pixel[0].a); although it should have an other color and I have no clue why. Is there an error in swapping the data?
Initialization:
IDirect3D9 *m_d3d;
IDirect3DDevice9 *m_d3ddev;
D3DDISPLAYMODE *m_d3ddm;
HRESULT hr;
m_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (m_d3d == NULL)
{
qFatal("Cannot create Direct3D!");
}
m_d3ddm = (D3DDISPLAYMODE *)calloc(1, sizeof(D3DDISPLAYMODE));
hr = m_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, m_d3ddm);
if (hr != D3D_OK)
{
m_d3d->Release();
qFatal("Cannot get DisplayMode!");
}
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = m_target;
d3dpp.BackBufferWidth = m_d3ddm->Width;
d3dpp.BackBufferHeight = m_d3ddm->Height;
d3dpp.BackBufferFormat = m_d3ddm->Format;
hr = m_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_target, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &m_d3ddev);
if (FAILED(hr))
{
m_d3d->Release();
qFatal("Failed CreateDevice!");
}
else
{
qDebug("Created Device with %i * %i", m_d3ddm->Width, m_d3ddm->Height);
}
Capture:
IDirect3DSurface9 *renderTarget= NULL;
IDirect3DSurface9 *destTarget = NULL;
hr = m_d3ddev->GetRenderTarget(0, &renderTarget);
if (FAILED(hr))
{
qFatal("Failed GetRenderTarget!");
}
hr = m_d3ddev->CreateOffscreenPlainSurface(m_d3ddm->Width, m_d3ddm->Height, m_d3ddm->Format, D3DPOOL_SYSTEMMEM, &destTarget, NULL);
if (FAILED(hr))
{
qFatal("Failed CreateOffscreenPlainSurface!");
}
hr = m_d3ddev->GetRenderTargetData(renderTarget, destTarget);
if (FAILED(hr))
{
qFatal("Failed GetRenderTargetData!");
}
D3DLOCKED_RECT lr;
ZeroMemory(&lr, sizeof(D3DLOCKED_RECT));
hr = destTarget->LockRect(&lr, 0, D3DLOCK_READONLY);
if (FAILED(hr))
{
qFatal("Cannot lock rect!");
}
ARGB *pixel = (ARGB *)lr.pBits;
if (!pixel)
{
qFatal("No data!");
}
qDebug("%.2f, %.2f, %.2f, %.2f", pixel[0].r, pixel[0].g, pixel[0].b, pixel[0].a);
hr = destTarget->UnlockRect();
if (FAILED(hr))
{
qFatal("Cannot unlock rect!");
}
renderTarget->Release();
destTarget->Release();
Cleaning
m_d3ddev->Release();
m_d3d->Release();
delete m_d3ddm;
delete m_d3d;
delete m_d3ddev;
This code is a simulation of what you should have.. It shows that the backbuffer is actually being captured. I created the device the exact same way you did. I capture the back-buffer the same way..
To test if the back-buffer is actually captured, I have set the window background to a navy-blue colour. Then when I call the capture function, I compare all the colours in the buffer to the original background colour.. This lets me know if a single pixel is different than the background..
If it is different, we "possibly" failed to capture the background. Otherwise, we are good..
I've tested the below and it indeed captures the backbuffer perfectly fine.. I even saved the backbuffer to a bitmap to see if it captures it and it does..
All in all, there's nothing wrong with your code, but I cannot guarantee that you are using it correctly.. In other words, you haven't shown me where you call your back-buffer capture code.. There are many places it can be called, but the best places are either in a hook to EndScene or a hook to Present. If you are using this on your own application, then call it in your onDraw/onUpdate function..
#include <tchar.h>
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <iostream>
HRESULT capture(IDirect3DDevice9* m_d3ddev, void* buffer, int& width, int& height, D3DFORMAT format);
void onUpdate(HWND hwnd, IDirect3D9* d3d9, IDirect3DDevice9* d3ddev9, D3DFORMAT format)
{
d3ddev9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
d3ddev9->BeginScene();
//All drawing goes here..
d3ddev9->EndScene();
d3ddev9->Present(NULL, NULL, NULL, NULL); //Swaps the back and front buffers. Displays all drawing on the screen..
RECT rect;
GetClientRect(hwnd, &rect);
int width = rect.right - rect.left, height = rect.bottom - rect.top;
unsigned char* buffer = new unsigned char[width * height * 4];
capture(d3ddev9, buffer, width, height, format);
unsigned char* px = buffer;
for(int i = 0; i < height; ++i)
{
for(int j = 0; j < width; ++j)
{
unsigned char B = *px++;
unsigned char G = *px++;
unsigned char R = *px++;
unsigned char A = *px++;
if(D3DCOLOR_XRGB(R, G, B) != D3DCOLOR_XRGB(0, 40, 100))
{
MessageBox(NULL, "FAILED.. Colour differs from original background..", NULL, 0);
}
}
}
delete[] buffer;
}
int onDisplay(HWND hwnd)
{
IDirect3D9* d3d9 = NULL;
IDirect3DDevice9* d3ddev9 = NULL;
D3DPRESENT_PARAMETERS Parameters = {0};
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE* dispMode = new D3DDISPLAYMODE();
d3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, dispMode);
Parameters.Windowed = true;
Parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
Parameters.hDeviceWindow = hwnd;
Parameters.BackBufferFormat = dispMode->Format;
d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &Parameters, &d3ddev9);
delete dispMode;
MSG messages;
ShowWindow(hwnd, SW_SHOW);
while(true)
{
if(PeekMessage(&messages, NULL, 0, 0, PM_REMOVE) > 0)
{
if(messages.message != WM_QUIT)
{
TranslateMessage(&messages);
DispatchMessageW(&messages);
continue;
}
break;
}
else
{
onUpdate(hwnd, d3d9, d3ddev9, Parameters.BackBufferFormat);
}
}
if(d3ddev9) d3ddev9->Release();
if(d3d9) d3d9->Release();
return messages.wParam;
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
WNDCLASSEX wincl;
wincl.hInstance = hThisInstance;
wincl.lpszClassName = "D3DWindow";
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if(!RegisterClassEx(&wincl)) return 0;
HWND hwnd = CreateWindowEx(0, "D3DWindow", "D3D9: Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, hThisInstance, NULL);
return onDisplay(hwnd);
}
HRESULT capture(IDirect3DDevice9* m_d3ddev, void* buffer, int& width, int& height, D3DFORMAT format)
{
IDirect3DSurface9 *renderTarget= NULL;
IDirect3DSurface9 *destTarget = NULL;
HRESULT hr = m_d3ddev->GetRenderTarget(0, &renderTarget);
hr = m_d3ddev->CreateOffscreenPlainSurface(width, height, format, D3DPOOL_SYSTEMMEM, &destTarget, NULL);
if(FAILED(hr))
{
printf("Failed CreateOffscreenPlainSurface!");
}
hr = m_d3ddev->GetRenderTargetData(renderTarget, destTarget);
if(FAILED(hr))
{
printf("Failed GetRenderTargetData!");
}
D3DLOCKED_RECT lr;
ZeroMemory(&lr, sizeof(D3DLOCKED_RECT));
hr = destTarget->LockRect(&lr, 0, D3DLOCK_READONLY);
if(FAILED(hr))
{
printf("Cannot lock rect!");
}
if(lr.pBits)
{
memcpy(buffer, lr.pBits, width * height * 4);
}
hr = destTarget->UnlockRect();
if(FAILED(hr))
{
printf("Cannot unlock rect!");
}
renderTarget->Release();
destTarget->Release();
return hr;
}