Related
I would like to know when the system language has changed in my application, even when the application is not active. So I created an implementation using ITfLanguageProfileNotifySink. But, ITfLanguageProfileNotifySink::OnLanguageChange seems to only be executing when the window is active. How can I have this execute when the window is not the top active window?
Sample Code
#include <windows.h>
#include <msctf.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class NotifyMe : protected ITfLanguageProfileNotifySink {
public:
ITfSource *m_tfSource;
DWORD m_dwCookie;
void Init();
virtual HRESULT STDMETHODCALLTYPE OnLanguageChange(LANGID langid, __RPC__out BOOL *pfAccept);
virtual HRESULT STDMETHODCALLTYPE OnLanguageChanged();
// IUnknown implementation
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
ULONG m_ulRefCount; ///< COM object reference count
};
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if(hwnd == NULL)
{
return 0;
}
CoInitialize(nullptr);
NotifyMe notify;
notify.Init();
ShowWindow(hwnd, nCmdShow);
MSG msg = {};
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void NotifyMe::Init() {
m_tfSource = NULL;
ITfInputProcessorProfiles *pProfiles;
HRESULT hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (LPVOID*)&pProfiles);
if(SUCCEEDED(hr)) {
hr = pProfiles->QueryInterface(IID_ITfSource, (LPVOID*)&m_tfSource);
if(SUCCEEDED(hr)) {
hr = m_tfSource->AdviseSink(IID_ITfLanguageProfileNotifySink, (ITfLanguageProfileNotifySink*)this, &m_dwCookie);
if(FAILED(hr) || m_dwCookie == -1) {
m_tfSource->Release();
m_tfSource = NULL;
}
}
pProfiles->Release();
}
}
HRESULT STDMETHODCALLTYPE NotifyMe::OnLanguageChange(LANGID langid, __RPC__out BOOL *pfAccept)
{
if(pfAccept) *pfAccept = TRUE;
return S_OK;
}
HRESULT STDMETHODCALLTYPE NotifyMe::OnLanguageChanged()
{
OutputDebugStringA("Language Changed");
return S_OK;
}
HRESULT STDMETHODCALLTYPE NotifyMe::QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject)
{
if(!ppvObject)
return E_INVALIDARG;
if(riid == IID_IUnknown)
*ppvObject = static_cast<IUnknown*>(this);
else if(riid == IID_ITfLanguageProfileNotifySink)
*ppvObject = static_cast<ITfLanguageProfileNotifySink*>(this);
else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
ULONG STDMETHODCALLTYPE NotifyMe::AddRef()
{
InterlockedIncrement(&m_ulRefCount);
return m_ulRefCount;
}
ULONG STDMETHODCALLTYPE NotifyMe::Release()
{
// Decrement the object's internal counter.
ULONG ulRefCount = InterlockedDecrement(&m_ulRefCount);
if(m_ulRefCount == 0)
delete this;
return ulRefCount;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
// i want to listen to these events when the window is not in focus
case WM_SETFONT:
OutputDebugStringA("Font Changed");
break;
case WM_INPUTLANGCHANGE:
OutputDebugStringA("Language Changed - WndProc");
break;
// -- along with paint
//case WM_PAINT:
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
That's not how the ITfLanguageProfileNotifySink work. It is only for YOUR application.
What you need to understand is that every application got its own language just like yours. So when you switch to an app, the language there could be English, and when you switch back to any other app, it could be French, Japanese, Russian, or whatever language you set there.
What you want to achieve is to detect that change for "every" application, right? Then ITfLanguageProfileNotifySink is not the way.
You have to use GetKeyboardLayout instead because it allows you to get the language of any thread. But looks like you only want to detect the "active" thread, right? So you'd also want to pair it with GetWindowThreadProcessId(GetForegroundWindow(), NULL) because this would get the thread of the currently active window for you.
And for that to be integrated correctly with your application, you also have to modify your loop so that it doesn't "wait" for UI messages and block your detection scheme by using PeekMessage().
MSG msg = {};
const int period_ms = 10;
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
static LANGID currentLangId = LOWORD(::GetKeyboardLayout(0));
// Get active window thred
DWORD threadId = ::GetWindowThreadProcessId(::GetForegroundWindow(), NULL);
LANGID newLangId = LOWORD(::GetKeyboardLayout(threadId));
if (newLangId != currentLangId)
{
currentLangId = newLangId;
wchar_t szLangName[256];
GetLocaleInfo(MAKELCID(newLangId, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLangName, 256);
OutputDebugString(szLangName);
OutputDebugString(L"\n");
}
Sleep(period_ms);
}
Note that if the language changed in an app from X -> Y, it'll print 'Y', then when you switch to another app, it'll mostly print 'X' first and then in the next iteration, it'd print 'Y' right after. That's because "most" apps change their language accordingly to the previous window after you open them.
And some other applications would keep their language changes only inside the application no matter what how you change it outside of it. It is different from one app to another. But with that loop, you'll be able to detect all changes.
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;
}
I made a project that creates a DLL. This project uses the WFS methods and they access some hardware (devices) to get information or execute some commands.
In my project, I first open these devices then register them, I later use other methods to get information or execute.
HRESULT extern WINAPI WFSOpen ( LPSTR lpszLogicalName, HAPP hApp, LPSTR lpszAppID, DWORD dwTraceLevel, DWORD dwTimeOut, DWORD dwSrvcVersionsRequired, LPWFSVERSION lpSrvcVersion, LPWFSVERSION lpSPIVersion, LPHSERVICE lphService);
HRESULT extern WINAPI WFSRegister ( HSERVICE hService, DWORD dwEventClass, HWND hWndReg);
As you can see, the WFSRegister requires HWND as a parameter. WFSRegister uses this parameter to send events or messages to it.
My project is not an MFC project and I have no windows. I decided to create a window and assign the correct HWND to WFSRegister. I also created WndProc to get the messages that the WFS methods will send to me later.
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WFS_EXECUTE_EVENT:
cout<<"WFS_EXECUTE_EVENT";
break;
case WFS_SERVICE_EVENT:
cout<<"WFS_EXECUTE_EVENT";
break;
case WFS_USER_EVENT:
cout<<"WFS_USER_EVENT";
break;
case WFS_SYSTEM_EVENT:
cout<<"WFS_SYSTEM_EVENT";
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam );
}
void Init_Window()
{
WNDCLASS Wclass;
Wclass.hInstance = gHinstance;
Wclass.cbClsExtra = 0;
Wclass.cbWndExtra = 0;
Wclass.lpszClassName = TEXT("Device_Manager_Class_Name");
Wclass.lpszMenuName = NULL;
Wclass.lpfnWndProc = WndProc;
Wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
Wclass.hCursor = LoadIcon(NULL, IDC_ARROW);
Wclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
Wclass.style = CS_OWNDC;
if(!RegisterClass(&Wclass))
{
cout<<"Unable to Register Class";
}
ULONG Window_Width;
ULONG Window_Height;
DWORD style;
Window_Width = SCREEN_WIDTH;
Window_Height = SCREEN_HEIGHT;
style = WS_OVERLAPPED|WS_SYSMENU;
gHwnd = CreateWindow(TEXT("Device_Manager_Class_Name")
, TEXT("Device_Manager_Class_Title")
, style
, 0
, 0
, Window_Width
, Window_Height
, GetDesktopWindow()
, NULL
, gHinstance
, NULL);
if(!gHwnd){
cout<<"Unable to create the main window";
}
ShowWindow(gHwnd, SW_SHOW);
UpdateWindow(gHwnd);
SetFocus(gHwnd);
}
Init_Window() Successfully create window, I have no prob here.
When I want to register my device, I call the following code to get the correct HWND:
HWND windows_handle = FindWindow(TEXT("Device_Manager_Class_Name"), 0);
HRESULT result = WFSRegister(wfsRes.hService, WFS_EXECUTE_EVENT || WFS_SERVICE_EVENT || WFS_USER_EVENT || WFS_SYSTEM_EVENT , windows_handle);
result is S_OK(meaning the device registered successfully) and windows_handle refers to the same HWND I created in Init_Window(). For example, both have 0x00100a58 values.
Now I change some property on my devices and I expect to get these message on my WndProc(), but it's not working.
WndProc() working somehow and gets some messages, but not the ones I want (not the ones the devices send to it).
I'm sure the devices send message (as events) because I can see they do by reading their logs.
For example:
2013/09/25 16:46:29 HService : 44 Event WFS_SRVE_SIU_PORT_STATUS Sent for HWND = 330d1c hResult = WFS_SUCCESS
HWND in the log refers to same HWND I created in Init_Window() and windows_handle.
Also, you all got what I want to do. If you have any other solution please feel free to mention it.
I found solution thanks to dear Igor Tandetnik
All i needed to do is to add GetMessage()
MSG msg;
BOOL bRet;
HWND windows_handle = FindWindow(TEXT("Device_Manager_Class_Name"), 0);
while( (bRet = GetMessage( &msg, windows_handle, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg); //<< This line send msg to WndProc()
}
}
I'm new here, I'm more adapted in C# then in C++.
Therefore, I require the assistance of C++ experts for this dilemnia I am currently having.
What is listed down below is just the code snippet that i think is necessary to complete, nevertheless, i believe there are still more to be achieved.
#include "stdafx.h"
#include "winmain.h"
#include "Resource.h"
#include <stdio.h>
#include <CommDlg.h>
#include <windows.h>
OPENFILENAME ofn;
TCHAR szFile[260];
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case ID_FILE_LOADBITMAP:
bBitmap = !bBitmap;
InvalidateRect(hWnd,0,TRUE);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
if(bBitmap)
{
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFile = szFile;
//
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
// use the contents of szFile to initialize itself.
//
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// Display the Open dialog box.
if (GetOpenFileName(&ofn)==TRUE)
hf = CreateFile(ofn.lpstrFile, GENERIC_READ,
0, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
LoadBitmap(__T("F-35C.bmp"), hdc);
EndPaint(hWnd, &ps);
break;
}
As you can see, as i click my case menu: LoadBitmap
It just loads the openfiledialog and choose the file that i want without showing in the Windows, that is all. What i actually want to do is to load the filepath into the LoadBitmap function instead of hardcoding it in the function ("F-35C.bmp).
I also do know that ofn.lpStrFile has the file path, but I am unable to load the Bitmap file despite replacing __T("F-35C.bmp") with ofn.lpStrFile.
As below shows the function of the LoadBitMap Function.
bool LoadBitmap(LPCWSTR szFileName, HDC hWinDC)
{
// Load the bitmap image file
HBITMAP hBitmap;
hBitmap = (HBITMAP)::LoadImage(NULL, szFileName, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE);
// Verify that the image was loaded
if (hBitmap == NULL) {
::MessageBox(NULL, __T("LoadImage Failed"), __T("Error"), MB_OK);
return false;
}
// Create a device context that is compatible with the window
HDC hLocalDC;
hLocalDC = ::CreateCompatibleDC(hWinDC);
// Verify that the device context was created
if (hLocalDC == NULL) {
::MessageBox(NULL, __T("CreateCompatibleDC Failed"), __T("Error"), MB_OK);
return false;
}
// Get the bitmap's parameters and verify the get
BITMAP qBitmap;
int iReturn = GetObject(reinterpret_cast<HGDIOBJ>(hBitmap), sizeof(BITMAP),
reinterpret_cast<LPVOID>(&qBitmap));
if (!iReturn) {
::MessageBox(NULL, __T("GetObject Failed"), __T("Error"), MB_OK);
return false;
}
// Select the loaded bitmap into the device context
HBITMAP hOldBmp = (HBITMAP)::SelectObject(hLocalDC, hBitmap);
if (hOldBmp == NULL) {
::MessageBox(NULL, __T("SelectObject Failed"), __T("Error"), MB_OK);
return false;
}
// Blit the dc which holds the bitmap onto the window's dc
BOOL qRetBlit = ::BitBlt(hWinDC, 0, 0, qBitmap.bmWidth, qBitmap.bmHeight,
hLocalDC, 0, 0, SRCCOPY);
if (!qRetBlit) {
::MessageBox(NULL, __T("Blit Failed"), __T("Error"), MB_OK);
return false;
}
// Unitialize and deallocate resources
::SelectObject(hLocalDC, hOldBmp);
::DeleteDC(hLocalDC);
::DeleteObject(hBitmap);
return true;
}
To add on, I am using Microsoft Visual Studio 2010 Developer Version with Win32 Application ( Not Console ).
You've got your LoadBitmap() parameters backwards. This is from MSDN:
HBITMAP LoadBitmap(
__in HINSTANCE hInstance,
__in LPCTSTR lpBitmapName
);
I'm also fairly certain that you don't need to wrap your filename in __T() macro for function calls.
I see a problem in the fact that you are opening the file right after the GetOpenFileName call, the dwShareMode parameter in CreateFile is set to 0 ( no sharing is allowed ) and you do not close or use the obtained handle( hf ). This will result in the failure of the LoadImage call, because the file is still open with no sharing at the time of the call.
Solution: Either remove the CreateFile call, because it's useless in this code, or close the handle before calling LoadBitmap.
Do not do all of this inside WM_PAINT, which will be called many MANY times during the execution of your program.
Load the bitmap once, and keep the HBITMAP around. The painting code should then start with that HBITMAP.
I am using Netbeans 7.1 to toy around with the AI tutorial I found here.
edit: I am using the GCC compiler.
I've gotten everything working, but I can't seem to get the application to compile and run with the Windows Subsystem... The application appears to be written properly for Windows API, and the executable that came with the source files from that website launches without producing the black console window that my own executable creates.
I've tried adding -mwindows as an option to the linker, and I've tried -Wl,-subsystem,windows. Neither of these have worked for me. I've provided the main.cpp below.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#include "utils.h"
#include "CController.h"
#include "CTimer.h"
#include "resource.h"
#include "CParams.h"
// edited this out, still not working
// #pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
///////////////////////GLOBALS ////////////////////////////////////
char* szApplicationName = "Smart Sweepers v1.0";
char* szWindowClassName = "sweeper";
//The controller class for this simulation
CController* g_pController = NULL;
//create an instance of the parameter class.
CParams g_Params;
//---------------------------- Cleanup ----------------------------------
//
// simply cleans up any memory issues when the application exits
//-----------------------------------------------------------------------
void Cleanup()
{
if (g_pController)
delete g_pController;
}
//-----------------------------------WinProc-----------------------------
//
//-----------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
//these hold the dimensions of the client window area
static int cxClient, cyClient;
//used to create the back buffer
static HDC hdcBackBuffer;
static HBITMAP hBitmap;
static HBITMAP hOldBitmap;
switch(msg)
{
case WM_CREATE:
{
//seed the random number generator
srand((unsigned) time(NULL));
//get the size of the client window
RECT rect;
GetClientRect(hwnd, &rect);
cxClient = rect.right;
cyClient = rect.bottom;
//setup the controller
g_pController = new CController(hwnd);
//create a surface for us to render to(backbuffer)
hdcBackBuffer = CreateCompatibleDC(NULL);
HDC hdc = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hdc,
cxClient,
cyClient);
ReleaseDC(hwnd, hdc);
hOldBitmap = (HBITMAP)SelectObject(hdcBackBuffer, hBitmap);
}
break;
//check key press messages
case WM_KEYUP:
{
switch(wparam)
{
case VK_ESCAPE:
{
PostQuitMessage(0);
}
break;
case 'F':
{
g_pController->FastRenderToggle();
}
break;
//reset the demo
case 'R':
{
if (g_pController)
{
delete g_pController;
}
//setup the new controller
g_pController = new CController(hwnd);
}
break;
}//end WM_KEYUP switch
}
break;
//has the user resized the client area?
case WM_SIZE:
{
cxClient = LOWORD(lparam);
cyClient = HIWORD(lparam);
//resize the backbuffer accordingly
SelectObject(hdcBackBuffer, hOldBitmap);
HDC hdc = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hdc,
cxClient,
cyClient);
ReleaseDC(hwnd, hdc);
hOldBitmap = (HBITMAP)SelectObject(hdcBackBuffer, hBitmap);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
//fill our backbuffer with white
BitBlt(hdcBackBuffer,
0,
0,
cxClient,
cyClient,
NULL,
NULL,
NULL,
WHITENESS);
//render the mines and sweepers
g_pController->Render(hdcBackBuffer);
//now blit backbuffer to front
BitBlt(ps.hdc, 0, 0, cxClient, cyClient, hdcBackBuffer, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
{
SelectObject(hdcBackBuffer, hOldBitmap);
//clean up our backbuffer objects
DeleteDC(hdcBackBuffer);
DeleteObject(hBitmap);
// kill the application, this sends a WM_QUIT message
PostQuitMessage(0);
}
break;
default:break;
}//end switch
// default msg handler
return (DefWindowProc(hwnd, msg, wparam, lparam));
}//end WinProc
//-----------------------------------WinMain-----------------------------------------
// Entry point for our windows application
//-----------------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASSEX winclass;
HWND hwnd;
MSG msg;
// first fill in the window class stucture
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON1));
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground= NULL;
winclass.lpszMenuName = NULL;
winclass.lpszClassName= szWindowClassName;
winclass.hIconSm = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON1));
// register the window class
if (!RegisterClassEx(&winclass))
{
MessageBox(NULL, "Error Registering Class!", "Error", 0);
return 0;
}
// create the window (one that cannot be resized)
if (!(hwnd = CreateWindowEx(NULL,
szWindowClassName,
szApplicationName,
WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
GetSystemMetrics(SM_CXSCREEN)/2 - CParams::WindowWidth/2,
GetSystemMetrics(SM_CYSCREEN)/2 - CParams::WindowHeight/2,
CParams::WindowWidth,
CParams::WindowHeight,
NULL,
NULL,
hinstance,
NULL)))
{
MessageBox(NULL, "Error Creating Window!", "Error", 0);
return 0;
}
//Show the window
ShowWindow(hwnd, SW_SHOWDEFAULT );
UpdateWindow(hwnd);
//create a timer
CTimer timer(CParams::iFramesPerSecond);
//start the timer
timer.Start();
// Enter the message loop
bool bDone = FALSE;
while(!bDone)
{
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
if( msg.message == WM_QUIT )
{
//Stop loop if it's a quit message
bDone = TRUE;
}
else
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
if (timer.ReadyForNextFrame() || g_pController->FastRender())
{
if(!g_pController->Update())
{
//we have a problem, end app
bDone = TRUE;
}
//this will call WM_PAINT which will render our scene
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
}//end while
// Clean up everything and exit the app
Cleanup();
UnregisterClass( szWindowClassName, winclass.hInstance );
return 0;
} // end WinMain
This seems a bit strange, but windows subsystem application uses WinMainCRTStartup as entry point. So the following line looks inconsistent:
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
It probably should be "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" or "/SUBSYSTEM:console /ENTRY:mainCRTStartup"
On the other hand, I never trried to make a windows app with gcc. It may completely ignore this #pragma... Anyway, try to comment it out and see what happens. Generally compiler should be able to select the proper entry point without the compile time parameter anyway.