Drawing on a window from its device context handle

Just starting out? Need help? Post your questions and find answers here.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8453
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Can be done using an image. You draw the image to the hdc of the target window. Sounds simple enough but it really isn't. You have to know when the target window is being repainted or your image won't be persistent. It'll go away if windows cover it up or it gets minimized etc. So how to know when to paint? Use a WH_CALLWNDPROCRET hook (Sparkie's idea from an earlier similar question.) I wrote this code then but didn't post it, so here it is:

This code needs to be compiled in a dll in order to work:

Code: Select all

; Compile this code as shared dll = "Draw.DLL"

ProcedureDLL AttachProcess(instance)

  Global drawmessage = RegisterWindowMessage_("CustomDraw")
  Global window = FindWindow_(#Null, "CustomDrawTest")
  Global hook = GetWindowLong_(window, #GWL_USERDATA)
 
EndProcedure

ProcedureDLL CallWndProcRet(nCode, wParam, lParam) 

  *msg.CWPRETSTRUCT = lparam

  Select *msg\message
    Case #WM_PAINT,#WM_NCPAINT
    PostMessage_(window, drawmessage, 0, 0)
  EndSelect  
 
  ProcedureReturn CallNextHookEx_(hook, nCode, wParam, lParam) 
EndProcedure
Now a test program:

Code: Select all

; When you've made "Draw.dll", make it available and run this code:

Global drawmessage = RegisterWindowMessage_("CustomDraw")
Declare Draw(hwnd)

OpenWindow(0,0,0,1,1,"CustomDrawTest",#PB_Window_BorderLess|#PB_Window_Invisible)

RunProgram("calc.exe")
Delay(1000)
hwnd = FindWindow_(0,"Calculator")
draw(hwnd)

hDLL = OpenLibrary(0, "draw.dll")

ThreadID = GetWindowThreadProcessId_(hwnd, @ProcessID)
proc = GetFunction(0, "CallWndProcRet")

hook = SetWindowsHookEx_(#WH_CALLWNDPROCRET, proc, hDll, ThreadID ) 
SetWindowLong_(WindowID(0),#GWL_USERDATA, hook)

Repeat
  msg = WaitWindowEvent(1)
  If msg = drawmessage
    Draw(hwnd)
  EndIf
Until IsWindow_(hwnd) = #False

CloseWindow(0)

Procedure Draw(hwnd)
   GetClientRect_(hwnd, @cr.RECT)
    w = cr\right-cr\left
    tmp = CreateImage(#PB_Any,w,1,#PB_Image_DisplayFormat)
    hdc = GetDC_(hwnd)
    dcimg = StartDrawing(ImageOutput(tmp))
      ;here the 2ddrawing commands..
      Line(0,0,w,0,#Red)
      BitBlt_(hdc, 0,0,ImageWidth(tmp),ImageHeight(tmp),dcimg,0,0,#SRCCOPY)
     StopDrawing()
    ReleaseDC_(hwnd, hdc)
    FreeImage(tmp)
EndProcedure
And your calculator will always have a red line on it.
Last edited by netmaestro on Mon Feb 23, 2009 4:25 am, edited 2 times in total.
BERESHEIT
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

I see! You're using BitBlit to copy what you've drawn to the PureBasic image to the target hDC. A good trick and it seems like a safe way to go about it too.

Thanks. :)
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8453
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Sorry I unthinkingly removed the parameter from WaitWindowEvent but it needs it otherwise the program will keep running until it gets a message.
BERESHEIT
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

Post by Fluid Byte »

I had a little different way in mind since you can actually create your own output for StartDrawing().

Well, how can you draw multiple different transparent shapes on the window? It seems a little fiddly since the output is an image instead the of windows' DC.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Fluid Byte wrote:Well, how can you draw multiple different transparent shapes on the window? It seems a little fiddly since the output is an image instead the of windows' DC.
You could blit the window to the image first and do a sort of double-buffering.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

remains the question, how to create a PB compatible Output Channel for the StartDrawing command out of a DC...

I always thought that the return value of XXOutput() is a DC, so I'm a bit puzzled now...
oh... and have a nice day.
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

Post by Fluid Byte »

Kaeru Gaman wrote:remains the question, how to create a PB compatible Output Channel for the StartDrawing command out of a DC...
Like this for example:

Code: Select all

Structure DrawingInfoStruct
	Type.l    				; Type of the DC
	Window.l  				; Window associated to the DC (if any)
	DC.l      				; DC (Display Device Context)
	ReleaseProcedure.l	; Address to a procedure to release the DC when StopDrawing() is called
	PixelBuffer.l      	; Address of the memory pixel buffer (DirectX)
	Pitch.l            	; Bytes per line (DirectX)
	Width.l
	Height.l
	Depth.l
EndStructure

RunProgram("notepad.exe",#PB_Compiler_Home + "SDK\Readme.txt","")
Delay(1000)

*dis.DrawingInfoStruct = AllocateMemory(SizeOf(DrawingInfoStruct))
*dis\Type = 1
*dis\Window = FindWindow_("Notepad",0)

hdc = StartDrawing(*dis)
BackColor(0)
For x=0 To 10
	For y=0 To 10
		Box(10+x*60,10+y*60,48,48,Random($FFFFFF))
		DrawText(28,28+y*60,"....::::  I   H A Z   H A C K E D   N O T E P A D !  ::::....",#Red)
	Next
Next
StopDrawing()
So I answered the question myself. You just need to put this into the 'Draw()' procedure and you can achieve the desired result.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Fluid Byte, you're using the window handle not the device context.
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

Post by Fluid Byte »

Mistrel wrote:Fluid Byte, you're using the window handle not the device context.
From which the DC is obtained, yes.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Is there a way to replace the hDC of one window with another? For example, if I want to paint the contriols of a window on the device context of an off-screen image and blit it back?
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

Post by Fluid Byte »

Mistrel wrote:For example, if I want to paint the contriols of a window on the device context of an off-screen image and blit it back?
What would be the purpose of that? Don't want to sound rude, just curious.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Fluid Byte wrote:
Mistrel wrote:For example, if I want to paint the contriols of a window on the device context of an off-screen image and blit it back?
What would be the purpose of that? Don't want to sound rude, just curious.
It's useful for double-buffering the output of a window.
Post Reply