Last active
May 27, 2020 07:52
-
-
Save alf-p-steinbach/af5805d6bfd0f70ab1ed518286179c0d to your computer and use it in GitHub Desktop.
Demo of Windows API level coding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | |
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> | |
<assemblyIdentity type="win32" name="Demo of Windows API level coding" version="1.0.0.0"/> | |
<application> | |
<windowsSettings> | |
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings" | |
>UTF-8</activeCodePage> | |
</windowsSettings> | |
</application> | |
<dependency> | |
<dependentAssembly> | |
<assemblyIdentity | |
type="win32" | |
name="Microsoft.Windows.Common-Controls" | |
version="6.0.0.0" | |
processorArchitecture="*" | |
publicKeyToken="6595b64144ccf1df" | |
language="*" | |
/> | |
</dependentAssembly> | |
</dependency> | |
</assembly> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Source encoding: UTF-8 with BOM (π is a lowercase Greek "pi"). | |
//----------------------------------- <windows.h> include. Put this in a separate header. | |
#ifdef UNICODE | |
# error "This app is UTF-8. Don't define UNICODE." | |
#endif | |
#define STRICT | |
#define NOMINMAX | |
#define WIN32_LEAN_AND_MEAN // Reduces header size 50%, also fixes winsock2. | |
#include <windows.h> // Main include. | |
#include <windowsx.h> // Message cracker macros + utility wrapper macros. | |
#include <commctrl.h> // InitCommonControlsEx | |
//--------------------------------------------------------------------------------------- | |
#include <stdint.h> // uintptr_t | |
#include <stdlib.h> // EXIT_... | |
#include <string> // std::string | |
#include <stdexcept> // std::(runtime_error, exception) | |
#define FAIL( s ) cppx::fail( std::string() + __func__ + " - " + s ) | |
#define WITH( decl ) if( auto&& [[maybe_unused]] _ = decl; false ) {} else | |
#include <iostream> //!DEBUG | |
using std::clog; | |
namespace cppx { | |
using std::runtime_error, std::string; | |
using C_str = const char*; | |
auto hopefully( const bool e ) -> bool { return e; } | |
auto fail( const string& s ) -> bool { throw runtime_error( s ); } | |
} // namespace cppx | |
namespace winapi_util { | |
using cppx::C_str, cppx::hopefully, cppx::fail; | |
using std::string; | |
inline auto create_default_font() | |
-> HFONT | |
{ | |
// See <url: https://stackoverflow.com/a/6057761/464581> | |
// Get the system message box font information. | |
NONCLIENTMETRICS ncm = { sizeof( ncm ) }; | |
::SystemParametersInfo( SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0 ); | |
// Create a corresponding font. | |
const HFONT result = ::CreateFontIndirect( &ncm.lfMessageFont ); | |
hopefully( result != 0 ) | |
or FAIL( "CreateFontIndirect failed" ); | |
return result; | |
} | |
inline auto the_default_gui_font() | |
-> HFONT | |
{ | |
static const HFONT the_font = create_default_font(); | |
return the_font; | |
} | |
auto create_control( | |
const HWND parent, | |
const int id, | |
const C_str windowclass_name, | |
const POINT position, | |
const SIZE size, | |
const C_str text = "", | |
const DWORD additional_styles = 0 | |
) -> HWND | |
{ | |
const HWND handle = ::CreateWindow( | |
windowclass_name, | |
text, | |
WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | additional_styles, | |
position.x, position.y, size.cx, size.cy, | |
parent, // parent | |
reinterpret_cast<HMENU>( static_cast<uintptr_t>( id ) ), | |
::GetModuleHandle( nullptr ), | |
nullptr // custom params | |
); | |
hopefully( handle != 0 ) | |
or FAIL( "CreateWindow failed" ); | |
SetWindowFont( handle, the_default_gui_font(), false ); // <windowsx.h> macro | |
return handle; | |
} | |
auto text_of( const HWND window ) | |
-> string | |
{ | |
const int n_chars = GetWindowTextLength( window ); | |
if( n_chars == 0 ) { | |
return ""; | |
} | |
string result( n_chars + 1, '\0' ); | |
::GetWindowText( window, &result[0], int( result.size() ) ); | |
result.resize( n_chars ); // Remove trialing zero-byte. | |
return result; | |
} | |
auto text_of_item( const HWND window, const int item_id ) | |
-> string | |
{ | |
const HWND item = ::GetDlgItem( window, item_id ); | |
hopefully( item != 0 ) | |
or FAIL( "GetDlgItem failed" ); | |
return text_of( item ); | |
} | |
namespace common_controls { | |
struct Usage | |
{ | |
Usage() | |
{ | |
INITCOMMONCONTROLSEX params = { sizeof( params ) }; | |
params.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; | |
::InitCommonControlsEx( ¶ms ) | |
or FAIL( "InitCommonControlsEx failed" ); | |
} | |
}; | |
} | |
} // winapi_util | |
namespace app { | |
using cppx::hopefully, cppx::fail; | |
using winapi_util::text_of_item; | |
namespace main_window | |
{ | |
using std::string; | |
using winapi_util::create_control; | |
const auto& windowclass_name = "Main window"; | |
namespace cmd_id { | |
const int generate = 101; | |
} // namespace cmd_id | |
namespace control_id { | |
const int name_edit = 1001; | |
const int age_edit = 1002; | |
const int result_edit = 1003; | |
} // namespace control_id | |
void on_cmd_generate( const HWND window ) | |
{ | |
const string name = text_of_item( window, control_id::name_edit ); | |
const string age = text_of_item( window, control_id::age_edit ); | |
const string text = name + " is " + age + " years old"; | |
clog << "Setting text \"" + text + "\"\n"; | |
::SetDlgItemText( window, control_id::result_edit, text.c_str() ); | |
} | |
void on_command( const HWND window, const int id ) | |
{ | |
clog << "on_command " << id << "\n"; | |
switch( id ) { | |
case IDOK: | |
case cmd_id::generate: return on_cmd_generate( window ); | |
} | |
} | |
void on_wm_close( const HWND window ) | |
{ | |
::DestroyWindow( window ); | |
} | |
void on_wm_command( | |
const HWND window, | |
const int command_id, | |
const HWND control, | |
const UINT notification_code | |
) | |
{ | |
(void) control; | |
if( notification_code > 1 ) { | |
// Handle notification messages. E.g. each edit keypress sends execution here. | |
} else { | |
on_command( window, command_id ); | |
} | |
} | |
auto on_wm_create( const HWND window, CREATESTRUCT* ) | |
-> bool | |
{ | |
create_control( window, 0, "STATIC", {100, 50}, {98, 38}, "Name:" ); | |
create_control( window, control_id::name_edit, "EDIT", {200, 50}, {98, 38}, "", WS_BORDER | WS_TABSTOP ); | |
create_control( window, 0, "STATIC", {100, 90}, {98, 38}, "Age:" ); | |
create_control( window, control_id::age_edit, "EDIT", {200, 90}, {98, 38}, "", WS_BORDER | WS_TABSTOP ); | |
create_control( window, cmd_id::generate, "BUTTON", {150, 140}, {98, 38}, "Generate", BS_DEFPUSHBUTTON | WS_TABSTOP ); | |
const HWND x = create_control( window, control_id::result_edit, "EDIT", {100, 200}, {300, 200} ); | |
::EnableWindow( x, false ); | |
return true; | |
} | |
void on_wm_destroy( const HWND window ) | |
{ | |
(void) window; | |
::PostQuitMessage( EXIT_SUCCESS ); | |
} | |
auto WINAPI message_handler( | |
const HWND window, | |
const UINT message_id, | |
const WPARAM word_param, | |
const LPARAM long_param | |
) -> LRESULT | |
{ | |
#define HANDLE_WM( name, func ) HANDLE_WM_##name( window, word_param, long_param, func ) | |
switch( message_id ) { | |
case WM_CLOSE: return HANDLE_WM( CLOSE, on_wm_close ); | |
case WM_COMMAND: return HANDLE_WM( COMMAND, on_wm_command ); | |
case WM_CREATE: return HANDLE_WM( CREATE, on_wm_create ); | |
case WM_DESTROY: return HANDLE_WM( DESTROY, on_wm_destroy ); | |
} | |
return ::DefDlgProc( window, message_id, word_param, long_param ); | |
#undef HANDLE_WM | |
} | |
void register_windowclass() | |
{ | |
WNDCLASS params = {}; | |
params.style = CS_DBLCLKS; | |
params.lpfnWndProc = &message_handler; | |
params.hInstance = ::GetModuleHandle( nullptr ); | |
params.hIcon = ::LoadIcon( 0, IDI_APPLICATION ); | |
params.hCursor = ::LoadCursor( 0, IDC_ARROW ); | |
params.hbrBackground = reinterpret_cast<HBRUSH>( COLOR_3DFACE + 1 ); | |
params.lpszClassName = windowclass_name; | |
params.cbWndExtra = DLGWINDOWEXTRA; // To be able to use DefDlgProc. | |
clog << "Calling RegisterClass\n"; | |
const ATOM result = ::RegisterClass( ¶ms ); | |
hopefully( result != 0 ) | |
or FAIL( "RegisterClass failed" ); | |
clog << "RegisterClass succeeded\n"; | |
} | |
auto create() | |
-> HWND | |
{ | |
clog << "Calling CreateWindow\n"; | |
const HWND handle = ::CreateWindow( | |
windowclass_name, | |
"Demo of Windows API level coding", | |
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, | |
CW_USEDEFAULT, CW_USEDEFAULT, 640, 400, | |
HWND(), // parent | |
HMENU(), | |
GetModuleHandle( nullptr ), | |
nullptr // custom params | |
); | |
hopefully( handle != 0 ) | |
or FAIL( "CreateWindow failed" ); | |
clog << "CreateWindow succeeded\n"; | |
return handle; | |
} | |
} // namespace main_window | |
void process_messages_for( const HWND window ) | |
{ | |
MSG m; | |
while( ::GetMessage( &m, 0, 0, 0 ) > 0 ) { | |
const bool processed = ::IsDialogMessage( window, &m ); | |
if( not processed ) { | |
::TranslateMessage( &m ); | |
::DispatchMessage( &m ); | |
} | |
} | |
hopefully( m.message == WM_QUIT ) | |
or FAIL( "GetMessage failed" ); | |
} | |
void run() | |
{ | |
WITH( winapi_util::common_controls::Usage() ) { | |
main_window::register_windowclass(); | |
const HWND window = main_window::create(); | |
::ShowWindow( window, SW_SHOWDEFAULT ); | |
process_messages_for( window ); | |
} | |
} | |
} // namespace app | |
auto main() | |
-> int | |
{ | |
using std::exception; | |
using std::cerr; | |
clog << "Starting.\n"; | |
try { | |
app::run(); | |
clog << "Finished.\n"; | |
return EXIT_SUCCESS; | |
} catch( const exception& x ) { | |
::MessageBox( 0, x.what(), "Oops", MB_ICONERROR | MB_SETFOREGROUND ); | |
} | |
clog << "Finished.\n"; | |
return EXIT_FAILURE; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <windows.h> | |
1 RT_MANIFEST "app-manifest.xml" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment