Author Topic: [SDK] 05 - Take control of your BUTTONS  (Read 1306 times)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1874
    • zapsolution
[SDK] 05 - Take control of your BUTTONS
« on: February 09, 2020, 05:12:59 pm »
WORK IN PROGRESS
This is the fifth post of a serie, where I shall try to explain how to take complete control over SDI window.

This is a major release, because most of the basic functions I wanted to put in, are now there.

You can now create three type of buttons

1 - zButImage

2 - zPushButton

3 - zFrameButton

With small modifications it could be possible to create more, like ckeck box or radio button, using the "ZBUTIMAGE" class.

You can even use zFrameButton to draw a static image.

I have added a new temporary double buffer, that can be used to remove flickering when drawing complex child controls.

'// Create temporary double buffer
Code: [Select]
FUNCTION zDoubleBuffer(BYVAL hDC AS LONG, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL Action AS LONG) STATIC AS LONG
    DIM Xx AS LONG, Yy AS LONG, UseDC AS LONG, hDCTemp AS LONG, hBMTemp AS LONG, hBMPrev AS LONG
    IF Action THEN
     ' Create OFF screen bitmap to avoid flickering and allow smooth display
     ' ---------------------------------------------------------------------
       hDCTemp = CreateCompatibleDC(hDC)
       hBMTemp = CreateCompatibleBitmap(hDC, x, y)
       hBMPrev = SelectObject(hDCTemp, hBMTemp)
       Xx = x: Yy = y
       UseDC = hDC
       FUNCTION = hDCTemp
    ELSE
     ' End of OFF screen drawing, fast BitBlt to the target display Window
     ' -------------------------------------------------------------------
     ' Draw final result to the target window
       CALL BitBlt(UseDC, x, y, Xx, Yy, hDCTemp, 0, 0, %SRCCOPY)
     ' Deallocate system resources
       CALL SelectObject(hDCTemp, hBMPrev)
       CALL DeleteObject(hBMTemp)
       CALL DeleteDC(hDCTemp)
       FUNCTION = 0
    END IF

END FUNCTION
 
The %ANCHOR_CENTER_HORZ_BOTTOM has been enhanced, you can see it on the multimedia buttons as well as the frame button drawn arround them, when you resize the form.

All images have been reworked to use transparent opacity and little shadow to give your form a professionnal look like this one:



Nice isn't it ?

« Last Edit: February 09, 2020, 05:30:03 pm by Patrice Terrier »
Patrice
(Always working with the latest Windows version available...)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1874
    • zapsolution
Re: [SDK] 05 - Take control of your BUTTONS
« Reply #1 on: February 09, 2020, 05:35:15 pm »
Quote
it is as beautiful as the api coding is long

