Window Subclassing terminates the external application - dll

I am trying to subclass the menu of an external application. I am using the SetWindowLong function to subclass the Windows Procedure, it works well, but after doing what I want, it terminates the application, I dont know why.
This is the function I inject:
DWORD __stdcall AddNewMenuItem(){
HWND hwndAna = NULL;
HMENU hMenu = NULL;
hwndAna = FindWindowA("NameOfTheMainWindow,NULL);
if(hwndAna != INVALID_HANDLE_VALUE){
hMenu = GetMenu(hwndAna);
if(hMenu != NULL){
HMENU hMenuFile = CreatePopupMenu();
AppendMenu(hMenuFile, MF_STRING , (UINT_PTR)ID_Load , L"Show Info");
AppendMenu(hMenuFile, MF_STRING , (UINT_PTR)ID_VWS , L"Go to Site");
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hMenuFile, L"Site");
DrawMenuBar(hwndAna);
OriginalProcedure = SetWindowLongA(hwndAna, GWL_WNDPROC, (LONG)MyProc);
return 1;
}
}
return 0;
}
And this is the My own Windows Procedure:
LRESULT APIENTRY MyProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam){
switch(iMsg)
{
case WM_COMMAND:
switch LOWORD(wParam)
{
case ID_Load:
MessageBoxA(NULL,"this is info", "Information!", MB_OK|MB_ICONINFORMATION);
break;
case ID_VWS:
ShellExecute(NULL, L"Open", L"http://www.gotowebsite.com/", NULL, NULL, SW_SHOWNORMAL);
break;
}
break;
}
return CallWindowProc((WNDPROC)OriginalProcedure, hwnd, iMsg, wParam, lParam);
}
And these are the IDs:
#define ID_VWS 0x8801
#define ID_Load 0x8802
All codes react to the messages, I mean it appends menu successfully, when I click the submenus it shows messagebox etc, but after this, it terminates the application. Somehow it returns WM_CLOSE message after ID messages and I cant manage it. I appreciate your help.

Related

Getting 0xC000041D crash in simple directX12 application

