Announcement

Collapse
No announcement yet.

Windows programming 101 tutorials

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Windows programming 101 tutorials

    Over time I have posted a number of what I call "Windows 101"
    tutorials (or detailed explanation) about programming the Windows
    API. I decided to search the forums and to put them all into one
    thread to make it easier for any who are learning Windows programming
    to find them.

    Class Styles 101:

    There are three sets of styles for use with any window class.
    The first is the extended window styles which are predefined in
    Windows. The constants start with %WS_EX_

    The second and third set of styles are combined to create the
    window styles. Windows styles can be a 32 bit value. The two high
    bytes of this value (&00010000 to &FFFF0000) are predefined window
    styles. These style constants start with %WS_ and are predefined
    by windows.

    The lower two bytes of the style value are for custom window styles
    based on the window class (&H00000001 to &H0000FFFF). These styles
    are defined for each window class and are not interchangable between
    window classes. For example the button control (or any class based
    on the button control) styles start with %BS_ and are defined for
    that class. These styles have no meaning for any other window
    class.

    Windows actually does not recognize the meaning of any of these
    custom class styles (the low 2 bytes). Only the window procedure
    for the class understands how to make sense of the styles.

    When writing your own custom control (even if you superclass),
    you should always define your own set of Styles for the class
    (lower 2 bytes) and define a prefix and name for the styles.

    Let's say I write a custom control called a Turtle Graphics
    control. I would define my own window styles for the control
    (the lower two bytes) and then prefix them with a prefix that
    makes sense, like %TGS_

    Additional Notes:

    %DS_CONTROL is a pseudo style supported by the Dialog class.

    %WS_EX_CONTROLPARENT is the actual extended style that makes a
    window handle child windows and tabbing properly.

    The dialog class (which DDT uses) doesn't support all the
    extended window styles. When the dialog is created (it gets
    the WM_CREATE message internally) it will read the styles used
    and then convert some of them to actual extended window styles.

    The %DS_CONTROL style will be converted to the extended window style
    %WS_EX_CONTROLPARENT.

    If you are using your own window classes (SDK style code), then
    %DS_CONTROL won't work and you must use the extended window
    style %WS_EX_CONTROLPARENT !

    Windows defines a number of custom styles just for the Dialog
    class which all start with the %DS_ prefix. When the dialog is
    created, the internal window procedure for the dialog class,
    will convert all the %DS_ styles to the appropriate %WS_ or
    %WS_EX styles.

    Dialogs are a predefined window class in Windows and they don't
    work exactly the same as window classes you create yourself.

    An interesting tool to use to learn more, is Borje's WinSpy.
    You can check to see what styles the dialog actually ends up
    with after it is created.

    As has been discussed before, the two high bytes of the window
    style value are universal window styles that any window class
    can use (universal). The two low bytes are custom styles unique
    to the window class. The Dialog class has its own set of custom
    styles just like controls do. These styles start with the prefix
    %DS_ . Now some of these custom styles are features that already
    exist in windows in the extended window styles, so the dialog
    class converts them to their extended style counter parts.

    The extended window styles are also universal for all window
    classes. You can only use the extended styles made available by windows.


    I should clarify one point.

    The dialog class converts %DS_ styles
    during the creation of the dialog before its window procedure
    gets the %WM_CREATE message.

    The reason is that to create a Dialog requires that you use
    the Dialog functions in the API, rather than CreateWindowEx.

    It is likely that the conversion takes place here, before the
    dialog functions call CreateWindowEx.

    Windows supports two different dialog
    template structures. The default one does not support extended
    window styles, which is why the %DS_ styles are needed to be
    able to set extended window styles.

    to be continued ...


    ------------------
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

  • #2
    Subclassing 101:

    Every window has its own class (ie. edit, static, etc.). A Class
    is a specific type of window with specific features. The edit class
    allows typing in text. The button class actually is multiple controls,
    such as a button, checkbox, optionbutton or frame.

    Dialogs are a unique window class predefined by windows. Controls
    are also predefined by windows. You can also create your own
    custom window class as well, as is done with SDK style coding
    (non-ddt).

    Every window class at some point must be registered with windows.
    When a class is registered one parameter passed is the address
    to the window procedure for the class. The predefined window
    classes all have their own window procedure which exist within
    the operating system DLL's. When your applications must send
    messages to a predefined window class, windows will send it to
    the window procedure in the OS DLL's for that class.

    Every window procedure looks like this:

    Code:
    FUNCTION WindowProc(BYVAL hWnd AS LONG, BYVAL Msg AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
        SELECT CASE Msg
            ' define all custom message processing below
            CASE %WM_CREATE
            CASE %WM_DESTROY
            CASE ELSE
        END SELECT
    FUNCTION=DefWindowProc(hWnd,Msg,wParam,lParam)
    END FUNCTION
    By default window messages are processed by the DefWindowProc
    function. This function processes all messages in a default
    way. The window procedure may also include processing of any
    message to customize how the class will look and act. Also
    the window procedure will likely have custom messages
    (%WM_USER + SomeNumber) that handle custom features of the
    window class. For example the edit control has custom messages
    which start with the prefix %EM_ .

    When you, the programmer, create (register) your own window class,
    you have access to the window procedure for that class in your
    application. The problem with the predefined window classes
    (ie. controls, dialogs), is that you don't have direct access
    to the window procedure for those classes. The code for their
    window procedures exists in the operating system DLL's and not
    in your application.

    This is where subclassing comes in !

    Every window has the ability to have its internal window
    procedure address to be changed. When a window (ie. control) is
    created, it stores the address to the original window procedure
    for the class in its data structure (instance data). Windows
    allows you to change the address to another address so the
    window uses a different window procedure. The problem with this
    is that if you change the window procedure address from the original
    you lose all the original's features.

    Subclassing is a way of rerouting messages to a window procedure.

    When you use the GetWindowLong function with the %GWL_WNDPROC flag,
    you get the original window procedure address for the original
    window class the window is based on. Now you have the original
    window procedure address.

    By using the SetWindowLong function you can change the window
    procedure address for the specific window to another one, in this
    case a custom one written by you. SetWindowLong also returns
    the previous value so you don't have to use GetWindowLong to
    get the original value.

    Lastly, windows has an interesting function called CallWindowProc
    which allows you to send messages to the original window procedure
    by passing its address.

    Now, let's subclass a window:

    Code:
    FUNCTION SubClassWindow(BYVAL hWnd&, BYVAL NewAddress&) AS LONG
        FUNCTION=SetWindowLong(hWnd&, %GWL_WNDPROC, NewAddress&)
    END FUNCTION
    '
    GLOBAL gOrigAddress&
    '
    SUB StartSubClass(BYVAL hWnd&)
        gOrigAddress&=SubClassWindow(hWnd&, CODEPTR(MyWindowProc))
    END SUB
    '
    FUNCTION MyWindowProc(BYVAL hWnd AS LONG, BYVAL Msg AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
        SELECT CASE Msg
            ' define all custom message processing below
            CASE %WM_CREATE
            CASE %WM_DESTROY
            CASE ELSE
        END SELECT
        FUNCTION=CallWindowProc(gOrigAddress&,hWnd,Msg,wParam,lParam)
    END FUNCTION
    By saving the original window procedure address for the windows
    original class, you can now send all unprocessed messages in
    your custom subclass window procedure to the original window
    prcoedure so the window has the attributes of the original
    class. You can also preprocess any messages you before the
    original window does so as to add new features.


    Notes:

    Advantages of Superclassing over subclassing:

    Superclassing requires a few more steps.

    You must get the original wndclass structure for an existing window
    class by using the GetClassInfo (or GetClassInfoEx) function.

    You must get the original class window procedure address from
    this info and store it. You then change the class name (to your
    own custom class name), the window procedure address to your
    own custom window procedure class, modify the extra window bytes
    value (increase it for any custom data storage) and the register
    the new window class. Now you have a new window class which points
    to your suprclass window procedure. The window procedure looks
    much similiar to the subclass window procedure above. Any messages
    not processed must be passed to the original window procedure using
    the CallWindowProc function.

    Subclassing requires that you must subclass each and every control
    that you want subclassed. Superclassing doesn't require any
    special coding for each and every control. You simply create the
    controls (using the new class name) just like the original controls
    would have been (ie. instead of CONTROL ADD "edit" it would be CONTROL ADD "myedit"
    if the superclass name is myedit). You just create the new class
    control and thats it. It is automatically superclassed.

    If you need multiple controls of a particular class that all need
    to be subclassed, the superclassing is far more efficient. If you
    only need one control subclassed, then subclassing is more likely
    better (slightly less code).

    Now it should be noted that both subclassing and superclassing
    will slow down a control slightly, since two window procedures
    will be called for many messages (the subclass window procedure
    and the original window procedure unprocessed messages are passed
    to). With todays fast computers, this is likely not going to
    effect the speed the control responds by much.



    ------------------
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

    Comment


    • #3
      Windows Graphics 101 :

      Windows has a lot of overhead when drawing. It's not like the
      old days (remember the Commodore 64) when you could literally
      peek and poke video ram. Especially when you use API functions
      like SetPixel can you see how slow it can be to draw in windows.
      The larger an area an API command draws on the faster the drawing
      is per pixel. As a test use the Rectangle API function to draw a
      large filled rectangle and then try drawing the same filled rectangle
      a pixel at a time using the SetPixelV (faster than SetPixel) API
      function. The speed difference will be dramatic. Both techniques
      accomplish the same thing, but drawing with SetPixel demonstrates
      the huge amount of overhead Windows has in drawing.

      The next part of the equation is the difference between drawing into
      RAM and drawing directly into Video RAM. When you draw directly into
      a Windows DC (Device Context) you are drawing into Video RAM. Video
      RAM drawing is terribly slow compared to drawing into regular RAM.

      What slows Windows down is the Device Context (DC) arrangement.
      Using a DC has its purpose though. It allows windows to draw into
      a variety of devices using the same GDI functions, such as Video RAM,
      regular RAM, a Printer or any other device that could be drawn into.
      The use of a DC is very powerful, but the drawback is that there
      is a lot of extra overhead to keep track of all the stuff associated
      with a DC. The need to select objects into and out of a DC adds
      a significant amount of overhead. DC's are needed for Windows to do
      what it does. It just slows things down.

      Now using the above information, we can develop better approaches
      to drawing in Windows for faster display rates. Here are a few
      techniques of how to speed things up.

      (1) Drawing into RAM is significantly faster than drawing into
      Video RAM (a Windows DC) !


      This is where buffering comes in. By creating a Memory DC
      and selecting a Bitmap into , you can now draw directly into RAM.
      When you must use the slower , lower level GDI functions (like
      SetPixel) to draw with, then you should always draw into a Memory
      DC rather than a Window DC (Video RAM). This will significantly
      speed up drawing. Now remember, when you draw into a memory DC
      you can't see the results. You will have to somehow move the image
      from RAM (memory DC) to Video RAM (window DC). This brings us to
      point #2.

      (2) Use only high level, large area, GDI (API) functions when
      drawing into a Windows DC !


      When drawing into actual video ram (window DC during WM_PAINT), the
      more complex GDI functions which cover a larger area should be used.
      For example, PatBlt is commonly used for filling the background of
      a Windows DC. These types of GDI functions (which cover a large area)
      are much more highly optimized than the lower level ones. One of the
      more commonly used GDI functions used when using Buffers (memory DC)
      is BitBlt. BitBlt is highly optimized and it can move large amounts
      of data (pixels) back and forth between DCs. BitBlt is commonly used
      to move data (pixels) from a memory DC where an image has been drawn
      to a window DC (which is video ram). Another GDI function that can
      be very useful is StretchBlt which can scale an image into
      any size window DC.

      By using these very fast and highly optimized GDI (API) functions
      you can significantly speed up drawing into a window DC.

      Note: When drawing into a Printer DC, things are a little different
      since many printer drivers don't support BitBlt. In this case
      DIBs (Device Independant Bitmaps) need to be used and GDI
      functions like StretchDiBits should be used.

      Putting the above two principles to use is the basics to using
      a memory DC as a buffer. Using a memory buffer is also useful in
      adding persistance to a window DC. Rather than have to redraw an image
      from scratch into a window DC, every time the WM_PAINT message is
      processed, a single image can be copied (using BitBlt) from memory
      into the window DC. Consider this technique as draw once, BitBlt
      many.

      There are other techniques other than this for drawing that can add
      speed but they are a bit more advanced. One such is using DIBs. Simply
      put a DIB is a memory image similiar to a Bitmap, but the difference
      is that it is device independent and you can actually choose the format
      of how the data is stored. For example if the video display is 16 bit
      color, you could copy the data into a DIB which is stored as 32 bit
      color. The other difference, which is significant, is that with a
      DIB you have direct access to each pixel in byte form. The GDI is
      not necessary for accessing the pixels. Its kind of like the old days
      where you can peek and poke directly into video ram. In this case, there
      is an extra few steps. You create a memory DC and memory bitmap which
      is compatible with the video mode (ie. 256 color, 16 bit, etc.).
      You then move the data from the memory Bitmap (and DC) into a DIB
      section (simply a block of RAM allocated to hold a bunch of bytes).
      Now you can peek and poke all you want directly into the DIB section
      data. You can write your own custom drawing functions which work
      directly on RAM data. Once you finish drawing, you now move the data
      in the DIB section back into the memory DC bitmap. Now you can BitBlt
      the memory DC into video ram. I think it is also possible to skip
      the memory DC step when using DIBs and to move data back and forth
      between a window DC and a DIB section, but I haven't tried it yet
      and I can't verify how it works.

      Notes:

      Just to add to my Graphics 101:

      I wasn't trying to get into the technical aspects of how Windows
      draws things. Of course more is involved than what I mentioned.
      The video driver and the GDI are between your code and direct video
      RAM and you can't access it directly.

      My point though is the when you draw into a window DC, for all
      practical purposes you are indirectly drawing into the video RAM
      (thats where what you draw is stored). The problem with what you
      draw in a window DC, is that is doesn't have persistance. The memory
      where the window DC's pixels are stored (video RAM or whatever) is shared by
      all windows. The DC itself may be shared (it stores the current objects
      like pen, brush) or not, but the area where the pixels are stored
      is shared by all windows. Any other window can write over your windows
      pixel data in video ram. Because of its lack of persistance because
      of sharing pixel space with any window that may draw in it, a Window
      DC's pixel data should always be considered temporary.

      Now a memory DC (with an associated Bitmap, which is here the pixels
      are actually stored) can be isolated from access by other windows.
      You can create your own memory DC and memory bitmap to draw in and no one
      else can bother it. This produces persistance.

      Memory DC's (with a memory bitmap selected into it) have two advantages
      . One is speed of drawing. Drawing into a memory DC is always faster
      than drawing into a window DC (for whatever reasons). Second a memory
      DC (with its bitmap) has persistance, where as a window DC does not.

      To prove this concept of persistance, write a program that only processes
      the WM_PAINT message one time (the first time called) and see what
      happens when you move over it with another window.


      A Lesson in Persistance !

      Below is a program that demonstrates what happens with a window
      DC not being painted all the time. It demonstrates the lack of
      persistance when you draw into a window DC. If the window is
      prevented from processing the WM_PAINT (or the WM_ERASEBKGND)
      message, the pixels drawn in the windows DC are not remembered.

      Code:
      #COMPILE EXE
      #REGISTER NONE
      #DIM ALL          '  This is helpful to prevent errors in coding
      
      #INCLUDE "win32api.inc"   ' Must come first before other include files !
      
      DECLARE SUB LIB_InitColors()
      DECLARE SUB LIB_DeleteBrushes()
      DECLARE SUB ShowDialog_Form1(BYVAL hParent&)
      DECLARE CALLBACK FUNCTION Form1_DLGPROC
      DECLARE SUB ShowDialog_Form2(BYVAL hParent&)
      DECLARE CALLBACK FUNCTION Form2_DLGPROC
      DECLARE CALLBACK FUNCTION CBF_FORM2_BUTTON1()
      
      %FORM2_BUTTON1            = 100
      
      GLOBAL App_Brush&()
      GLOBAL App_Color&()
      GLOBAL App_Font&()
      GLOBAL hForm1&    ' Dialog handle
      GLOBAL hForm2&    ' Dialog handle
      GLOBAL PaintFlag&
      
      ' *************************************************************
      '                    Application Entrance
      ' *************************************************************
      
      FUNCTION PBMAIN
          LOCAL Count&
          LIB_InitColors
          PaintFlag&=1
          ShowDialog_Form1 0
          ShowDialog_Form2 hForm1&
          DO
              DIALOG DOEVENTS TO Count&
          LOOP UNTIL Count&=0
          LIB_DeleteBrushes
      END FUNCTION
      
      SUB ShowDialog_Form1(BYVAL hParent&)
          LOCAL Style&, ExStyle&
          Style& = %WS_POPUP OR %DS_MODALFRAME OR %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU OR %DS_CENTER
          ExStyle& = 0
          DIALOG NEW hParent&, "WM_PAINT limited window", 0, 0,  267,  177, Style&, ExStyle& TO hForm1&
          DIALOG SHOW MODELESS hForm1& , CALL Form1_DLGPROC
      END SUB
      
      CALLBACK FUNCTION Form1_DLGPROC
          SELECT CASE CBMSG
              CASE %WM_PAINT
                  ' Windows calls WM_ERASEBKGND to fill background
                  ' so I process that message
              CASE %WM_ERASEBKGND
                  IF PaintFlag&=0 THEN
                      FUNCTION=1
                      EXIT FUNCTION
                  END IF
              ' -----------------------------------------------
              CASE %WM_CTLCOLORDLG
                  IF CBLPARAM=CBHNDL THEN
                      ' Dialogs colors
                      SetTextColor CBWPARAM, App_Color&(0)
                      SetBkColor   CBWPARAM, App_Color&( 17)
                      FUNCTION=App_Brush&( 17)
                  END IF
              CASE ELSE
          END SELECT
      END FUNCTION
      
      SUB ShowDialog_Form2(BYVAL hParent&)
          LOCAL Style&, ExStyle&
          Style& = %WS_POPUP OR %DS_MODALFRAME OR %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU OR %DS_CENTER
          ExStyle& = 0
          DIALOG NEW hParent&, "Click Button to Toggle painting of other window", 0, 0,  245,  59, Style&, ExStyle& TO hForm2&
          CONTROL ADD "Button", hForm2&,  %FORM2_BUTTON1,  "Toggle WM_PAINT for other Window", 37, 17, 176, 15, _
              %WS_CHILD OR %WS_VISIBLE OR %BS_PUSHBUTTON OR %WS_TABSTOP CALL CBF_FORM2_BUTTON1
          DIALOG SHOW MODELESS hForm2& , CALL Form2_DLGPROC
      END SUB
      
      CALLBACK FUNCTION Form2_DLGPROC
          SELECT CASE CBMSG
              ' -----------------------------------------------
              CASE %WM_CTLCOLORDLG
                  IF CBLPARAM=CBHNDL THEN
                      ' Dialogs colors
                      SetTextColor CBWPARAM, App_Color&(0)
                      SetBkColor   CBWPARAM, App_Color&( 10)
                      FUNCTION=App_Brush&( 10)
                  END IF
              CASE ELSE
          END SELECT
      END FUNCTION
      
      ' *******************************************************************
      ' *                         Library Code   *
      ' ********************************************************************
      
      SUB LIB_InitColors()
          DATA         0,  8388608,    32768,  8421376,      196,  8388736,    16512, 12895428
          DATA   8421504, 16711680,    65280, 16776960,      255, 16711935,    65535, 16777215
          DATA  10790052, 16752768, 10551200, 16777120, 10526975, 16752895, 10551295, 13948116
          DATA  11842740, 16768188, 14483420, 16777180, 14474495, 16768255, 14483455, 15000804
          LOCAL T&, RGBVal&
          REDIM App_Brush&(0 TO 31)
          REDIM App_Color&(0 TO 31)
          FOR T&=0 TO 31
              RGBVal&=VAL(READ$(T&+1))
              App_Brush&(T&)=CreateSolidBrush(RGBVal&)
              App_Color&(T&)=RGBVal&
          NEXT T&
      END SUB
      
      ' -------------------------------------------------------------
      
      SUB LIB_DeleteBrushes()
          LOCAL T&
          FOR T&=0 TO 31
              DeleteObject App_Brush&(T&)
          NEXT T&
      END SUB
      
      ' -------------------------------------------------------------
      
      CALLBACK FUNCTION CBF_FORM2_BUTTON1
          IF CBCTLMSG=%BN_CLICKED THEN
              IF PaintFlag&=0 THEN
                  PaintFlag&=1
              ELSE
                  PaintFlag&=0
              END IF
          END IF
      END FUNCTION
      In the test program I posted, click the button on the green window
      and then move the green window around by its caption bar.

      See what happens to the blue window as the green window is moved
      around.

      Now click the button the green window again (make sure it is the
      real window and not the left over image) and now move the green
      window around over the blue window and see what happens. The blue
      window now repaints itself.





      ------------------
      Chris Boss
      Computer Workshop
      Developer of "EZGUI"
      http://cwsof.com
      Chris Boss
      Computer Workshop
      Developer of "EZGUI"
      http://cwsof.com
      http://twitter.com/EZGUIProGuy

      Comment


      • #4
        Device Contexts 101:

        Each DC (Device Context) has a number a attributes that can
        be set , such as font, brush, bitmap (memory DC only), pen, etc.

        The SelectObject API call is one (among many) that is used to
        change some attributes of a DC.

        API functions that are used to change a DC attribute usually
        return a value which is the previous attribute value. For example
        if you change the font for a DC (using SelectObject) the return
        value is the previous (or old) font handle.

        You may have noticed a lot of API code where the old attribute
        is stored in a variable (ie. hOldFont) and then selected back into
        the DC once the drawing is done. The question is why ?

        The reason is that not all DC's are unique per each window. Actually
        most DC's are shared rather than unique. If you have a dozen controls
        on a Dialog, the controls and the Dialog itself may share the
        exact same window DC. Whether windows share a DC depends upon
        the styles used when defining the window class. A Window can have
        either a (1) shared DC with its parent, (2) Unique DC for all
        windows of the same class or (3) a unique DC per window of the
        class. Most window classes use option (1) !

        Since DC's can be shared among a number of windows, you must be
        very careful to always set back the attributes of a DC you are drawing
        into back to its original values once you finish drawing. This is
        why you see a lot of code that selects original objects back into
        the DC once done.

        While you can set back each attribute you change once your code
        finishes drawing, there is another way if you change a number of
        attributes. You can use the SaveDC and RestoreDC
        functions to save and restore the entire DC set of attributes.
        This is much easier and cleaner when making a number of changes
        to a DC.

        What are some of the common DC attributes one may change when
        drawing ?

        BackGround Color and Mode
        Text color
        Bitmap (memory DC's only)
        Brush
        Clipping region
        Pen position
        Pen
        Drawing mode
        Font
        Map mode
        Viewport attributes
        (and more ...)

        Which attributes can be set using SelectObject ?

        Bitmap
        Brush
        Font
        Pen

        The other attributes have their own unique API call to change
        the attribute, such as SetBKColor, SetBKMode, SelectClipRegion,
        etc.

        Now some DC's aren't shared and since they have their own unique
        DC, you can change attributes all you want and not need to reset
        them back until you destroy the DC (it is always good to reset
        them back before you destroy the DC).

        Rule of thumb:

        Always restore a DC's original attributes after you finish drawing
        (if the DC is shared) or before the DC is destroyed (if the DC is
        private).

        Also be aware that some APIs automatically reset a DC to its
        original settings, for example some printer APis will reset the
        DC (ie. StartDoc if I remember correctly).

        Now correct me if I am wrong, only Window DC's (Display Context)
        can share a DC. The memory DC and printer DC should always be
        a unique (private) DC.

        Now of course there may be some slight variations to what I describe
        above, so maybe a few of the GDI experts out there can fill in
        anything that is missing or over simplified.



        ------------------
        Chris Boss
        Computer Workshop
        Developer of "EZGUI"
        http://cwsof.com
        Chris Boss
        Computer Workshop
        Developer of "EZGUI"
        http://cwsof.com
        http://twitter.com/EZGUIProGuy

        Comment


        • #5
          Windows 101:

          Both Dialogs and Controls are technically Windows. A Window can have
          many properties which make it unique. A control is simply a Window
          on a Window (The dialog is the parent and the control is a child).

          All Windows ultimately are created using the Windows API function
          called CreateWindowEx ! You can create Windows directly by
          calling CreateWindowEx or you can create windows by using a higher level
          API function which internally will use CreateWindowEx.

          For example, if you use the Windows Dialog engine (which I think
          DDT uses internally) the specs for each window can be defined in
          a number of ways. You can use a Dialog in a resource file , which is
          simply the specs for the Dialog, which can be read into memory using
          the API, then the API constructs the dialog based on your specs by
          calling CreateWindowEx.

          DDT commands can be used to create a Dialog, which in essence is
          generating the specs on the fly (the DDT commands). DDT likely uses
          the many Windows Dialog functions to accomplish this and these functions
          will eventually call CreateWindowEx.

          So called SDK style coding (meaning the API directly), simply uses
          CreateWindowEx directly to create all the windows (Dialogs and controls).

          By understanding CreateWindowEx, you will understand the underlying
          concept of how windows are created.

          CreateWindowEx creates a window based on a Class name. A class
          is a specific type of window. A Dialog is a unique class. Each control
          type is a unique class (some classes produce multiple types of controls).

          Each class was created by Registering it with windows. The standard
          controls (buttons, edit, etc.) are predefined (registered) by Windows
          already. Other controls must be registered first before your program
          can create them using CreateWindowEx. The common controls for example
          must be registered for use by your program by calling its initialization
          function first, before you attempt to create any controls.

          Once a class is registered , a control can be created by using CreateWindowEx
          (or any of the higher level commands that eventually call it, such as DDT
          commands). All you need to know is the classname of the control and
          create it with the correct Styles.

          Every window has two sets of Styles (properties for a better word).
          One is the standard Styles and the other are extended Styles.
          The standard styles consist of two parts, one are generic window
          styles that affect such things as the border of a control, does it
          have a tabstop, etc. Most controls can use most of these generic
          or standard styles.

          Secondly, each class type has its own unique standard styles for that
          class, that define unigue features specific to that class.

          Lastly, the extended styles are again generic universal styles
          (or properties) for controls (or windows) that are advanced styles
          available to most windows, even though not all will support them.
          Such things as a transparent background, MDI, etc are defined by
          such styles.

          How you create your controls (call API directly or use DDT or any other
          means) is not the important thing.

          The hardest part of creating a Dialog is its initial design. It is
          easier to design it Visually, so many chose to use a Visual Designer
          of some type. If you use a Dialog Resource editor, then your final
          outcome will be a resource file, which requires using the Windows
          dialog engine to load it and display it. There are a number of
          freeware tools that can help convert dialogs from either a resource
          or a VB Form file into DDT, which are very helpful.

          The method you choose has more to do with your style of programming and
          what works best for you.



          ------------------
          Chris Boss
          Computer Workshop
          Developer of "EZGUI"
          http://cwsof.com
          Chris Boss
          Computer Workshop
          Developer of "EZGUI"
          http://cwsof.com
          http://twitter.com/EZGUIProGuy

          Comment


          • #6
            Drawing in Windows 101:

            Drawing in Windows requires an understanding of a number of
            basic concepts. First is what is called a DC.

            (1) A DC (Device Context) is basically a defined area for drawing
            with its own unique set of attributes. There are different kinds
            of DC's. One is a Window DC, which ultimately means the area
            drawn on is the video memory of the video card, so the image
            will be visible to the user. Windows shields you from direct
            access to the video memory (unless you use DirectX), but the
            image none the less will be drawn on the video memory. The second
            type of DC is a memory DC. The programmer can create any memory
            DC they like , when needed. The area drawn on will be a Bitmap
            which has been selected into the DC. A common technique is to
            create a memory DC, select a Bitmap into it and then draw on it
            and once the image is done, then BitBlt (copy) that image from
            the memory DC to a Window DC (so it is visible). Another type of
            DC is a printer DC, where the area drawn on is the printed page.

            To get access to a Window DC, one of three ways is used:

            Process the WM_PAINT message and get the DC using the BeginPaint
            API function. Now you can draw on it until Endpaint is executed.

            Use the GetDC API function to get the DC for the client area
            (inside border) of any window. You can draw on this DC and the
            image will appear on the screen immediately. You finish by
            using the ReleaseDC API function to free the DC.

            Lastly, you can use the GetWindowDC API function to get a DC for
            the entire window of any window. This includes the non-client area
            which is the border of a control. Again, once done drawing you
            must use the ReleaseDC function to free the DC.

            (2) Every DC has a set of attributes. A newly created DC has
            those attributes set to default settings (ie. a memory DC). A
            shared DC (Window DC's can be shared by many different windows)
            has the attributes which were defined the last time the DC was
            accessed. As a rule of thumb, when working with a shared DC
            (ie. window DC), you should always restore the DC's attributes
            to what they were before you starting accessing the DC. This can
            be done by using the SaveDC and RestoreDC functions.

            What are the attributes of a DC ?

            There are many, but I will list just a few here that are commonly
            used:

            Code:
            Attribute               Description                  Function to Create it   Function to Delete it    Function to set it
            ------------------------------------------------------------------------------------------------------------------------
            Pen         Line color, width and style for drawing      CreatePen              DeleteObject              SelectObject  
            Brush       8 x 8 pixel pattern for filling shapes       CreateSolidBrush       DeleteObject              SelectObject
                                                                     CreateHatchBrush
                                                                     CreatePatternBrush
            Font              Font used for drawing text             CreateFont             DeleteObject              SelectObject
            Text_FG_Color     Color of Text                                                                           SetTextColor
            Text_BG_Color     BG color behind drawn text                                                              SetBKColor 
            BG fill mode      Mode of how to fill background                                                          SetBKMode
                              (solid [filled] or transparent)
            Draw Mode         How drawn object is mixed with                                                          SetROP2
                              existing background image
            '
            '
            '
            '
            [b]Drawing Functions[/b] (some affected by above attributes):
            
            Function           Description
            ------------------------------------------------------------------------------------------------------------------------
            MoveToEx           Move Pen position to X,Y location
            SetPixel           Draw pixel by color and return previous color
            SetPixelV          Draw pixel by color
            Ellipse            Draw an ellipse
            Rectangle          Draw a Rectangle
            LineTo             Draw a line from pen position to new position
            Arc                Draw an Arc
            PolyLineTo         Draw multiple lines by values in array
            RoundRect          Draw a rounded rectangle
            Polygon            Draw a Polygon
            Pie                Draw a Pie shaped object
            BitBlt             Copy image from one DC to another (bitmap)
            StretchBlt         Copy and stretch image from one DC to another (bitmap)

            One of the best methods of drawing is to use a memory DC to
            draw on and when you need to display it on the screen, simply
            use the BitBlt function to copy it from the memory DC to the
            Window DC (during WM_PAINT).

            Here is how to create a memory DC:

            Code:
            hDC& = CreateCompatibleDC(%NULL)  ' make DC compatible with screen
            ' newly created DC has a default 1 x 1 pixel monchrome bitmap selected in it
            W& = 200 ' pixels
            H& = 200 ' pixels
            ' if you want to make bitmap the size of a windows client area
            ' use the GetClientRect function to get width and height
            hBmp& = CreateCompatibleBitmap(hDC&, W&, H&)
            OriginalBitmap& = SelectObject(hDC&, hBmp&)
            SaveDC hDC&
            '
            ' now you can draw on this DC using any drawing command
            ' you can also BitBlt this image into a window DC during WM_PAINT
            '
            '
            '
            '
            '
            '
            ' when you are finished with the memory DC and no longer need it
            ' you must do the following:
            SelectObject hDC&, OriginalBitmap&
            DeleteObject hBmp&
            RestoreDC hDC&, -1
            ' delete any still existing objects created like pens or brushes
            DeleteDC hDC&

            ------------------
            Chris Boss
            Computer Workshop
            Developer of "EZGUI"
            http://cwsof.com
            Chris Boss
            Computer Workshop
            Developer of "EZGUI"
            http://cwsof.com
            http://twitter.com/EZGUIProGuy

            Comment


            • #7
              Writing Custom Controls - 101 :

              There are three ways to produce a custom control.

              (1) Subclassing !

              If all you need to do is add a minor feature or two to an
              existing control, then you simply subclass the control which
              basically creates a hook into the controls original Window
              procedure, and then intercept and process any messages that
              you need to modify. For example, you could process the
              WM_ERASEBKGND message to draw a different type of background,
              rather than have Windows simply color it. You could trap certain
              keypresses and prevent them from coming through, by processing the
              WM_KEYDOWN/WM_KEYUP messages.

              (2) Superclassing !

              Superclassing, is where you actually create a new window class
              based on an existing one. You get information about the existing
              window class (ie. Button), which includes its window procedure
              address and then set the address to your custom controls window
              procedure. In the controls window procedure, you process many of
              the messages, but when you want to use the features of the original
              control window class, you simply pass the messages on to it by
              using the CallWindowProc function.

              Superclassing has the advantage of being able to add extra window
              bytes (a form of window data storage for each instance of the window)
              for storing information about your control, plus you have access to
              the controls WM_CREATE message (not available when subclassing).

              Superclassing is an excellent way of building custom controls.

              (3) Create a New Window Class !

              You can also build a custom control from scratch. You simply create
              a totally new window class and process its messages in a window
              procedure. This is the hardest route, since you must process more
              messages to make the control do something. While the DefWindowProc
              API function does many things for you and many messages can simply
              be passed to it, you will have to process a number of messages to
              make your control do anything valuable. On the other hand, this
              form of custom control is the most powerful, since you decide how
              everything works.

              If you would like an example of a simple custom control, you can
              download my freeware custom control sample program (full source code)
              at http://ezgui.com/page1.htm .


              There aren't a lot of good books on writing custom controls. Plus
              most new books deal with things like ActiveX which is not what you
              want for PB. The only good book I could find may be out of print,
              but you might find a copy (used) somewhere.

              The book is:

              "Windows Custom Controls"

              By William Smith and Robert Ward
              Published by R&D Technical Books
              Copyright 1993

              Now the book is about 16 bit windows programming, but few things
              have changed between 32 bit and 16 bit windows programming of custom
              controls. Some of the API stuff may be slightly different, but the
              basic concept is still the same. It would be best to check each
              API function used in the book with an up to date API reference to
              see if it has changed at all in 32 bit windows.

              I learned a lot from this book, since it explained some of the basic
              concepts quite well.

              Notes:

              With regards to subclassing, I used the word "hook" only as a
              generic term. Subclassing is a sort of hook into a window procedure
              , but it is not one of the Hooks you are refering to.

              A hook, simply means you are intercepting messages before they
              are processed by the procedure that normally handles them. Subclassing
              is a form of hook.

              The hooks you are refering to are the hook functions in the API, such
              as SetWindowsHookEx, which is used to monitor messages for either
              a specific thread (all windows in that thread) or the all threads
              (the entire system).



              ------------------
              Chris Boss
              Computer Workshop
              Developer of "EZGUI"
              http://cwsof.com
              Chris Boss
              Computer Workshop
              Developer of "EZGUI"
              http://cwsof.com
              http://twitter.com/EZGUIProGuy

              Comment


              • #8
                Debugging 101 :

                As an experienced software developer (and a good debugger) I find
                that debugging Windows applications depends more on the skill of
                the programmer than on what debugger you use. A debugger is only
                a tool and not always the most critical. Let me explain why !

                I personally find that bugs (using PB) fall into one of three
                catagories:

                (1) Errors in variable names.
                To deal with this I find two critical coding steps are absolutely
                necessary. One is always use #DIM ALL ! This is probably one of the
                most important tools built into the PB language. It prevents so
                many errors from getting through. The second is the naming of
                Global variables. One common mistake is not using prefixs. While
                C programmers tend use the syntax gMyVar& (use a small g), I find
                that isn't easy to notice at a glance, so I personally prefer to
                use a 3 letter prefix and then an underscore (ie. App_MyVar&). By
                using a consistant prefix for all global variables you can prevent
                a lot of problems and also make your code more readable.

                (2) Errors in making API calls.
                In this case it is most important to know a few things. One is
                , if the app GPF's, where did it crash (any good debugger will help
                you find approximately where the crash occured) and what operating
                system DLL was the call made to. When windows displays the error message
                dialog, it usually tells you what operating system (or other) DLL
                the crash occured in. Knowing what OS DLL the crash occured in, helps
                in figuring out what API call may have caused the problem. Another
                area where API calls create a problem is the win32api.inc file.
                Make sure that if you update it, that there aren't subtle changes
                in declares that could cause you a problem. One common problem is
                where a TYPE was changed somehow. Using tools like inclean to see
                what API's and TYPEs your app is using, can help in tracking down
                bugs in declares. This can happen more often that you think. This
                is why when I am already working on a project, I never change the
                win32api.inc file during development. I may check the new inc file
                and modify the one I am already using, but I never simply start
                using a new inc file in the middle of developing a project.

                (3) Logic Bombs
                This is the most common error and the hardest to find ! Logic bombs
                occur when the code is written incorrectly for the necessary logic
                , but no API call errors or other types of errors exist in the code.
                No debugger can find a logic bomb ! The programmer is the
                only one who can. There are tools to help find logic bombs, but
                they are not sophisticated. The tools are very simple. What are they ?

                The messagebox is a common tool to use. In many cases you can simply
                display a messagebox at a certain point in a program to test to see
                if certain code is being executed and to display a variable value
                at a certain point.

                The BEEP command is another tool. There are many situations where
                a messagebox can't be used and could even crash an app. The BEEP
                command is useful in testing whether certain code was executed. I
                use this quite often.

                Custom written debugging code. This is probably one of the most
                useful tools. You, the programmer, write code to help in debugging.
                This can include such things as a routine to write data to a text
                file during the execution of loop type code that does a lot of
                calculations. Another technique is to create a simple debug
                window (dialog) and a simple command to print data to that debug window.
                I find this invaluable when debugging event code (are certain
                messages generated and in what order). I think this should be used
                more often.

                Common Sense is the last tool ! To quote (paraphrase is more like it)
                the famous fictional character, "Sherlock Holmes",
                'after you have eliminated all possibilities that don't fit the facts
                , then what ever is left over, no matter how improbable it may seem,
                is the solution'.


                You would be amazed at how often the "Sherlock Holmes" principle
                will solve complex programming problems. It does really work !

                Now this is not to say that one should not find a good debugger !
                I just think that a debugger is not always your most critical tool.

                The PB language offers a number of useful tools in the language
                itself.

                You might find it interesting that while the software I develop
                is known for being very solid (very few if any bugs), I normally
                don't use any type of debugger (not even the PB debugger). I do
                though use all the methods described above.



                ------------------
                Chris Boss
                Computer Workshop
                Developer of "EZGUI"
                http://cwsof.com
                Chris Boss
                Computer Workshop
                Developer of "EZGUI"
                http://cwsof.com
                http://twitter.com/EZGUIProGuy

                Comment


                • #9
                  Trapping Keypresses without subclassing

                  A less known technique of trapping either keypresses
                  or mouse movement (rather than subclass) is to trap them
                  in the message loop. Rather than let DDT handle the
                  message loop, write your own. All keyboard and mouse messages
                  get sent to the message loop first, before they get passed on
                  to the window procedures of controls (or the form).

                  This way you can process all the key presses or mouse movement
                  in one place, rather than subclass every window.



                  ------------------
                  Chris Boss
                  Computer Workshop
                  Developer of "EZGUI"
                  http://cwsof.com
                  Chris Boss
                  Computer Workshop
                  Developer of "EZGUI"
                  http://cwsof.com
                  http://twitter.com/EZGUIProGuy

                  Comment


                  • #10
                    Well that's all the Tutorials I have written that I can find !

                    Please feel free to add more tutorials to this thread written
                    by anyone else !


                    Also if you have any links to web sites with good tutorials on
                    Windows subjects or would like to suggest any good books for
                    those who are learning Windows programming feel free to add them
                    here too !

                    I would suggest not posting comments or non-tutorial stuff in
                    this thread so as not to distract from it helping others.


                    If you would like to make some comments about this thread, please start
                    another thread !

                    Some Windows programming books I would highly recommend:

                    Windows 95 - A Developers Guide
                    by Jeffrey Ricter and Jonathan Locke
                    copyright 1995
                    published by M&T Books

                    Windows 98 Programming from the ground up
                    by Herbert Schildt
                    copyright 1998
                    published by Osborne McGraw Hill

                    Multithreading Applications in WIN32"
                    (the complete guide to threads)
                    by Jim Beveridge and Robert Wiener
                    copyright 1998
                    published by Addison-Wesley Developers Press



                    [This message has been edited by Chris Boss (edited July 24, 2006).]
                    Chris Boss
                    Computer Workshop
                    Developer of "EZGUI"
                    http://cwsof.com
                    http://twitter.com/EZGUIProGuy

                    Comment

                    Working...
                    X