I'm trying to add a clickable URL on my window. I'm using the Win32 API, C++, and Visual Studio.
When I try the example from Microsoft, the link appears as plain text, with the <a>
tags.
They put the Hyperlink in the title bar, but they use WS_CHILD
, which hides it. So I used WS_CAPTION
to show it.
What am I missing? Is it supposed to work only in dialogs?
Here are the related code parts:
wWinMain()
:
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_LINK_CLASS;
InitCommonControlsEx(&icex);
MyRegisterClass2()
:
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc2;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = WC_LINK;
wcex.hIconSm = NULL;
return RegisterClassExW(&wcex);
InitInstance()
:
hWndMain = CreateWindowW(
szWindowClass,
szTitle,
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,
xPos,
yPos,
windowWidth,
windowHeight,
nullptr,
nullptr,
hInstance,
nullptr
);
RECT rectLink = {
30,
30,
300,
40
};
hWndLink = CreateWindowEx(
0,
WC_LINK,
L"link: <A HREF=\"https://www.google.com\">click</A>",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_CAPTION,
rectLink.left,
rectLink.top,
rectLink.right,
rectLink.bottom,
hWndMain,
NULL,
hInstance,
NULL);
if (!hWndLink)
{
return FALSE;
}
ShowWindow(hWndMain, nCmdShow);
ShowWindow(hWndLink, nCmdShow);
UpdateWindow(hWndMain);
UpdateWindow(hWndLink);
WndProc2()
:
LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
It's a little hard to be sure what you have wrong.
The main steps you need to get a functioning SysLink control are:
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
You didn't mention anything about doing this, but without it, InitCommonControlsEx
will fail, and the control won't be created or displayed at all (at least on older versions of Windows--I guess I've never tested what happens if you skip that on a recent version).
InitCommonControlsEx
, usually right in WinMain
, before creating your main window: INITCOMMONCONTROLSEX ex{};
ex.dwSize = sizeof(ex);
ex.dwICC = ICC_LINK_CLASS;
if (!InitCommonControlsEx(&ex)) {
MessageBox(NULL, L"InitCommonControlsEx", L"Registration failed", MB_OK);
PostQuitMessage(0);
}
WndProc
, create the SysLink: case WM_CREATE:
link = CreateWindow(
WC_LINK,
L"<a href=\"www.google.com\">Google</a>",
WS_CHILD | WS_VISIBLE | LWS_USEVISUALSTYLE,
10, 10, 400, 20,
hWnd,
NULL,
hInst,
0);
break;
case WM_NOTIFY: {
NMHDR *nmh = (NMHDR*)lParam;
switch (nmh->code) {
case NM_CLICK:
if (nmh->hwndFrom == link) {
NMLINK* lnkHdr = (NMLINK*)lParam;
MessageBox(NULL, lnkHdr->item.szUrl, L"Here we open", MB_OK);
}
break;
}
break;
}
ComCtl32.lib
Here's a quick demo program that puts all of the above together. The skeleton is just what Visual Studio generates for an empty Windows program, with the above bits added in appropriate places.
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#include "framework.h"
#include "SysLink2.h"
#include <CommCtrl.h>
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// 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);
HWND link;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
INITCOMMONCONTROLSEX ex{};
ex.dwSize = sizeof(ex);
ex.dwICC = ICC_LINK_CLASS | ICC_WIN95_CLASSES;
if (!InitCommonControlsEx(&ex)) {
MessageBox(NULL, L"InitCommonControlsEx", L"Registration failed", MB_OK);
PostQuitMessage(0);
}
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_SYSLINK2, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SYSLINK2));
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;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
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_SYSLINK2));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SYSLINK2);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&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)
{
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;
}
//
// 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)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
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_CREATE:
link = CreateWindow(
WC_LINK,
L"<a href=\"www.google.com\">Google</a>",
WS_CHILD | WS_VISIBLE | LWS_USEVISUALSTYLE,
10, 10, 400, 20,
hWnd,
NULL,
hInst,
0);
break;
case WM_NOTIFY: {
//
NMHDR *nmh = (NMHDR*)lParam;
switch (nmh->code) {
case NM_CLICK:
if (nmh->hwndFrom == link) {
NMLINK* lnkHdr = (NMLINK*)lParam;
MessageBox(NULL, lnkHdr->item.szUrl, L"Here we open", MB_OK);
}
break;
}
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc 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;
}
In action, it looks like this:
When clicked, it pops up a message box like this:
The exact look you get for the window frames and such will depend a bit on how you build it, the OS Version you run it on, and so on. You indicated an interest in Windows XP. I don't have a copy of that handy to test on any more, but I ran this on the oldest I still have around (Windows 7).