Basic Win32 WebView2 example not working in pure C - c++

A new problem blows my mind lately: a very simple code using the WebView2 library from Microsoft works if compiled as C++ but not as C. What could be causing the problem? I tried all sorts of fixes, like using an older version of the WebView2 library, using Edge Canary or beta or a different version of WebView2 Runtime, it simply refuses to work.
Here is the sample code in C:
#include <initguid.h>
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#include <Shlobj_core.h>
#include "WebView2.h"
#define APPLICATION_NAME TEXT("WebView2")
#define error_printf printf
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* envHandler;
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* completedHandler;
HWND hWnd = NULL;
ICoreWebView2Controller* webviewController = NULL;
ICoreWebView2* webviewWindow = NULL;
BOOL bEnvCreated = FALSE;
ULONG HandlerRefCount = 0;
ULONG HandlerAddRef(IUnknown* This)
{
return ++HandlerRefCount;
}
ULONG HandlerRelease(IUnknown* This)
{
--HandlerRefCount;
if (HandlerRefCount == 0)
{
if (completedHandler)
{
free(completedHandler->lpVtbl);
free(completedHandler);
}
if (envHandler)
{
free(envHandler->lpVtbl);
free(envHandler);
}
}
return HandlerRefCount;
}
HRESULT HandlerQueryInterface(
IUnknown* This,
IID* riid,
void** ppvObject
)
{
*ppvObject = This;
HandlerAddRef(This);
return S_OK;
}
HRESULT HandlerInvoke(
IUnknown* This,
HRESULT errorCode,
void* arg
)
{
if (!bEnvCreated)
{
bEnvCreated = TRUE;
char ch;
completedHandler = malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler));
if (!completedHandler)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"Cannot allocate ICoreWebView2CreateCoreWebView2ControllerCompletedHandler",
GetLastError()
);
ch = _getch();
return GetLastError();
}
completedHandler->lpVtbl = malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl));
if (!completedHandler->lpVtbl)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"Cannot allocate ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl",
GetLastError()
);
ch = _getch();
return GetLastError();
}
completedHandler->lpVtbl->AddRef = HandlerAddRef;
completedHandler->lpVtbl->Release = HandlerRelease;
completedHandler->lpVtbl->QueryInterface = HandlerQueryInterface;
completedHandler->lpVtbl->Invoke = HandlerInvoke;
ICoreWebView2Environment* env = arg;
env->lpVtbl->CreateCoreWebView2Controller(
env,
hWnd,
completedHandler
);
}
else
{
ICoreWebView2Controller* controller = arg;
if (controller != NULL) {
webviewController = controller;
webviewController->lpVtbl->get_CoreWebView2(
webviewController,
&webviewWindow
);
}
ICoreWebView2Settings* Settings;
webviewWindow->lpVtbl->get_Settings(
webviewWindow,
&Settings
);
Settings->lpVtbl->put_IsScriptEnabled(
Settings,
TRUE
);
Settings->lpVtbl->put_AreDefaultScriptDialogsEnabled(
Settings,
TRUE
);
Settings->lpVtbl->put_IsWebMessageEnabled(
Settings,
TRUE
);
Settings->lpVtbl->put_AreDevToolsEnabled(
Settings,
FALSE
);
Settings->lpVtbl->put_AreDefaultContextMenusEnabled(
Settings,
TRUE
);
Settings->lpVtbl->put_IsStatusBarEnabled(
Settings,
TRUE
);
RECT bounds;
GetClientRect(hWnd, &bounds);
webviewController->lpVtbl->put_Bounds(
webviewController,
bounds
);
webviewWindow->lpVtbl->Navigate(
webviewWindow,
L"https://google.com/"
);
}
return S_OK;
}
LRESULT CALLBACK WindowProc(
_In_ HWND hWnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
/*case WM_NCCALCSIZE:
{
return 0;
}*/
case WM_DPICHANGED:
{
RECT* const newWindowSize = (RECT*)(lParam);
SetWindowPos(
hWnd,
NULL,
newWindowSize->left,
newWindowSize->top,
newWindowSize->right - newWindowSize->left,
newWindowSize->bottom - newWindowSize->top,
SWP_NOZORDER | SWP_NOACTIVATE);
return TRUE;
}
case WM_SIZE:
{
if (webviewController != NULL) {
RECT bounds;
GetClientRect(hWnd, &bounds);
webviewController->lpVtbl->put_Bounds(
webviewController,
bounds
);
};
break;
}
default:
{
return DefWindowProc(
hWnd,
uMsg,
wParam,
lParam
);
}
}
return 0;
}
int WINAPI wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nShowCmd
)
{
int ch;
FILE* conout;
AllocConsole();
freopen_s(
&conout,
"CONOUT$",
"w",
stdout
);
HRESULT hr;
if (!SetProcessDpiAwarenessContext(
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
))
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"SetProcessDpiAwarenessContext",
GetLastError()
);
ch = _getch();
return GetLastError();
}
hr = CoInitialize(NULL);
if (FAILED(hr))
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"CoInitialize",
hr
);
ch = _getch();
return hr;
}
WNDCLASS wndClass = { 0 };
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = APPLICATION_NAME;
hWnd = CreateWindowEx(
0,
(LPCWSTR)(
MAKEINTATOM(
RegisterClass(&wndClass)
)
),
APPLICATION_NAME,
WS_OVERLAPPEDWINDOW,
100, 100, 800, 800,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"CreateWindowEx",
GetLastError()
);
ch = _getch();
return GetLastError();
}
ShowWindow(hWnd, nShowCmd);
envHandler = malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler));
if (!envHandler)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"Cannot allocate ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler",
GetLastError()
);
ch = _getch();
return GetLastError();
}
envHandler->lpVtbl = malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl));
if (!envHandler->lpVtbl)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"Cannot allocate ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl",
GetLastError()
);
ch = _getch();
return GetLastError();
}
envHandler->lpVtbl->AddRef = HandlerAddRef;
envHandler->lpVtbl->Release = HandlerRelease;
envHandler->lpVtbl->QueryInterface = HandlerQueryInterface;
envHandler->lpVtbl->Invoke = HandlerInvoke;
UpdateWindow(hWnd);
CreateCoreWebView2EnvironmentWithOptions(
NULL,
NULL,
NULL,
envHandler
);
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(
&msg,
NULL,
0,
0)) != 0)
{
// An error occured
if (bRet == -1)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
As you can see, I simply used lpVtbl where appropiate and provided proper callbacks. The code is a bit more compact in C++:
#include <Windows.h>
#include <stdio.h>
#include <wrl.h>
#include <wil/com.h>
#include "WebView2.h"
#define APPLICATION_NAME TEXT("WebView2")
static wil::com_ptr<ICoreWebView2Controller> webviewController;
static wil::com_ptr<ICoreWebView2> webviewWindow;
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
case WM_DPICHANGED:
{
RECT* const newWindowSize = (RECT*)(lParam);
SetWindowPos(hwnd,
NULL,
newWindowSize->left,
newWindowSize->top,
newWindowSize->right - newWindowSize->left,
newWindowSize->bottom - newWindowSize->top,
SWP_NOZORDER | SWP_NOACTIVATE);
return TRUE;
}
case WM_SIZE:
{
if (webviewController != NULL) {
RECT bounds;
GetClientRect(hwnd, &bounds);
webviewController->put_Bounds(bounds);
};
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
{
return DefWindowProc(
hwnd,
uMsg,
wParam,
lParam
);
}
}
return 0;
}
int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PWSTR pCmdLine,
int nCmdShow
)
{
SetProcessDpiAwarenessContext(
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
);
WNDCLASS wndClass = { 0 };
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = APPLICATION_NAME;
RegisterClass(&wndClass);
HWND hWnd = CreateWindowEx(
0,
APPLICATION_NAME,
APPLICATION_NAME,
WS_OVERLAPPEDWINDOW,
100, 100, 800, 800,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(
hWnd,
nCmdShow
);
UpdateWindow(hWnd);
CreateCoreWebView2EnvironmentWithOptions(NULL, NULL, NULL,
Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[hWnd](HRESULT result, ICoreWebView2Environment* env) -> HRESULT {
// Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
env->CreateCoreWebView2Controller(hWnd, Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT {
if (controller != nullptr) {
webviewController = controller;
webviewController->get_CoreWebView2(&webviewWindow);
}
// Add a few settings for the webview
// The demo step is redundant since the values are the default settings
ICoreWebView2Settings* Settings;
webviewWindow->get_Settings(&Settings);
Settings->put_IsScriptEnabled(TRUE);
Settings->put_AreDefaultScriptDialogsEnabled(TRUE);
Settings->put_IsWebMessageEnabled(TRUE);
Settings->put_AreDevToolsEnabled(FALSE);
//Settings->put_AreDefaultContextMenusEnabled(FALSE);
Settings->put_IsStatusBarEnabled(FALSE);
// Resize WebView to fit the bounds of the parent window
RECT bounds;
GetClientRect(hWnd, &bounds);
webviewController->put_Bounds(bounds);
webviewController->put_ZoomFactor(0.8);
// Schedule an async task to navigate to Bing
webviewWindow->Navigate(HOME_PAGE);
// Step 4 - Navigation events
// Step 5 - Scripting
// Step 6 - Communication between host and web content
return S_OK;
}).Get());
return S_OK;
}).Get());
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(
&msg,
NULL,
0,
0)) != 0)
{
// An error occured
if (bRet == -1)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
Since I have now stumbled upon this, I am really curious what may be causing this. Why does it matter in the end? Thanks for any pointers.
It does not work means the web page is not displayed. The window is blank. The WebView2 actually is not displayed on the window.
Edit:
What are wil::com_ptrs actually? If I change this:
static wil::com_ptr<ICoreWebView2Controller> webviewController;
static wil::com_ptr<ICoreWebView2> webviewWindow;
To this:
static ICoreWebView2Controller* webviewController;
static ICoreWebView2* webviewWindow;
In C++, I break it. Why? Just why? (I replaced the callbacks Microsoft::WRL::Callback with standalone classes and, naturally, it still worked, but getting rid of the COM pointers and using regular pointers breaks it. Why...?

The solution is simple, if one actually takes a minute to look a bit on the correctness and logic of the code, rather than suggesting generics and commenting just so that the patch cables have some bytes to carry around. This section has to be modified a bit: since I use controller after the function returns, and without using smart pointers, I have to increase its reference count so that the library knows I use it and does not free it after the Invoke function body is executed. Of course, when you do the assignment in the version with smart pointers, this is automatically done in the background. Unfortunately, I overlooked this, not being that aware this happens. Things like these is the reason I write these ports in C, so that I take an in-depth look and better understand how stuff works from top to bottom. 'It can't be done' without an actual reason is not a valid answer for me.
Anyway, here is the fixed version:
if (controller != NULL) {
webviewController = controller;
webviewController->lpVtbl->get_CoreWebView2(
webviewController,
&webviewWindow
);
webviewController->lpVtbl->AddRef(webviewController); // <-- here, increase the reference count for the webviewController
}
That's it. many thanks to the guy who helped me out: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1124

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

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

Converting C++ dll injector into unicode

Okay so Ive Ran and tested the solution given on the website, which came with positive results.
Now I've converted this into a win32 unicode project without any compile errors or runtime errors(it even says its injected the dll). Without any noticeable errors everything seems fine other than the fact that the dll is not executing after injection.
Keep in mind the Dll does work if the Injectors character set is not set, but not if converted into unicode. Ive even converted the dll into unicode just to test and that works on the NON unicode Injector. With that in mind ive came to the conclusion that the problem lies in the converted Injector.
If anymore information is needed feel free to ask. You may also ask, why convert into unicode? its my personal preference. Since i did not write the code myself converting does help me learn the code.
I did Erase the 2nd function but that function was never called and was practically useless. it was the same function except with A different Variable Type. Conversion did not work prior to erasing.
Is there anything wrong with this code?
I think the problem is here in Injector.cpp
**Now I am thinking its the DLL. After changing kernel32.dll to something random i do receive an error through GetLastError() error 127 which was expected. But then the program of which was injected into crashes. which then means the dll was injected. ** So After thinking this through I tested without adding a dll into the equation, throws the same error along with the crash. seems like its injecting but its not injecting the dll. ** DLL_NAME is being loaded into the function. wcslen(DLL_NAME) is returning a value, along with RemoteString(is 0 with no dll loaded).
That being said I've included Dllmain.cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <Windows.h>
#include <string.h>
#include <stdio.h>
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
wchar_t* helloStr;
wchar_t buf[250];
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
helloStr = L"Hello";
wsprintf(buf, helloStr);
MessageBoxW(NULL, buf, NULL, NULL);
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
helloStr = L"Goodbye";
wsprintf(buf, helloStr);
MessageBoxW(NULL, buf, NULL, NULL);
break;
}
return TRUE;
}
Injector.cpp
#include "stdafx.h"
#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <conio.h>
#include <stdio.h>
#include <iostream>
#include "Resource.h"
Injector::Injector(void)
{
}
Injector::~Injector(void)
{
}
#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ)
bool Injector::Inject(wchar_t* procName, wchar_t* DLL_NAME)
{
DWORD pID = GetTargetThreadIDFromProcName(procName);
HANDLE Proc = 0;
HMODULE hLib = 0;
wchar_t buf[50] = { 0 };
LPVOID RemoteString, LoadLibAddy;
if (!pID)
return false;
Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
if (!Proc)
{
swprintf_s(buf, L"OpenProcess() failed: %d", GetLastError());
MessageBoxW(NULL, buf, L"Loader", MB_OK);
wprintf(buf);
return false;
}
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
// Allocate space in the process for our DLL
RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, wcslen(DLL_NAME), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// Write the string name of our DLL in the memory allocated
WriteProcessMemory(Proc, (LPVOID)RemoteString, DLL_NAME, wcslen(DLL_NAME), NULL);
// Load our DLL
CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL);
CloseHandle(Proc);
return true;
}
DWORD Injector::GetTargetThreadIDFromProcName(wchar_t* ProcName)
{
PROCESSENTRY32 pe;
HANDLE thSnapShot;
BOOL retval = false;
thSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (thSnapShot == INVALID_HANDLE_VALUE)
{
MessageBoxW(NULL, L"Error: Unable to create toolhelp snapshot!", L"2MLoader", MB_OK);
return false;
}
pe.dwSize = sizeof(PROCESSENTRY32);
retval = Process32First(thSnapShot, &pe);
while (retval)
{
if (!wcscmp(pe.szExeFile, ProcName))
{
return pe.th32ProcessID;
}
retval = Process32Next(thSnapShot, &pe);
}
return 0;
}
Resource.h
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 130
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif
#pragma once
#include <Windows.h>
class Injector
{
public:
Injector(void);
~Injector(void);
bool Inject(wchar_t* procName, wchar_t* DLL_NAME);
private:
DWORD GetTargetThreadIDFromProcName(wchar_t * ProcName);
};
Main.cpp
// DLL_Injector_WIN32.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "DLL_Injector_WIN32.h"
#include <Commdlg.h>
#include <iostream>
#include <Windows.h>
#include "Resource.h"
#define MAX_LOADSTRING 100
Injector* injector = new Injector();
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
wchar_t szFile[MAX_PATH];
LPTSTR PROC_NAME = new TCHAR[1024];
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
BOOL FileOpen(HWND hwnd);
int start(wchar_t* DLL_PATH, wchar_t* PROC_NAME);
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_DLL_INJECTOR_WIN32, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DLL_INJECTOR_WIN32));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DLL_INJECTOR_WIN32));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_DLL_INJECTOR_WIN32);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 350, 100,
NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_CREATE:
CreateWindowEx(
WS_EX_CLIENTEDGE,
L"BUTTON",
L"Inject",
WS_CHILD | WS_VISIBLE | WS_EX_CLIENTEDGE,
280, 10, 45, 25,
hWnd, (HMENU)IDC_INJECT, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
CreateWindowEx(
WS_EX_CLIENTEDGE,
L"BUTTON",
L"DLL",
WS_CHILD | WS_VISIBLE | WS_EX_CLIENTEDGE,
240, 10, 35, 25,
hWnd, (HMENU)IDC_DLL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
CreateWindowEx(
WS_EX_CLIENTEDGE,
L"EDIT",
L"",
WS_CHILD | WS_VISIBLE | WS_EX_CLIENTEDGE | ES_AUTOHSCROLL,
65, 10, 170, 25,
hWnd, (HMENU)IDC_PROCESS, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
CreateWindowEx(
0,
L"STATIC",
L"Process",
WS_CHILD | WS_VISIBLE,
5, 10, 55, 25,
hWnd, (HMENU)NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDC_DLL:
{
FileOpen(hWnd);
// MessageBox(NULL, szFile, L"TEST", NULL);
}
break;
case IDC_INJECT:
{
GetDlgItemText(hWnd, IDC_PROCESS, PROC_NAME, 1024);
start(szFile, PROC_NAME);
}
break;
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
BOOL FileOpen(HWND hwnd)
{
OPENFILENAME ofn;
HANDLE hf;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = L"DLL\0*.dll\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileNameW(&ofn) == TRUE)
{
//CheckDlgButton(hwnd, IDC_PASS_LIST, BST_UNCHECKED);
hf = CreateFile(ofn.lpstrFile,
GENERIC_READ,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL);
return TRUE;
if (hf == (HANDLE)-1)
{
MessageBox(NULL, L"Could not open this file", L"File I/O Error", MB_ICONSTOP);
return FALSE;
}
}
else
{
return FALSE;
}
}
int start(wchar_t* DLL_PATH, wchar_t* PROC_NAME)
{
WCHAR dllDir[MAX_PATH];
wcscpy_s(dllDir, MAX_PATH, DLL_PATH);
//MessageBox(NULL, dllDir, L"DLL path: ", MB_ICONSTOP);
//MessageBox(NULL, PROC_NAME, L"Process: ", MB_ICONSTOP);
if (injector->Inject(PROC_NAME, dllDir)){
MessageBox(NULL, L"DLL injected!", L"DLL injected!", MB_ICONSTOP);
}
else {
MessageBox(NULL, L"Failed to inject the dll...", L"File I/O Error", MB_ICONSTOP);
}
return 0;
}
Turns out WriteProcessMemory wanted DLL_NAME to be a char. converted wchar_t* to char in a new variable and now i have no problems.

infine windows message loop - c++

I tried to create this window class which creates and shows a window. When I run this class, GetMessage keep on sending WM_PAINT message, and task manager shows me about 50% CPU usage just from my process.
main.cpp:
#include "Window.h"
int WINAPI WinMain(
HINSTANCE /* hInstance */,
HINSTANCE /* hPrevInstance */,
LPSTR /* lpCmdLine */,
int /* nCmdShow */
)
{
Window::GlobalInitialize();
Window window(L"abcd", 500, 500);
if (SUCCEEDED(window.Initialize()))
window.RunMessageLoop();
Window::GlobalTerminate();
return 0;
}
Window.h:
#ifndef WINDOW_HEADER
#define WINDOW_HEADER
#include <Windows.h>
#include <functional>
#pragma comment(lib, "d2d1.lib")
class Window;
typedef std::function<void(Window *window, UINT id, LPCWSTR message)> ErrorCallback;
class Window {
public:
#define ERROR_FAILED_HINSTANCE 1
#define ERROR_FAILED_HINSTANCE_STR L"Failed to retrieve hInstance"
#define ERROR_FAILED_REGISTER 2
#define ERROR_FAILED_REGISTER_STR L"Failed to register window class"
#define ERROR_FAILED_CREATION 3
#define ERROR_FAILED_CREATION_STR L"Failed to create window"
typedef std::function<HRESULT(Window *window)> WEOnCreate;
typedef std::function<HRESULT(Window *window)> WEOnDestroy;
typedef std::function<HRESULT(Window *window)> WEOnRender;
typedef std::function<HRESULT(Window *window, UINT width, UINT height)> WEOnResize;
typedef std::function<HRESULT(Window *window, UINT horizontalResolution, UINT verticalResolution)> WEOnScreenResolutionChange;
Window(LPCWSTR title, UINT width, UINT height);
~Window();
HRESULT SetSize(UINT width, UINT height);
HRESULT SetTitle(LPCWSTR title);
inline UINT GetWidth() { return _width; }
inline UINT GetHeight() { return _height; }
inline LPCWSTR GetTitle() { return _title; }
inline HWND GetHandle() { return hWnd; }
inline void SetOnCreateCallback(WEOnCreate fun) { _onCreate = fun; }
inline void SetOnDestroyCallback(WEOnDestroy fun) { _onDestroy = fun; }
inline void SetOnRenderCallback(WEOnRender fun) { _onRender = fun; }
inline void SetOnResizeCallback(WEOnResize fun) { _onResize = fun; }
inline void SetOnScreenResolutionChangeCallback(WEOnScreenResolutionChange fun) { _onResChange = fun; }
inline void SetExtraAllocatedSpace(void *ptr) { extra = ptr; }
inline void *GetExtraAllocatedSpace() { return extra; }
inline void Terminate() { if (hWnd) DestroyWindow(hWnd); }
static inline void SetErrorCallback(ErrorCallback fun) { _errorCallback = fun; }
HRESULT Initialize();
void RunMessageLoop();
static HRESULT GlobalInitialize();
static HRESULT GlobalTerminate();
private:
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static inline void throwError(Window *window, UINT id, LPCWSTR message) {
if (_errorCallback)
_errorCallback(window, id, message);
}
WEOnCreate _onCreate;
WEOnDestroy _onDestroy;
WEOnRender _onRender;
WEOnResize _onResize;
WEOnScreenResolutionChange _onResChange;
static ErrorCallback _errorCallback;
static LPCWSTR szClassName;
static HINSTANCE hInstance;
HWND hWnd;
int _width, _height;
LPCWSTR _title;
void *extra;
};
#endif
Window.cpp:
#include "Window.h"
//Initialize static variables
ErrorCallback Window::_errorCallback = nullptr;
LPCWSTR Window::szClassName = L"WindowClass";
HINSTANCE Window::hInstance;
Window::Window(LPCWSTR title = L"Window", UINT width = 640, UINT height = 480) :
_onCreate(nullptr),
_onDestroy(nullptr),
_onRender(nullptr),
_onResize(nullptr),
hWnd(NULL),
extra(NULL),
_width(width),
_height(height),
_title(title) {}
Window::~Window() {
if (hWnd) {
DestroyWindow(hWnd);
hWnd = NULL;
}
}
HRESULT Window::GlobalInitialize() {
// Retreive hInstance
hInstance = GetModuleHandle(NULL);
if (!hInstance) {
throwError(NULL, ERROR_FAILED_HINSTANCE, ERROR_FAILED_HINSTANCE_STR);
return E_FAIL;
}
// Create window class
WNDCLASSEX wcex = {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = Window::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = hInstance;
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.hCursor = LoadCursor(NULL, IDI_APPLICATION);
wcex.lpszClassName = szClassName;
if (!RegisterClassEx(&wcex)) {
throwError(NULL, ERROR_FAILED_REGISTER, ERROR_FAILED_REGISTER_STR);
return E_FAIL;
}
return S_OK;
}
HRESULT Window::GlobalTerminate() {
if (UnregisterClass(szClassName, hInstance))
return S_OK;
else
return E_FAIL;
}
void Window::RunMessageLoop() {
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
HRESULT Window::Initialize() {
// Create the window
hWnd = CreateWindow(
szClassName,
_title,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
_width,
_height,
NULL,
NULL,
hInstance,
this
);
if (!hWnd) {
throwError(this, ERROR_FAILED_CREATION, ERROR_FAILED_CREATION_STR);
return E_FAIL;
}
// Show and render the window
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
return S_OK;
}
LRESULT CALLBACK Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_CREATE) {
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
Window *window = (Window *)pcs->lpCreateParams;
::SetWindowLongPtr(hWnd, GWLP_USERDATA, PtrToUlong(window));
if (window->_onCreate != nullptr)
window->_onCreate(window);
}
Window *pWnd = reinterpret_cast<Window *>(static_cast<LONG_PTR>(GetWindowLongPtr(hWnd, GWLP_USERDATA)));
HRESULT hr = S_OK;
if (!pWnd) {
return DefWindowProc(hWnd, message, wParam, lParam);
}
switch (message) {
case WM_PAINT:
{
if (pWnd->_onRender)
hr = pWnd->_onRender(pWnd);
else
hr = S_OK;
}
break;
case WM_SIZE:
{
if (pWnd->_onResize)
hr = pWnd->_onResize(pWnd, LOWORD(lParam), HIWORD(lParam));
else
hr = S_OK;
}
break;
case WM_DISPLAYCHANGE:
{
if (pWnd->_onResChange)
hr = pWnd->_onResChange(pWnd, LOWORD(lParam), HIWORD(lParam));
else
hr = S_OK;
}
break;
case WM_DESTROY:
{
if (pWnd->_onDestroy && FAILED(pWnd->_onDestroy(pWnd)))
break;
}
PostQuitMessage(0);
hWnd = NULL;
break;
default:
hr = DefWindowProc(hWnd, message, wParam, lParam);
}
return hr;
}
HRESULT Window::SetSize(UINT width, UINT height) {
if (hWnd)
if (!::SetWindowPos(hWnd, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER))
return E_FAIL;
_width = width;
_height = height;
return S_OK;
}
HRESULT Window::SetTitle(LPCWSTR title) {
if (hWnd)
if (!::SetWindowText(hWnd, title))
return E_FAIL;
_title = title;
return S_OK;
}
I hope someone can help me since everything looks OK(the window even runs fine).
Firstly, you seem to be treating the window procedure as if it was a COM method, but window procedures do not return an HRESULT - they return an LRESULT whose meaning is different for each message.
In the case of WM_PAINT it's not possible to return a value that indicates "I don't need to paint this time". The system will send WM_PAINT messages as long as a portion of your window is marked as dirty, and the way you mark the dirty area as "painted" is by calling BeginPaint and EndPaint. If you don't do this, the system will continue to consider your window as dirty and continue to send WM_PAINT messages.
You haven't shown the source code for your _onRender function but the very fact that you have made WM_PAINT handling optional (i.e. if nothing calls SetOnRenderCallback then no callback will be registered) means that you are probably not processing WM_PAINT correctly. At the very least, if you don't do the painting yourself, you should pass the message through to DefWindowProc to allow the default processing to take place.

direct3d initialization failure / c++

this is the code that creates a simple window and initializes a simple direct3d device. but every time the program reaches the render() function application terminates. i have no idea why that happens. can somebody explain me this weird behavior? thank you!!
//====================================================================================================
#include <windows.h>
#include <d3d9.h>
//====================================================================================================
HINSTANCE hInst;
HWND wndHandle;
//====================================================================================================
LPDIRECT3D9 pD3D; // the Direct3D object
LPDIRECT3DDEVICE9 pd3dDevice; // the Direct3D device
//====================================================================================================
bool initWindow(HINSTANCE hInstance);
bool initDirect3D(void);
void cleanUp (void);
void render(void);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//====================================================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
if (!initWindow(hInstance)) return false;
if (!initDirect3D()) return false;
MSG msg;
ZeroMemory(&msg, sizeof(msg));
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
} else {
render(); // i think this is the problem ...
}
return static_cast<int>(msg.wParam);
}
bool initWindow(HINSTANCE hInstance )
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(0, IDI_APPLICATION);
wcex.hCursor = LoadCursor(0, IDC_ARROW);
wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
wcex.lpszMenuName = 0L;
wcex.lpszClassName = L"DirectXTemplate";
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
wndHandle = CreateWindow(L"DirectXTemplate", L"DirectX Template", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);
if (!wndHandle) return false;
ShowWindow(wndHandle, SW_SHOW);
UpdateWindow(wndHandle);
return true;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
bool initDirect3D(void)
{
pD3D = NULL;
pd3dDevice = NULL;
// create the DirectX object
if(NULL == (pD3D = Direct3DCreate9(D3D_SDK_VERSION))) return false;
// fill the presentation parameters structure
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferHeight = 480;
d3dpp.BackBufferWidth = 640;
d3dpp.hDeviceWindow = wndHandle;
// create a default DirectX device
if (FAILED(pD3D -> CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, wndHandle, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pd3dDevice))) return false;
return true;
}
void render(void)
{
// Check to make sure you have a valid Direct3D device
if(NULL == pd3dDevice) return; // clear the back buffer to a blue color
pd3dDevice -> Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
// Present the back buffer contents to the display
pd3dDevice -> Present(NULL, NULL, NULL, NULL);
}
void cleanUp (void)
{
// Release the device and the Direct3D object
if (pd3dDevice != NULL) pd3dDevice -> Release();
if (pD3D != NULL) pD3D -> Release();
}
#DuckMaestro is right. Your program is going through the msg/render process once and is then ending. It should only end if the msg is to exit the program. Try putting in a loop like this:
while(msg.message!=WM_QUIT){
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
} else {
render(); // i think this is the problem ...
}
}
Your...
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
} else {
render(); // i think this is the problem ...
}
...needs to be in a while loop, no? Step through your code with a debugger, statement by statement. Win32 applications need a while loop to stay alive, so-to-speak.