I have a simple Window class and I use a function for WM_PAINT that clears the render target view and presents the final image.so there's not much into it but I get a crash on line FLOAT clearColor[] = { 0.2f, 0.4f, 0.6f, 1.0f }; that says:0xC000041D: An unhandled exception was encountered during a user callback.
here's the window class:
class Win32Window : public WindowsWindow
{
public:
Win32Window(const WindowProps& props);
virtual ~Win32Window();
void OnUpdate() override;
unsigned int GetWidth() const override { return m_Data.Width; }
unsigned int GetHeight() const override { return m_Data.Height; }
void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; }
void SetVSync(bool enabled) override;
bool IsVSync() const override;
virtual void* GetNativeWindow() const override { return m_Window; }
private:
HWND m_Window;
RECT m_WindowRect;
HINSTANCE m_hInst;
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
virtual void Init(const WindowProps& props);
virtual void Shutdown();
};
the definitions:
Win32Window::Win32Window(const WindowProps& props)
{
HZ_PROFILE_FUNCTION();
Init(props);
}
Win32Window::~Win32Window()
{
HZ_PROFILE_FUNCTION();
Shutdown();
}
void Win32Window::Init(const WindowProps& props)
{
m_hInst = GetModuleHandle(0);
m_Data.WideCharacterTitle = props.WideCharacterTitle;
m_Data.Width = props.Width;
m_Data.Height = props.Height;
HZ_CORE_INFO("Creating window {0} ({1}, {2})",props.StringTypeTitle, props.Width, props.Height);
WNDCLASSEXW windowClass = {};
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = &WndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = m_hInst;
windowClass.hIcon = ::LoadIcon(m_hInst, NULL);
windowClass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = m_Data.WideCharacterTitle;
windowClass.hIconSm = ::LoadIcon(m_hInst, NULL);
static ATOM atom = ::RegisterClassExW(&windowClass);
assert(atom > 0);
int screenWidth = ::GetSystemMetrics(SM_CXSCREEN);
int screenHeight = ::GetSystemMetrics(SM_CYSCREEN);
RECT windowRect = { 0, 0, static_cast<LONG>(m_Data.Width), static_cast<LONG>(m_Data.Height) };
::AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
int windowWidth = windowRect.right - windowRect.left;
int windowHeight = windowRect.bottom - windowRect.top;
int windowX = std::max<int>(0, (screenWidth - windowWidth) / 2);
int windowY = std::max<int>(0, (screenHeight - windowHeight) / 2);
m_Window = ::CreateWindowExW(NULL, m_Data.WideCharacterTitle, m_Data.WideCharacterTitle, WS_OVERLAPPEDWINDOW, windowX, windowY, windowWidth, windowHeight, NULL, NULL, m_hInst, this);
assert(m_Window && "Failed to create window");
m_Context = GraphicsContext::Create(m_Window);
m_Context->Init();
::ShowWindow(m_Window, SW_SHOW);
::UpdateWindow(m_Window);
}
LRESULT Win32Window::MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
m_Context->RenderWindow();
break;
case WM_DESTROY:
::PostQuitMessage(0);
break;
default:
return ::DefWindowProcW(hwnd, message, wParam, lParam);
}
}
LRESULT Win32Window::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
Win32Window* instance;
if (message == WM_CREATE)
{
instance = (Win32Window*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
instance->m_Window = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)instance);
}
else
{
instance = (Win32Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (instance)
{
return instance->MainWndProc(hwnd, message, wParam, lParam);
}
return ::DefWindowProcW(hwnd, message, wParam, lParam);
}
void Win32Window::Shutdown()
{
DestroyWindow(m_Window);
m_Window = nullptr;
}
void Win32Window::OnUpdate()
{
m_Context->Update();
}
void Win32Window::SetVSync(bool enabled)
{
m_Data.VSync = enabled;
}
bool Win32Window::IsVSync() const
{
return m_Data.VSync;
}
m_Context is coming from the parent class, And also WindowProps that has some data for initializing the window .definition of m_Context->RenderWindow():
m_CommnadQueue = D3D12Core::Get().GetCommandQueue(D3D12_COMMAND_LIST_TYPE_DIRECT);
auto m_CommandList = m_CommnadQueue->GetCommandList();
m_CurrentBackBufferIndex = m_SwapChain->GetCurrentBackBufferIndex();
auto m_BackBuffer = D3D12Core::Get().m_BackBuffers[m_CurrentBackBufferIndex];
auto m_RTVDescriptorSize = D3D12Core::Get().GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
{
CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_BackBuffer.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
m_CommandList->ResourceBarrier(1, &barrier);
FLOAT clearColor[] = { 0.2f, 0.4f, 0.6f, 1.0f };
CD3DX12_CPU_DESCRIPTOR_HANDLE rtv(m_RTVDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), m_CurrentBackBufferIndex, m_RTVDescriptorSize);
m_CommandList->ClearRenderTargetView(rtv, clearColor, 0, nullptr);
}
{
CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_BackBuffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
m_CommandList->ResourceBarrier(1, &barrier);
m_CommandList->Close();
ID3D12CommandList* const commandLists[] =
{
m_CommandList.Get()
};
D3D12Core::Get().m_FenceValues[m_CurrentBackBufferIndex] = m_CommnadQueue->ExecuteCommandList(m_CommandList);
UINT syncInterval = m_VSync;
UINT presentFlags = m_TearingSupport && !m_VSync ? DXGI_PRESENT_ALLOW_TEARING : 0;
m_SwapChain->Present(syncInterval, presentFlags);
m_CommnadQueue->WaitForFenceValue(D3D12Core::Get().m_FenceValues[m_CurrentBackBufferIndex]);
}
If there is anything else that I should put in question pls tell me.You can shout at me for my dumbness if you want to but help me pls,thx.
EDIT:
debug layer:
#ifdef _DEBUG
ComPtr<ID3D12Debug> debugInterface;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugInterface))))
{
debugInterface->EnableDebugLayer();
}
#endif
WaitForFenceValue after every Present is an extremely slow way to render in DirectX 12. You should generally cue 2-3 frames to the GPU rather than forcing the GPU to stall every frame. It is also highly problematic to have your WndProc block, which is what happens here. Your WndProc should always complete quickly as many hundreds of messages are processed a second for most applications.
Usually Direct3D rendering is not done within WM_PAINT. For a number of examples of common render loops, see GitHub.

Visual-C++ Win32 C++ Application. How do I print variables to the main screen?