This is because I choose to put everyting in the EXE for the tutorial pupose, however usually most of classes coding are done in DLL (see the size of the Windows API's DLLs).

Ultimatly most of the code should be moved into a DLL, where some more nice stuffs can be done, that couldn't be done in a EXE, like low level advanced  hooking functions.

Example of thing that can be done only with a DLL:
Code: [Select]
FUNCTION HookFunction (hLib AS DWORD, Func AS STRING, HookProc AS DWORD, OrigProc AS DWORD) AS LONG
    LOCAL lpImageDosHeader          AS IMAGE_DOS_HEADER PTR
    LOCAL lpImageNtHeaders          AS IMAGE_NT_HEADERS PTR
    LOCAL lpImageImportDescriptor   AS IMAGE_IMPORT_DESCRIPTOR PTR
    LOCAL lpImageImportByName       AS IMAGE_IMPORT_BY_NAME PTR
    LOCAL lpFuncNameRef             AS DWORD PTR
    LOCAL lpFuncAddr                AS DWORD PTR
    LOCAL flOldProtect              AS DWORD

    lpImageDosHeader = hLib

    IF @lpImageDosHeader.e_magic <> %IMAGE_DOS_SIGNATURE THEN EXIT FUNCTION ' invalid DOS signature
    lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
    IF @lpImageNtHeaders.Signature <> %IMAGE_NT_SIGNATURE THEN EXIT FUNCTION  ' invalid NT signature
    IF @lpImageNtHeaders.FileHeader.SizeOfOptionalHeader <> SIZEOF( @lpImageNtHeaders.OptionalHeader) OR _
       @lpImageNtHeaders.OptionalHeader.Magic <> %IMAGE_NT_OPTIONAL_HDR32_MAGIC THEN EXIT FUNCTION

    IF @lpImageNtHeaders.OptionalHeader.NumberOfRvaAndSizes <= %IMAGE_DIRECTORY_ENTRY_IMPORT THEN EXIT FUNCTION ' IMPORT section does not exist

    lpImageImportDescriptor = @lpImageNtHeaders.OptionalHeader.DataDirectory( %IMAGE_DIRECTORY_ENTRY_IMPORT ).VirtualAddress + lpImageDosHeader
    IF lpImageImportDescriptor = lpImageDosHeader THEN EXIT FUNCTION

    WHILE @lpImageImportDescriptor.OriginalFirstThunk <> 0
     ' DllName @lpImageImportDescriptor.pName + lpImageDosHeader = Asciiz Ptr
       lpFuncNameRef = @lpImageImportDescriptor.OriginalFirstThunk + lpImageDosHeader
       lpFuncAddr = @lpImageImportDescriptor.FirstThunk         + lpImageDosHeader

       DO WHILE @lpFuncNameRef <> 0
          lpImageImportByName = @lpFuncNameRef + lpImageDosHeader
          IF (@lpFuncNameRef AND %IMAGE_ORDINAL_FLAG) THEN

          ELSEIF @lpImageImportByName.ImpName = Func THEN
             IF VirtualProtect(BYVAL lpFuncAddr, 4, %PAGE_READWRITE, flOldProtect) = 0 THEN EXIT FUNCTION
             OrigProc = @lpFuncAddr: @lpFuncAddr = HookProc
             IF FlushInstructionCache(GetCurrentProcess, BYVAL @lpFuncAddr, 4) = 0 THEN EXIT FUNCTION
             IF flOldProtect <> %PAGE_READWRITE THEN VirtualProtect BYVAL lpFuncAddr, 4, flOldProtect, flOldProtect
             FUNCTION = 1: EXIT FUNCTION ' Success
          END IF
          INCR lpFuncNameRef
          INCR lpFuncAddr
       LOOP
       INCR lpImageImportDescriptor
    LOOP
END FUNCTION

The code above is being used to hook/replace existing API directly within Windows DLLs!

and here is an example that shows you how to use it, to replace the core API BeginPaint EndPaint by your own code.

Quote
FUNCTION GetMsgProc(BYVAL nCode AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG

   IF nCode < 0 THEN
      FUNCTION = CallNextHookEx(BYVAL hHook, BYVAL nCode, BYVAL wParam, BYVAL lParam)
   ELSEIF hHookDone = %FALSE THEN
      CALL HookFunction(GetModuleHandle(BYVAL %NULL), "BeginPaint", CODEPTR(skBeginPaint), hBeginPaint)
      CALL HookFunction(GetModuleHandle(BYVAL %NULL), "EndPaint", CODEPTR(skEndPaint), hEndPaint)
      hHookDone = %TRUE
   END IF

END FUNCTION

FUNCTION zBeginPaint ALIAS "zBeginPaint" (BYVAL hWnd AS LONG, ps AS PAINTSTRUCT) EXPORT AS LONG
    LOCAL rw AS RECT
    LOCAL pt AS POINTAPI
    Item& = skChild(hWnd&)
    IF Item& THEN
       hOwner& = skPopupOwner(hWnd&)
       OwnerItem& = skItem(hOwner&)
       IF OwnerItem& THEN
          IF Win(OwnerItem&).MemDC THEN
             ps.hDC = Win(OwnerItem&).MemDC
             ps.fErase = 0

             CALL GetWindowRect(hOwner&, rw)
             pt.X = 0: pt.Y = 0: CALL ClientToScreen(hWnd&, pt)
             ofX& = pt.X - rw.nLeft: ofY& = pt.Y - rw.nTop
             CALL GetClientRect(hWnd&, rw)

             ps.rcPaint.nLeft   = ofX&
             ps.rcPaint.nTop    = ofY&
             ps.rcPaint.nRight  = ofX& + rw.nRight
             ps.rcPaint.nBottom = ofY& + rw.nBottom

             ps.fRestore   = 0
             ps.fIncUpdate = 0
             FOR K& = 0 TO 31: ps.rgbReserved(K&) = 0: NEXT
             FUNCTION = ps.hDC
             EXIT FUNCTION
          END IF
       END IF
    END IF
    CALL DWORD hBeginPaint USING BeginPaint (hWnd, ps) TO Ret&
    FUNCTION = Ret&

END FUNCTION

Of course this kind of thing requires a very good knowledge of the core API, and you must know exactly what you are doing or be assured that you will crash your computer.
 :'(

This is one of the reason why Microsoft put ahead managed code, to protect the OS from direct access to the low level FLAT API engine, no more hackers/hookers there  :)


PS: The BeginPaint EndPaint hook function is a quick cut and past from my WinLIFT SkinEngine, that was written 19 years ago!

Patrice
(Always working with the latest Windows version available...)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1874
    • zapsolution
Re: [SDK] 05 - Take control of your BUTTONS
« Reply #2 on: February 09, 2020, 06:21:00 pm »
.
Patrice
(Always working with the latest Windows version available...)