I'm trying to create a screen locker, similar to Windows UAC, like shown in this tutorial. I am having difficulty creating the background maximized window, with reduced light, and a screenshot of the desktop.
Here is all the code I have tried so far:
program Project1;
uses
Winapi.Windows, Winapi.Messages, Vcl.Graphics, Vcl.Forms;
function MainWndProc(hWindow: HWND; Msg: UINT; wParam: wParam;
lParam: lParam): LRESULT;
var
ps: TPaintStruct;
ScreenDC: HDC;
ScreenHandle: HWnd;
ScreenBitmap: TBitmap;
begin
Result := 0;
case Msg of
WM_PAINT:
begin
BeginPaint(hWindow, ps);
ScreenHandle := GetDeskTopWindow;
ScreenDC := GetDC(ScreenHandle);
try
ScreenBitmap := TBitMap.Create;
try
ScreenBitmap.Width := Screen.Width;
ScreenBitmap.Height := Screen.Height;
BitBlt(ScreenBitmap.Canvas.Handle, 0, 0,
Screen.Width, Screen.Height, ScreenDC, 0, 0, SRCCOPY);
finally
ScreenBitmap.Free
end
finally
ReleaseDC(ScreenHandle, ScreenDC)
end;
EndPaint(hWindow, ps);
end;
WM_DESTROY: PostQuitMessage(0);
else
begin
Result := DefWindowProc(hWindow, Msg, wParam, lParam);
Exit;
end;
end;
end;
var
wc: TWndClass;
hWindow: HWND;
Msg: TMsg;
begin
wc.lpszClassName := 'App';
wc.lpfnWndProc := #MainWndProc;
wc.Style := CS_VREDRAW or CS_HREDRAW;
wc.hInstance := hInstance;
wc.hIcon := LoadIcon(0, IDI_APPLICATION);
wc.hCursor := LoadCursor(0, IDC_ARROW);
wc.hbrBackground := (COLOR_WINDOW + 1);
wc.lpszMenuName := nil;
wc.cbClsExtra := 0;
wc.cbWndExtra := 0;
RegisterClass(wc);
hWindow := CreateWindowEx(WS_EX_CONTROLPARENT or WS_EX_WINDOWEDGE,
'AppClass',
'CREATE_WND',
WS_VISIBLE or WS_CLIPSIBLINGS or
WS_CLIPCHILDREN or WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0,
400, 300,
0,
0,
hInstance,
nil);
ShowWindow(hWindow, CmdShow);
UpDateWindow(hWindow);
while GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
Halt(Msg.wParam);
end.
creating the background maximized window - very easy
CreateWindowExW(WS_EX_TOPMOST, L"myclassname", 0, WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), HWND_DESKTOP, 0, (HINSTANCE)&__ImageBase, this);
on WM_NCCREATE we need capture screen
//HDC _hDC; - class member
//HGDIOBJ _o;
BOOL Capture()
{
BOOL fOk = FALSE;
if (HDC hdc = GetDC(HWND_DESKTOP))
{
if (_hDC = CreateCompatibleDC(hdc))
{
int cx = GetSystemMetrics(SM_CXSCREEN), cy = GetSystemMetrics(SM_CYSCREEN);
if (HBITMAP hbmp = CreateCompatibleBitmap(hdc, cx, cy))
{
_o = SelectObject(_hDC, hbmp);
static BLENDFUNCTION bf = { AC_SRC_OVER, 0, 0x80, AC_SRC_ALPHA };// 0x80 -> 50%
fOk = AlphaBlend(_hDC, 0, 0, cx, cy, hdc, 0, 0, cx, cy, bf);
}
}
ReleaseDC(HWND_DESKTOP, hdc);
}
return fOk;
}
WindowProc can be enough simply
LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCDESTROY:
if (_hDC)
{
if (_o)
{
DeleteObject(SelectObject(_hDC, _o));
}
DeleteDC(_hDC);
}
//Release();
break;
case WM_NCCREATE:
//AddRef();
if (!Capture()) return FALSE;
break;
case WM_CLOSE:
return 0;// prevent from close
case WM_ERASEBKGND:
return TRUE;
case WM_PAINT:
{
PAINTSTRUCT ps;
if (BeginPaint(hwnd, &ps))
{
BitBlt(ps.hdc,
ps.rcPaint.left, ps.rcPaint.top,
ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
_hDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
EndPaint(hwnd, &ps);
}
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
static LRESULT CALLBACK _WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CScreenLocker* This;
if (uMsg == WM_NCCREATE)
{
This = reinterpret_cast<CScreenLocker*>(reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams);
SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(This));
}
else
{
This = reinterpret_cast<CScreenLocker*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
}
if (This)
{
//This->AddRef();
LRESULT r = This->WindowProc(hwnd, uMsg, wParam, lParam);
//This->Release();
return r;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
then, after you create background maximized window - hwnd, need create dialog with hwnd as parent. say
ShowWindow(hwnd, SW_SHOW);
MessageBoxW(hwnd,...);
DestroyWindow(hwnd);
Related
I'm trying to use cuda hardware accelerated video decoding, loading from a .mp4 file, according to the code bellow, but getting the image with shadows
Specification:
FFMPEG 4.3
Delphi 10.3
NVIDIA GeForce RTX 3050 Laptop GPU 4Gb DDR6
Intel Core i5 11400H 16 Gb RAM
unit UVideoDecoding;
interface
uses
Vcl.Graphics, Winapi.Windows,
System.SysUtils, System.Classes,
libavcodec, libavdevice,
libavfilter, libswresample, libavformat, libavutil,
libswscale;
type
TDecoder = class
protected
fFormatCtx: PAVFormatContext;
fCodec: PAVCodec;
fCodecParams, fLocalCodecParams: PAVCodecParameters;
fCodecCtx: libavcodec.PAVCodecContext;
fPacket: PAVPacket;
fFrameIn, fFrameOut: PAVFrame;
fScaler: PSwsContext;
fVideoStreamIndex: Integer;
fHWPixelFormat: AVPixelFormat;
fOutputWidth: Integer;
fOutputHeight: Integer;
function DecodeVideoPacket: Integer;
public
function Load(pFileName: string): Integer;
end;
implementation
function TDecoder.Load(pFileName: string): Integer;
var
vCount: Integer;
vLocalCodec: PAVCodec;
vFileName: AnsiString;
vType: AVHWDeviceType;
vDecPackResponse: Integer;
begin
vType := av_hwdevice_find_type_by_name('cuda');
{vType = AV_HWDEVICE_TYPE_CUDA}
fFormatCtx := avformat_alloc_context;
vFileName := AnsiString(pFileName);
avformat_open_input(fFormatCtx, PAnsiChar(vFileName), nil, nil);
avformat_find_stream_info(fFormatCtx, nil);
for vCount := 0 to fFormatCtx.nb_streams - 1 do
begin
fLocalCodecParams := fFormatCtx.streams[vCount].codecpar;
case fLocalCodecParams.codec_id of
AVCodecID.AV_CODEC_ID_H264: vLocalCodec := avcodec_find_decoder_by_name('h264_cuvid');
AVCodecID.AV_CODEC_ID_HEVC: vLocalCodec := avcodec_find_decoder_by_name('hevc_cuvid');
AVCodecID.AV_CODEC_ID_MJPEG: vLocalCodec := avcodec_find_decoder_by_name('mjpeg_cuvid');
AVCodecID.AV_CODEC_ID_MPEG4: vLocalCodec := avcodec_find_decoder_by_name('mpeg4_cuvid');
else
vLocalCodec := avcodec_find_decoder(fLocalCodecParams.codec_id);
end;
if fLocalCodecParams.codec_type = AVMEDIA_TYPE_VIDEO then
begin
fVideoStreamIndex := vCount;
fCodec := vLocalCodec;
fCodecParams := fLocalCodecParams;
end;
end;
{fCodec.name = h264_cuvid}
fCodecCtx := avcodec_alloc_context3(fCodec);
var vHWDeviceCtx: PAVBufferRef;
try
if av_hwdevice_ctx_create(vHWDeviceCtx, vType, nil, nil, 0) >= 0 then
fCodecCtx.hw_device_ctx := av_buffer_ref(vHWDeviceCtx);
finally
av_buffer_unref(vHWDeviceCtx);
end;
avcodec_open2(fCodecCtx, fCodec, nil);
{
fCodecCtx.codec_id = AV_CODEC_ID_H264
fCodecCtx.pix_fmt = AV_PIX_FMT_MMAL
}
fFrameIn := av_frame_alloc;
fFrameOut := av_frame_alloc;
fFrameOut.format := Integer(AV_PIX_FMT_BGRA);
fFrameOut.width := 640;
fFrameOut.height := 480;
//On getting sws_context I've tried srcFormat = AV_PIX_FMT_MMAL but the result was nil
fScaler := sws_getContext(fCodecCtx.Width, fCodecCtx.Height, AV_PIX_FMT_NV12{fCodecCtx.pix_fmt},
fOutputWidth, fOutputHeight, AV_PIX_FMT_BGRA, SWS_BILINEAR, nil, nil, nil);
fPacket := av_packet_alloc;
while (av_read_frame(fFormatCtx, fPacket) >= 0) do
begin
if fPacket.stream_index = fVideoStreamIndex then
begin
vDecPackResponse := DecodeVideoPacket;
av_packet_unref(fPacket);
if vDecPackResponse < 0 then
Break;
end
end;
Exit(0);
end;
function TDecoder.DecodeVideoPacket: Integer;
var
vResponse: Integer;
vBmp: Vcl.Graphics.TBitmap;
vScaleResult, vSize: Integer;
vBuffer: PByte;
vSWFrame, vTMPFrame: pAVFrame;
begin
Result := 0;
vResponse := avcodec_send_packet(fCodecCtx, fPacket);
if vResponse < 0 then
Exit(vResponse);
while (vResponse >= 0) do
begin
vResponse := avcodec_receive_frame(fCodecCtx, fFrameIn);
if (vResponse = AVERROR_EAGAIN) or (vResponse = AVERROR_EOF) then
Break
else if vResponse < 0 then
Exit(vResponse);
if vResponse >= 0 then
begin
vSWFrame := av_frame_alloc;
if av_hwframe_transfer_data(vSWFrame, fFrameIn, 0) >= 0 then
vTMPFrame := vSWFrame
else
vTMPFrame := fFrameIn;
vSize := av_image_get_buffer_size(AVPixelFormat(vTMPFrame.format), vTMPFrame.width, vTMPFrame.height, 1);
vBuffer := av_malloc(vSize);
if Assigned(vBuffer) then
av_image_copy_to_buffer(vBuffer, vSize, #vTMPFrame.data, #vTMPFrame.linesize, AVPixelFormat(vTMPFrame.format), vTMPFrame.width, vTMPFrame.height, 1);
MoveMemory(vTMPFrame.data[0], vBuffer, vSize);
vTMPFrame.data[0] := vTMPFrame.data[0] + vTMPFrame.linesize[0] * (fCodecCtx.height - 1);
vTMPFrame.linesize[0] := vTMPFrame.linesize[0] * -1;
vTMPFrame.data[1] := vTMPFrame.data[1] + vTMPFrame.linesize[1] * (fCodecCtx.height div 2 - 1);
vTMPFrame.linesize[1] := vTMPFrame.linesize[1] * -1;
vTMPFrame.data[2] := vTMPFrame.data[2] + vTMPFrame.linesize[2] * (fCodecCtx.height div 2 - 1);
vTMPFrame.linesize[2] := vTMPFrame.linesize[2] * -1;
vScaleResult := sws_scale(fScaler, #vTMPFrame.Data, #vTMPFrame.Linesize, 0,
fCodecCtx.Height, #fFrameOut.data, #fFrameOut.Linesize);
if vScaleResult <= 0 then
begin
Break;
end;
vBmp := Vcl.Graphics.TBitmap.Create;
try
vBmp.Height := fOutputHeight;
vBmp.Width := fOutputWidth;
vBmp.PixelFormat := TPixelFormat.pf32bit;
MoveMemory(PByte(vBmp.ScanLine[vBmp.Height -1]),
fFrameOut.data[0], fOutputHeight * fFrameOut.linesize[0]);
//Renders BMP on main thread
// if Assigned(fOnBitmap) then
// fOnBitmap(vBmp);
finally
av_frame_unref(vSWFrame);
av_frame_free(vTMPFrame);
av_freep(#vBuffer);
FreeAndNil(vBmp);
Result := 1;
end;
end;
end;
end;
end.
I've tried using different pixelformats but none of them properly decoded the video.
I have been doing a lot of work lately with taking screenshots (for a remote desktop system) and just stumbled across a problem while I'm trying to implement support for multiple monitors. While taking the screenshot is OK, the method I'm using to draw the cursor only presumes 1 screen. If I position the pointer on an additional screen (when taking a screenshot of that additional screen), the cursor does NOT show. I move the pointer to the main screen and it shows (of course in the wrong spot because it's the wrong screen).
My code is entirely below.
program Test;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows,
vcl.Graphics,
SysUtils;
function GetCursorInfo2: TCursorInfo;
var
hWindow: HWND;
pt: TPoint;
dwThreadID, dwCurrentThreadID: DWORD;
begin
Result.hCursor := 0;
ZeroMemory(#Result, SizeOf(Result));
if GetCursorPos(pt) then
begin
Result.ptScreenPos := pt;
hWindow := WindowFromPoint(pt);
if IsWindow(hWindow) then
begin
dwThreadID := GetWindowThreadProcessId(hWindow, nil);
dwCurrentThreadID := GetCurrentThreadId;
if (dwCurrentThreadID <> dwThreadID) then
begin
if AttachThreadInput(dwCurrentThreadID, dwThreadID, True) then
begin
Result.hCursor := GetCursor;
AttachThreadInput(dwCurrentThreadID, dwThreadID, False);
end;
end
else
Result.hCursor := GetCursor;
end;
end;
end;
procedure TakeScreenshot(var Bmp: TBitmap; WndHdc: HDC; Width, Height, Left, Top: Integer);
const
CAPTUREBLT = $40000000;
var
DesktopCanvas: TCanvas;
MyCursor: TIcon;
CursorInfo: TCursorInfo;
IconInfo: TIconInfo;
DC: HDC;
begin
DC := GetDC(WndHdc);
try
if (DC = 0) then
Exit;
Bmp.Width := Width;
Bmp.Height := Height;
DesktopCanvas := TCanvas.Create;
try
DesktopCanvas.Handle := DC;
BitBlt(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, DesktopCanvas.Handle, Left, Top, SRCCOPY or CAPTUREBLT);
MyCursor := TIcon.Create;
try
CursorInfo := GetCursorInfo2;
if CursorInfo.hCursor <> 0 then
begin
MyCursor.Handle := CursorInfo.hCursor;
GetIconInfo(CursorInfo.hCursor, IconInfo);
Bmp.Canvas.Draw(CursorInfo.ptScreenPos.X - IconInfo.xHotspot, CursorInfo.ptScreenPos.Y - IconInfo.yHotspot, MyCursor);
end;
finally
MyCursor.ReleaseHandle;
MyCursor.Free;
end;
finally
DesktopCanvas.Free;
end;
finally
if (DC <> 0) then
ReleaseDC(0, DC);
end;
end;
function EnumDisplayMonitors(dc: HDC; rect: PRect; EnumProc: pointer; lData: Integer): Boolean; stdcall; external user32 name 'EnumDisplayMonitors';
type
TMonInfo = record
h: THandle;
DC: HDC;
R: TRect;
end;
var
MonList: array of TMonInfo;
function MonitorEnumProc(hMonitor: THandle; hdcMonitor: HDC; lprcMonitor: DWORD; dwData: Integer): Boolean; stdcall;
var
I, Width, Height, Left, Top: Integer;
Bmp: TBitmap;
begin
I := High(MonList) + 1;
SetLength(MonList, I + 1);
MonList[I].h := hMonitor;
MonList[I].DC := hdcMonitor;
MonList[I].R := PRect(lprcMonitor)^;
Left := PRect(lprcMonitor)^.Left;
Top := PRect(lprcMonitor)^.Top;
Width := PRect(lprcMonitor)^.Width;
Height := PRect(lprcMonitor)^.Height;
Bmp := TBitmap.Create;
try
TakeScreenshot(Bmp, hdcMonitor, Width, Height, Left, Top);
Bmp.SaveToFile('C:\Screen' + IntToStr(I + 1) + '.bmp');
finally
Bmp.Free;
end;
Result := True;
end;
procedure Main;
var
S: string;
I: Integer;
begin
Writeln('Number of monitors: ' + IntToStr(High(MonList) + 1) + #13#10);
Writeln('-----------------');
for I := 0 to High(MonList) do
with MonList[I] do
begin
S := #13#10 + 'Handle: ' + IntToStr(h) + #13#10 + 'Dc: ' + IntToStr(DC) + #13#10 + 'Size: ' + IntToStr(R.Right) + 'x' + IntToStr(R.Bottom) + #13#10;
Writeln(S);
Writeln('-----------------');
end;
end;
begin
try
EnumDisplayMonitors(0, nil, Addr(MonitorEnumProc), 0);
Main;
Writeln(#13#10 + 'Connected: ' + IntToStr(GetSystemMetrics(SM_CMONITORS)) + #13#10);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
The issue was that the cursor coordinates you get from GetCursorInfo2 are not the correct coordinates relative to your Bitmap.
First, determine whether the cursor point is in the lprcMonitor, you ccould use PtInRect, and then use DrawIcon to draw the hcursor into the bitmap, if it returns true.
Here is an C++ sample convert from your code(since I am not familiar with delphi):
#include <windows.h>
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
using namespace std;
#pragma comment(lib, "Gdiplus.lib")
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
//HCURSOR GetCursorInfo2(POINT * pt)
//{
// POINT p = { 0 };
// HWND hWindow = NULL;
// HCURSOR hCursor = NULL;
// if (GetCursorPos(&p))
// {
// pt->x = p.x;
// pt->y = p.y;
// hWindow = WindowFromPoint(*pt);
// if (IsWindow(hWindow))
// {
// DWORD dwThreadID = GetWindowThreadProcessId(hWindow, NULL);
// DWORD dwCurrentThreadID = GetCurrentThreadId();
// if (dwCurrentThreadID != dwThreadID)
// {
// if (AttachThreadInput(dwCurrentThreadID, dwThreadID, TRUE))
// {
// hCursor = GetCursor();
// AttachThreadInput(dwCurrentThreadID, dwThreadID, FALSE);
// }
// }
// }
// }
// return hCursor;
//}
void TakeScreenshot(HDC hdcbmp, HDC WndHdc, int Width, int Height, int Left, int Top)
{
HDC hdc = GetDC(NULL);
if (hdc == 0) exit(-1);
BitBlt(hdcbmp, 0, 0, Width, Height, hdc, Left, Top, SRCCOPY | CAPTUREBLT);
CURSORINFO cursorinfo = { 0 };
cursorinfo.cbSize = sizeof(CURSORINFO);
if (GetCursorInfo(&cursorinfo))
{
RECT rc = { Left ,Top,Left + Width ,Top + Height };
if (PtInRect(&rc, cursorinfo.ptScreenPos))
{
DrawIcon(hdcbmp, cursorinfo.ptScreenPos.x - Left, cursorinfo.ptScreenPos.y - Top, cursorinfo.hCursor);
}
}
/*ICONINFO IconInfo = { 0 };
GetIconInfo(hCursor, &IconInfo);*/
}
BOOL CALLBACK Monitorenumproc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
static int count = 0;
int Left = lprcMonitor->left;
int Top = lprcMonitor->top;
int Width = lprcMonitor->right - lprcMonitor->left;
int Height = lprcMonitor->bottom - lprcMonitor->top;
HDC dev = GetDC(NULL);
HDC CaptureDC = CreateCompatibleDC(dev);
HBITMAP CaptureBitmap = CreateCompatibleBitmap(dev, Width, Height);
HGDIOBJ old_obj = SelectObject(CaptureDC, CaptureBitmap);
TakeScreenshot(CaptureDC, dev, Width, Height, Left, Top);
Gdiplus::Bitmap bitmap(CaptureBitmap, NULL);
CLSID pngClsid;
GetEncoderClsid(L"image/bmp", &pngClsid);
wstring BmpNameString = L"C:\\screen";
BmpNameString = BmpNameString + std::to_wstring(count) + L".bmp";
count++;
bitmap.Save(BmpNameString.c_str(), &pngClsid, NULL);
SelectObject(CaptureDC, old_obj);
DeleteDC(CaptureDC);
ReleaseDC(NULL, dev);
DeleteObject(CaptureBitmap);
return TRUE;
}
int main(void)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
EnumDisplayMonitors(0, NULL, Monitorenumproc, 0);
GdiplusShutdown(gdiplusToken);
return 0;
}
And attention to these lines in function TakeScreenshot:
CURSORINFO cursorinfo = { 0 };
cursorinfo.cbSize = sizeof(CURSORINFO);
if (GetCursorInfo(&cursorinfo))
{
RECT rc = { Left ,Top,Left + Width ,Top + Height };
if (PtInRect(&rc, cursorinfo.ptScreenPos))
{
DrawIcon(hdcbmp, cursorinfo.ptScreenPos.x - Left, cursorinfo.ptScreenPos.y - Top, cursorinfo.hCursor);
}
}
When doing "real time" tracking, the header control occasionally leaves artifacts behind, as the image below shows:
The first two images are from the attached program. The third image (without the blue coloring) is from Windows Explorer.
To get the artifacts, simply drag the separator off the right side of program's window right edge and bring it back quickly into view. It may take a couple of tries, depending on how quickly you bring the separator back into the window.
Windows Explorer avoids the problem by having the header not paint that black vertical bar when dragging.
EDIT: As Sertac below pointed out, Windows Explorer uses a different control, which is why it does not exhibit the problem.
I have two (2) questions:
How does one tell the header control not to paint that vertical black bar? I couldn't find anything in the documentation on that.
If getting rid of the black bar is not possible without "owner-drawing" the header, is there some way to prevent the artifact from appearing?
The program I am using to test the header control is below.
{$LONGSTRINGS OFF}
{$WRITEABLECONST ON}
{$ifdef WIN32} { tell Windows we want v6 of commctrl }
{$R Manifest32.res}
{$endif}
{$ifdef WIN64}
{$R Manifest64.res}
{$endif}
program _Header_Track;
uses Windows, Messages, CommCtrl;
const
ProgramName = 'Header_Track';
{-----------------------------------------------------------------------------}
{$ifdef VER90} { Delphi 2.0 }
type
ptrint = longint;
ptruint = dword;
const
ICC_WIN95_CLASSES = $000000FF; { missing in Delphi 2 }
type
TINITCOMMONCONTROLSEX = packed record
dwSize : DWORD;
dwICC : DWORD;
end;
PINITCOMMONCONTROLSEX = ^TINITCOMMONCONTROLSEX;
function InitCommonControlsEx(var InitClasses : TINITCOMMONCONTROLSEX)
: BOOL; stdcall; external comctl32;
{$endif}
{$ifdef VER90}
// for Delphi 2.0 define GetWindowLongPtr and SetWindowLongPtr as synonyms of
// GetWindowLong and SetWindowLong respectively.
function GetWindowLongPtr(Wnd : HWND;
Index : ptrint)
: ptruint; stdcall; external 'user32' name 'GetWindowLongA';
function SetWindowLongPtr(Wnd : HWND;
Index : ptrint;
NewLong : DWORD)
: ptruint; stdcall; external 'user32' name 'SetWindowLongA';
function GetClassLongPtr(Wnd : HWND;
Index : ptrint)
: ptruint; stdcall; external 'user32' name 'GetClassLongA';
function SetClassLongPtr(Wnd : HWND;
Index : ptrint;
NewLong : ptruint)
: ptruint; stdcall; external 'user32' name 'SetClassLongA';
{$endif}
{$ifdef FPC}
{ make the FPC definitions match Delphi's }
type
THDLAYOUT = record
Rect : PRECT;
WindowPos : PWINDOWPOS;
end;
PHDLAYOUT = ^THDLAYOUT;
function Header_Layout(Wnd : HWND; Layout : PHDLAYOUT) : WINBOOL; inline;
begin
Header_Layout := WINBOOL(SendMessage(Wnd, HDM_LAYOUT, 0, ptruint(Layout)));
end;
{$endif}
{-----------------------------------------------------------------------------}
function WndProc (Wnd : HWND; Msg : UINT; wParam, lParam : ptrint)
: ptrint; stdcall;
{ main application/window handler function }
const
HEADER_ID = 1000;
HEADER_ITEMS_WIDTH = 100;
Header : HWND = 0;
HeaderText : packed array[0..2] of pchar =
(
'Name',
'Date modified',
'Type'
);
var
ControlsInit : TINITCOMMONCONTROLSEX;
HeaderPos : TWINDOWPOS;
HeaderRect : TRECT;
HeaderNotification : PHDNOTIFY absolute lParam; { note overlay on lParam }
HeaderLayout : THDLAYOUT;
HeaderItem : THDITEM;
ClientRect : TRECT;
Style : ptruint;
i : integer;
begin
WndProc := 0;
case Msg of
WM_CREATE:
begin
{ initialize the common controls library }
with ControlsInit do
begin
dwSize := sizeof(ControlsInit);
dwICC := ICC_WIN95_CLASSES; { includes headers }
end;
InitCommonControlsEx(ControlsInit);
{ create the header control }
Header := CreateWindowEx(0,
WC_HEADER, { class name }
nil, { caption }
HDS_BUTTONS or
WS_CHILD or
WS_VISIBLE or
WS_CLIPCHILDREN or
WS_CLIPSIBLINGS,
0, { at parent x = 0 }
0, { y = 0 }
0, { width }
0, { height }
Wnd, { parent }
HEADER_ID, { child id }
hInstance,
nil);
if Header = 0 then
begin
MessageBox(Wnd,
'Couldn''t create a header',
'Main Window - WM_CREATE',
MB_ICONERROR or MB_OK);
WndProc := -1; { abort window creation }
exit;
end;
{ remove the annoying double click behavior of the header buttons }
Style := GetClassLongPtr(Header, GCL_STYLE);
Style := Style and (not CS_DBLCLKS);
SetClassLongPtr(Header, GCL_STYLE, Style);
{ tell the header which font to use }
SendMessage(Header, WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 0);
{ insert the column header in the header control }
with HeaderItem do
for i := low(HeaderText) to high(HeaderText) do
begin
mask := HDI_FORMAT or HDI_TEXT or HDI_WIDTH;
pszText := HeaderText[i];
fmt := HDF_STRING;
cxy := HEADER_ITEMS_WIDTH; { width }
Header_InsertItem(Header, i, HeaderItem);
end;
exit;
end;
WM_SIZE:
begin
{ update the header size and location }
with HeaderLayout do
begin
WindowPos := #HeaderPos;
Rect := #HeaderRect;
end;
GetClientRect(Wnd, ClientRect);
CopyRect(HeaderRect, ClientRect);
ZeroMemory(#HeaderPos, sizeof(HeaderPos));
Header_Layout(Header, #HeaderLayout); { updates HeaderPos }
{ use HeaderPos to place the header where it should be in the window }
with HeaderPos do
begin
SetWindowPos(Header,
Wnd, x, y, cx, cy,
Flags);
end;
exit;
end; { WM_SIZE }
WM_NOTIFY:
begin
case HeaderNotification^.Hdr.Code of
HDN_BEGINTRACK:
begin
{ Allow dragging using the left mouse button only }
if HeaderNotification^.Button <> 0 then
begin
WndProc := ptrint(TRUE); { don't track }
exit;
end;
exit;
end;
HDN_TRACK:
begin
{ tell the header to resize itself }
Header_SetItem(Header,
HeaderNotification^.Item,
HeaderNotification^.pitem^);
exit;
end;
end;
end;
WM_DESTROY:
begin
PostQuitMessage(0);
exit;
end;
end; { case msg }
WndProc := DefWindowProc (Wnd, Msg, wParam, lParam);
end;
{-----------------------------------------------------------------------------}
function InitAppClass: WordBool;
{ registers the application's window classes }
var
cls : TWndClassEx;
begin
cls.cbSize := sizeof(TWndClassEx); { must be initialized }
if not GetClassInfoEx (hInstance, ProgramName, cls) then
begin
with cls do
begin
style := CS_BYTEALIGNCLIENT;
lpfnWndProc := #WndProc;
cbClsExtra := 0;
cbWndExtra := 0;
hInstance := system.hInstance;
hIcon := 0;
hCursor := LoadCursor(0, IDC_ARROW);
hbrBackground := COLOR_WINDOW + 1;
lpszMenuName := nil;
lpszClassName := ProgramName;
hIconSm := 0;
end;
InitAppClass := WordBool(RegisterClassEx(cls));
end
else InitAppClass := TRUE;
end;
{-----------------------------------------------------------------------------}
function WinMain : integer;
{ application entry point }
var
Wnd : HWND;
Msg : TMsg;
begin
if not InitAppClass then Halt (255); { register application's class }
{ Create the main application window }
Wnd := CreateWindowEx(WS_EX_CLIENTEDGE,
ProgramName, { class name }
ProgramName, { window caption text }
ws_OverlappedWindow or { window style }
ws_SysMenu or
ws_MinimizeBox or
ws_ClipSiblings or
ws_ClipChildren or { don't affect children }
ws_visible, { make showwindow unnecessary }
20, { x pos on screen }
20, { y pos on screen }
600, { window width }
200, { window height }
0, { parent window handle }
0, { menu handle 0 = use class }
hInstance, { instance handle }
nil); { parameter sent to WM_CREATE }
if Wnd = 0 then Halt; { could not create the window }
while GetMessage (Msg, 0, 0, 0) do { wait for message }
begin
TranslateMessage (Msg); { key conversions }
DispatchMessage (Msg); { send to window procedure }
end;
WinMain := Msg.wParam; { terminate with return code }
end;
begin
WinMain;
end.
This is an artifact caused by attempting to use the control in two different functionality modes at the same time. That, and of course fast mouse movement...
The black vertical line is actually the indicator that hints the final separator position when the mouse button will be released. Of course this indicator is only to be used when the header control does not reflect column resizing at real time.
You are, however, resizing the column at real time responding to the tracking notification. You should instead use the header control in live column drag mode and so that the indicator will not be drawn at all.
In summary, include HDS_FULLDRAG control style:
Header := CreateWindowEx(0,
WC_HEADER, { class name }
nil, { caption }
HDS_BUTTONS or
WS_CHILD or
WS_VISIBLE or
WS_CLIPCHILDREN or
WS_CLIPSIBLINGS or
HDS_FULLDRAG,
0, { at parent x = 0 }
0, { y = 0 }
0, { width }
0, { height }
Wnd, { parent }
HEADER_ID, { child id }
hInstance,
nil);
and leave out the track notification:
...
{ // don't tell the header to resize, it will do it itself
HDN_TRACK:
begin
// tell the header to resize itself
Header_SetItem(Header,
HeaderNotification^.Item,
HeaderNotification^.pitem^);
exit;
end;
}
...
How to display 'x' (close) icon in TBalloonHint?
I want to programmatically display near a control on form a balloon hint that looks like notifications in system tray. If this is not what TBalloonHint can do, what should I use?
First you need a procedure to show your hint :
uses
CommCtrl;
// hWnd - control window handle to attach the baloon to.
// Icon - icon index; 0 = none, 1 = info, 2 = warning, 3 = error.
// BackCL - background color or clDefault to use system setting.
// TextCL - text and border colors or clDefault to use system setting.
// Title - tooltip title (bold first line).
// Text - tooltip text.
procedure ShowBalloonTip(hWnd: THandle; Icon: integer; BackCL, TextCL: TColor; Title: pchar; Text: PWideChar);
const
TOOLTIPS_CLASS = 'tooltips_class32';
TTS_ALWAYSTIP = $01;
TTS_NOPREFIX = $02;
TTS_BALLOON = $40;
TTF_SUBCLASS = $0010;
TTF_TRANSPARENT = $0100;
TTF_CENTERTIP = $0002;
TTM_ADDTOOL = $0400 + 50;
TTM_SETTITLE = (WM_USER + 32);
ICC_WIN95_CLASSES = $000000FF;
type
TOOLINFO = packed record
cbSize: integer;
uFlags: integer;
hWnd: THandle;
uId: integer;
rect: TRect;
hinst: THandle;
lpszText: PWideChar;
lParam: integer;
end;
var
hWndTip: THandle;
ti: TOOLINFO;
begin
hWndTip := CreateWindow(TOOLTIPS_CLASS, nil, WS_POPUP or TTS_CLOSE or TTS_NOPREFIX or TTS_BALLOON or TTS_ALWAYSTIP, 0, 0, 0, 0, hWnd, 0, HInstance, nil);
if hWndTip <> 0 then
begin
SetWindowPos(hWndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
ti.cbSize := SizeOf(ti);
ti.uFlags := TTF_CENTERTIP or TTF_TRANSPARENT or TTF_SUBCLASS;
ti.hWnd := hWnd;
ti.lpszText := Text;
Windows.GetClientRect(hWnd, ti.rect);
if BackCL <> clDefault then
SendMessage(hWndTip, TTM_SETTIPBKCOLOR, BackCL, 0);
if TextCL <> clDefault then
SendMessage(hWndTip, TTM_SETTIPTEXTCOLOR, TextCL, 0);
SendMessage(hWndTip, TTM_ADDTOOL, 1, integer(#ti));
SendMessage(hWndTip, TTM_SETTITLE, Icon mod 4, integer(Title));
//TTM_TRACKACTIVATE => Makes sure you have to close the hint you self
SendMessage(hWndTip, TTM_TRACKACTIVATE, integer(true), integer(#ti));
end;
end;
Then call it :
ShowBalloonTip(Button1.Handle, 4, clDefault, clRed, 'Baloon Title', 'Baloon text');
Hint: if you don’t have hWnd (e.g. Speed Buttons or other graphic component) or want to show the baloon elsewhere send TTM_TRACKPOSITION message after TTM_SETTITLE.
***** EDIT *****
This could also be done via a class helper
First create a unit with a Class helper
unit ComponentBaloonHintU;
interface
uses
Controls, CommCtrl, Graphics;
{$SCOPEDENUMS ON}
type
TIconKind = (None = TTI_NONE, Info = TTI_INFO, Warning = TTI_WARNING, Error = TTI_ERROR, Info_Large = TTI_INFO_LARGE, Warning_Large = TTI_WARNING_LARGE, Eror_Large = TTI_ERROR_LARGE);
TComponentBaloonhint = class helper for TWinControl
public
procedure ShowBalloonTip(Icon: TIconKind; const Title, Text: string);
end;
implementation
uses
Windows;
{ TComponentBaloonhint }
procedure TComponentBaloonhint.ShowBalloonTip(Icon: TIconKind; const Title, Text: string);
var
hWndTip: THandle;
ToolInfo: TToolInfo;
BodyText: pWideChar;
begin
hWndTip := CreateWindow(TOOLTIPS_CLASS, nil, WS_POPUP or TTS_CLOSE or TTS_NOPREFIX or TTS_BALLOON or TTS_ALWAYSTIP, 0, 0, 0, 0, Handle, 0, HInstance, nil);
if hWndTip = 0 then
exit;
GetMem(BodyText, 2 * 256);
try
ToolInfo.cbSize := SizeOf(TToolInfo);
ToolInfo.uFlags := TTF_CENTERTIP or TTF_TRANSPARENT or TTF_SUBCLASS;
ToolInfo.hWnd := Handle;
ToolInfo.lpszText := StringToWideChar(Text, BodyText, 2 * 356);
SetWindowPos(hWndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
ToolInfo.Rect := GetClientRect;
SendMessage(hWndTip, TTM_ADDTOOL, 1, integer(#ToolInfo));
SendMessage(hWndTip, TTM_SETTITLE, integer(Icon), integer(PChar(Title)));
SendMessage(hWndTip, TTM_TRACKACTIVATE, integer(true), integer(#ToolInfo));
finally
FreeMem(BodyText);
end;
end;
end.
Then call it:
uses
ComponentBaloonHintU;
procedure TForm1.Button1Click(Sender: TObject);
begin
Button1.ShowBalloonTip(TIconKind.Eror_Large, 'Baloon Title', 'Baloon text');
end;
i have a Win32 API Application in Delphi 2007 with no form and would like to load a bitmap from a .res file. Been looking around for two day's and just can't seem to find anything on this subject so was time to post. :)
Assuming i need to add code to "WM_PAINT" message just not sure what to add. maybe could use GDI.
/Thanks.
EDIT:
function WndProc(hWin: HWnd; Msg, WParam, LParam: Longint): Longint; stdcall;
var
hbmp: HBITMAP;
ps: PAINTSTRUCT;
DC, hdcMem: HDC;
bmp: BITMAP;
oldBitmap: HGDIOBJ;
begin
case Msg of
WM_CREATE:
begin
hbmp := LoadImage(HInstance, 'C:\test_img.bmp', IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // Never called H2077 Value assigned to 'hbmp' never used
ShowMessage('Im Here'); // Called
Result := 0;
Exit;
end;
WM_PAINT:
begin
hbmp := LoadImage(HInstance, 'C:\test_img.bmp', IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // Called
DC := BeginPaint(hWin, ps);
hdcMem := CreateCompatibleDC(DC);
oldBitmap := SelectObject(hdcMem, hbmp);
GetObject(hbmp, SizeOf(bmp), #bmp);
BitBlt(DC, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
DeleteObject(hbmp);
EndPaint(hWin, ps);
Result := 0;
Exit;
end;
WM_DESTROY:
begin
PostQuitMessage(0);
Result := 0;
Exit;
end;
end;
Result := DefWindowProc(hWin, Msg, WParam, LParam);
end;
Am i right in thinking that WM_CREATE is the equivalent of Form1.OnCreate and WM_DESTROY is Form1.OnDestroy ect..
Look at the TBitmap class in the Graphics unit. It has LoadFromResourceName() and LoadFromResourceID() methods.