I have tried C++11 methods and C methods to convert the string before printing and they either: Returned a string of the same characters or didn't print anything at all. I just wanted to know 3 things really: Is there a fault in the online tutorials and examples? Is there a fault in my interpretation and implementation of the code? What could I do differently to get this working?
// Yu-Gi-Oh! LP Calculator.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "Yu-Gi-Oh! LP Calculator.h"
#define MAX_LOADSTRING 100
#define LP UINT
#define ID CHAR
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Global Life Point Variables
struct Player {
LP Lp;
ID Name[MAX_LOADSTRING];
};
struct Player1, Player2, Player3, Player4;
// Forward declarations of functions included in this code module:
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TEMP: Remove these initializations when preferences are implemented.
Player1.Lp = 8000;
Player2.Lp = 8000;
Player3.Lp = 8000;
Player4.Lp = 8000;
LoadStringA(hInstance, (UINT)"John\0", Player1.Name, MAX_LOADSTRING);
LoadStringA(hInstance, (UINT)"Phil\0", Player2.Name, MAX_LOADSTRING);
LoadStringA(hInstance, NULL, Player3.Name, MAX_LOADSTRING);
LoadStringA(hInstance, NULL, Player4.Name, MAX_LOADSTRING);
// END TEMP
// TODO: Place code here.
// TODO: Load preferences file.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_YUGIOHLPCALCULATOR, szWindowClass, MAX_LOADSTRING);
WNDCLASSEXW 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_YUGIOHLPCALCULATOR));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_YUGIOHLPCALCULATOR);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassExW(&wcex);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_YUGIOHLPCALCULATOR));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT rect;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
GetClientRect(hWnd, &rect);
CHAR b[] = { NULL };
sprintf((char* const)&b, "%s", Player1.Name);
DrawTextA(hdc, b, ARRAYSIZE(b), &rect, DT_SINGLELINE | DT_CENTER | DT_TOP);
//TextOutA(hdc, rect.left, rect.top, s, ARRAYSIZE(Player1.Name));
SelectClipPath(hdc, RGN_AND);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
By changing:
#define ID CHAR
to:
#define ID LPCSTR
and changing:
ID Name[MAX_LOADSTRING];
to:
ID Name;
I made Player1.Name compatible with the type of the 2nd parameter of the DrawTextA function.
I then changed the 3rd parameter to -1 instead of using ARRAYSIZE(). This means that the function checks the size of the string itself? So the function is called like this:
DrawTextA(hdc, Player1.Name, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_TOP);
All I needed to do then was Initialize the string with my values instead of using LoadStringA so I changed:
LoadStringA(hInstance, (UINT)"John\0", Player1.Name, MAX_LOADSTRING);
to:
Player1.Name = "John";

Member modified by method reverts after it exits

I have a window (Windows), and my wndProc is basically the same as the one on the windows guides. However, even though WM_CLOSE gets passed (and I can use if(msg == WM_CLOSE)), I can't seem to set my shouldClose flag. I've confirmed that I still get the event within my processMessage method. So my question is this: what is going on, and how can I make it work?
Edit: I tried storing the window data as a struct instead of a class, and everything works just fine. Ie. All I changed was the type of the class, and a few errors.
class Win32Window {
this(wstring title, int width, int height) {
immutable wstring className = "glass_def_class_name\0";
auto hInstance = GetModuleHandle(null);
WNDCLASSW wc;
wc.lpfnWndProc = &windowProc;
wc.hInstance = hInstance;
wc.lpszClassName = &className[0];
RegisterClassW(&wc);
handle = CreateWindowExW(
0,
&className[0],
&title[0],
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
width, height,
null, null,
hInstance,
cast(void*) this);
ShowWindow(handle, SW_NORMAL);
}
~this() {
DestroyWindow(handle);
}
void processEvents() {
MSG msg;
while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
bool shouldClose;
HWND handle;
private:
LRESULT processMessage(UINT msg, WPARAM wp, LPARAM lp) nothrow {
switch (msg) {
case WM_CLOSE:
shouldClose = true;
return 0;
default:
return DefWindowProc(handle, msg, wp, lp);
}
}
}
private extern (Windows) LRESULT windowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) nothrow {
Win32Window window;
if (msg == WM_CREATE) {
CREATESTRUCT* create = cast(CREATESTRUCT*) lp;
window = cast(Win32Window*) create.lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, cast(LONG_PTR) create.lpCreateParams);
window.handle = hwnd;
}
else {
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
window = cast(Win32Window* ptr);
}
if (window)
return window.processMessage(msg, wp, lp);
else
return DefWindowProc(hwnd, msg, wp, lp);
}
void main()
{
auto window = new Win32Window("test", 1280, 720);
while(window.shouldClose == false) {
window.processEvents();
}
window.destroy();
}
Member modified by method reverts after it exits
this was when you worked with local copy of object, and make modification to this local copy, but not really to object. I not view another explanations
inside windowProc you do next Win32Window window = *(cast(Win32Window*) ptr); - so you make local copy of your initial object state in windowProc and then do all modification to this local copy - of course all it lost when you exit from windowProc correct code must be next:
private LRESULT windowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) nothrow {
Win32Window* window;
if (msg == WM_NCCREATE) {
CREATESTRUCT* create = (CREATESTRUCT*) lp;
window = (Win32Window*) create.lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) create.lpCreateParams);
window.handle = hwnd;
}
else {
window = (Win32Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
return window ? window->processMessage(msg, wp, lp) : DefWindowProc(hwnd, msg, wp, lp);
}
It turns out that you can't actually cast a pointer directly to a reference. An intermediary is necessary (or something to that effect). Thus, the winProc should look something like this:
private extern (Windows) LRESULT windowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) nothrow {
Win32Window window;
if (msg == WM_NCCREATE) {
auto create = cast(CREATESTRUCT*) lp;
window = cast(Win32Window) (cast(void*) create.lpCreateParams);
window.handle = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, cast(LONG_PTR) create.lpCreateParams);
}
else {
window = cast(Win32Window) (cast(void*) GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
return window ? window.processMessage(msg, wp, lp) : DefWindowProc(hwnd, msg, wp, lp);
}
Note the extra cast(void*) before create.lpCreateParams and GetWindowLongPtr(hwnd, GWLP_USERDATA). I'm not entirely sure why this works as opposed to my original code, but it seems to work, and I'll do some more investigating when I have the time.

CreateWindow never triggers WM_CREATE

I have the following bit of code;
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
printf("%d\n", message);
return 0;
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = "oglversionchecksample";
wc.style = CS_OWNDC;
if(!RegisterClassEx(&wc))
return 1;
CreateWindow(wc.lpszClassName, "openglversioncheck", WS_OVERLAPPED, 0, 0, 640, 480, 0, 0, hInstance, 0);
return 0;
}
Calling CreateWindow() triggers message numbers 36 WM_GETMINMAXINFO, 129 WM_NCCREATE, and then 130 WM_NCDESTROY, but message number 1 WM_CREATE is never triggered like it's supposed to be.
What am I doing wrong that's causing WM_CREATE to not be triggered?
It doesn't trigger any message, because message loop is not called yet.
You have to put this after CreateWindow
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Also, WndProc should not simply return zero. It should return the following:
return DefWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Visual Studio or other IDE's can create C++ -> Win32 project basic project, it sets up all the code.
The created window has no time to receive the messages, it is closed immediately. You should call GetMessage in loop to keep the window alive.
// Step 3: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
See this example: http://www.winprog.org/tutorial/simple_window.html

Win 32 API set window minimum size to keep text fully visible

I am currently a beginner in win 32 API and am making a sample program. The problem I face is I can't find a proper way to set the window's minimum size in order for the text inside it (centered) to be visible when the window gets at its minimum. The text is fully visible in all the other cases, except when I reach a certain "critical" size, after which the text is either seen partially or not seen at all.
I found how to change the width and height of the window, as shown here:
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_GETMINMAXINFO:
((MINMAXINFO*)lParam)->ptMinTrackSize.x =100; //apply custom min width
((MINMAXINFO*)lParam)->ptMinTrackSize.y =100; //apply custom min height
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint (hwnd, &ps);
GetClientRect(hwnd,&rc);
DrawText (hdc, TEXT ("Sample text here"), -1, &rc,DT_SINGLELINE | DT_CENTER | DT_VCENTER ) ;
EndPaint (hwnd, &ps);
}
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
I think these lines should be changed, but I don't know what change needs to be made.
((MINMAXINFO*)lParam)->ptMinTrackSize.x =100; //apply custom min width
((MINMAXINFO*)lParam)->ptMinTrackSize.y =100; //apply custom min height
My idea is that instead explicit float values which are assigned to x and y members of the POINT structure, a function should be called, that determines the text's minimum width and height.
Any help would be appreciated.
Below is the full code of this program.
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
#include <tchar.h>
#include <windows.h>
#include <stdio.h>
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");
RECT rc;
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW; /* Catch double-clicks */
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; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if (!RegisterClassEx (&wincl))
return 0;
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
_T("Code::Blocks Template Windows App"),
/* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
544, /* The programs width */
375, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
ShowWindow (hwnd, nCmdShow);
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_GETMINMAXINFO:
//window size/position is going to change
//apply custom min width/height 350,50
SIZE sz;
HDC hdc = GetDC( hwnd );
TCHAR* myString = TEXT("Sample Text!");
HFONT oldFont, myFont; //create your own font
//select your font into device context
oldFont = (HFONT)SelectObject( hdc, myFont );
//get font dimensions
GetTextExtentPoint32( hdc, myString, _tcslen( myString ), &sz );
((MINMAXINFO*)lParam)->ptMinTrackSize.x = sz.cx;
((MINMAXINFO*)lParam)->ptMinTrackSize.y = sz.cy;
//cleanup
SelectObject( hdc, oldFont );
DeleteObject( myFont );
ReleaseDC( hwnd, hdc );
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint (hwnd, &ps);
GetClientRect(hwnd,&rc);
DrawText (hdc, TEXT("Sample Text!"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_WORDBREAK ) ;
EndPaint (hwnd, &ps);
}
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
I apologize for the poor indentation.
I think that GetTextExtentPoint32 is what you want so you can calculate the text size and length. Here is the example showing how to use it.
Just in case your text gets bigger dimensions than your screen, you could add DT_WORDBREAK flag to your DrawText call-see the documentation.
You will need to create font and select it in your device context and CreateFontIndirect can help you-pay attention to the members of the LOGFONT structure so you can create font you wish. Here is one example of doing it.
Try something like this:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint (hwnd, &ps);
//create your own font
HFONT oldFont, myFont;
// create logical font for menu
LOGFONT lf = { sizeof(lf) };
_tcscpy_s( lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Microsoft Sans Serif" );
lf.lfHeight = -MulDiv( 12, GetDeviceCaps( hdc, LOGPIXELSY), 72);
lf.lfWeight = FW_HEAVY;
lf.lfItalic = FALSE;
lf.lfUnderline = FALSE;
myFont = CreateFontIndirect(&lf);
//select your font into device context
oldFont = (HFONT)SelectObject( hdc, myFont );
GetClientRect(hwnd,&rc);
DrawText (hdc, TEXT("Sample Text!"), -1, &rc,
DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_WORDBREAK ) ;
//cleanup
SelectObject( hdc, oldFont );
DeleteObject( myFont );
EndPaint (hwnd, &ps);
}
return 0L;
case WM_GETMINMAXINFO:
{
SIZE sz;
HDC hdc = GetDC( hwnd );
LPTSTR myString = TEXT( "Sample text here" );
//create your own font
HFONT oldFont, myFont;
// create logical font for menu
LOGFONT lf = { sizeof(lf) };
_tcscpy_s( lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Microsoft Sans Serif" );
lf.lfHeight = -MulDiv( 12, GetDeviceCaps( hdc, LOGPIXELSY), 72);
lf.lfWeight = FW_HEAVY;
lf.lfItalic = FALSE;
lf.lfUnderline = FALSE;
myFont = CreateFontIndirect(&lf);
//select your font into device context
oldFont = (HFONT)SelectObject( hdc, myFont );
//get font dimensions
GetTextExtentPoint32( hdc, myString, _tcslen( myString ), &sz );
((MINMAXINFO*)lParam)->ptMinTrackSize.x = sz.cx;
((MINMAXINFO*)lParam)->ptMinTrackSize.y = sz.cy;
//cleanup
SelectObject( hdc, oldFont );
DeleteObject( myFont );
ReleaseDC( hwnd, hdc );
}
break;
If you have further questions leave a comment.
Best regards.