Recent Posts

Pages: [1] 2 3 ... 10
1
The concept / FloodFill / Clipping (GDImage64 version 7.10)
« Last post by Patrice Terrier on January 11, 2022, 05:25:06 pm »
This VS2019 project, is based on GDImage64 version 7.10,

The coloring functions of GDI32 ExtFoodFill and GetPixel are working only in 24-bit RGB colors, without alpha channel.

To work around this limitation, here is a project that illustrates the clipping functions of GDImage64.
The principle is the same as in PhotoShop, a checkerboard is used in the background to visualize the transparent areas.
All fill and pixel selection functions work in 32-bit mode (with alpha channel).
When filling it is possible to use a tolerance factor, which allows to remove the artifacts caused by the jpeg compression.
Note: Cropped images must be saved in PNG format to preserve transparency.

About the interface:
This is entirely done with 2 GDImage controls, one for the work area (with the image),
the other containing the controls:
- The color selector, which allows you to use either a color picker or a combo box to choose the desired color.
- 2 vertical sliders, one to choose the level of transparency, the other for the filling tolerance.
- Several icons to choose or save an image, or cancel the modifications.
- Information concerning the coordinates of the cursor, as well as the color of the pixel flown over with its components A, R, G, B.




New in version 7.10

void zDetectDeviceAudio(IN HWND hWnd);
Populate a ComboBox or a ListBox with the name of available audio devices.

void zDetectDeviceVideo(IN HWND hWnd);
Populate a ComboBox or a ListBox with the name of available video devices.

WCHAR* ZI_SpinnerGetTime();
Get a formated string (HH:MM:SS) matching the spinner ellapsed time.

void ZI_SetHdcColorAlpha(IN HDC hDC, IN long Color, IN BYTE Alpha);
Use the HDC of a selected bitmap, to change the alpha channel of a specific color.

void ZI_SetBitmapColorAlpha(IN HBITMAP hBmp, IN long Color, IN BYTE Alpha);
Use a HBITMAP handle, to change the alpha channel of a specific color.

HBITMAP CropBitmapToBound(IN HBITMAP hBmp);
Clip all pixels using a null alpha channel.

HBITMAP ZI_CropBitmapColorToBound(IN HBITMAP hBmp, IN DWORD ARGBcolor);
Clip all pixels matching a specific Alpha, red, green, blue color.

void ZI_LinearFlood (IN HDC hDC, IN long x, IN long y, IN DWORD FillColor, IN long Tolerance);
This procedure fills an area of the display surface (HDC) with the FillColor,
using x-y coordinates for seed point (SeedColor and FillColor must be different).
The Tolerance is to consider the pixels within a specific range to match the SeedColor.

DWORD ZI_GetPixel(IN HDC hDC, IN long x, IN long y);
This is a 32-bit replacement for the 24-bit only GetPixel API, returning a color including the alpha channel.

HBITMAP ZI_CreateTileBitmap(IN WCHAR* szFileToTile, IN long Xin, IN long Yin, IN WORD nBitCount);
Create a specific 24 or 32-bit (nBitCount) tiled bitmap using a source file image to paint the Xin/Yin area of the resulting HBITMAP.

DWORD ZD_GetBitmapPixel(IN long nID, IN long x, IN long y);
Get the Alpha,red,green,blue color of a specific nID sprite object.

void ZD_LinearFlood(IN long nID, IN long x, IN long y, IN DWORD FillColor, IN long Tolerance);
This procedure fills an area of a specific nID sprite object with the FillColor,
using x-y coordinates as seed point (SeedColor and FillColor must be different).
The Tolerance is to consider the pixels within a specific range to match the SeedColor.
2
The 3D Model Collection / Ferrari Monza SP1
« Last post by Patrice Terrier on January 11, 2022, 10:47:55 am »
Ferrari Monza SP1

This model is the result of a cooperation between Saksham Kumar and Adam Wiese.
The original is available on Behance here:
https://www.behance.net/gallery/134032801/Ferrari-Monza-SP1-%28Free-Model%29

This version has been reworked with C4D in order to use the ObjReader specific materials, and to create the wavefront file.

Note: It requires the use of ObjReader version 3.00+










video


Note: Attachments are only available to registered users.
3
Post Your Questions & Comments Here / Re: PureBasic
« Last post by Patrice Terrier on December 26, 2021, 10:46:39 pm »
Good SDK coder avoid to use exotic Gadget ;)
4
Post Your Questions & Comments Here / PureBasic
« Last post by Frederick Harris on December 26, 2021, 05:27:10 pm »
I keep wondering about PureBasic.  It seems to have a lot of advantages.  I was actually thinking of buying it last summer for a try, but when I got to doing some research on it I discovered a number of things about it I didn't care for at all.  For example, one can write code like in old DOS GWBASIC or QuickBasic that isn't contained in procedures such as main(), WinMain(), PBMain(), etc.  Further, instead of using the '.' character to seperate struct/UDT members it uses the '\' character.  All that was just too strange to me.  There's a good writeup on it here at CodeProject....

https://www.codeproject.com/Articles/853831/PureBasic-The-Perfect-Cross-Platform-Native-Develo

In that article he provides an SDK GUI example as follows...

Code: [Select]
; If you want to use in GUI not only latin characters, compile it in Unicode!
; Warning: exe size - it's very, very small!

Declare.l WndProc(hWnd, Msg, wParam, lParam) ; declare Window events callback

; Global vars
WindowClass.s = "WndClass1"

; Initialize Window Class
wc.WNDCLASSEX
wc\cbSize = SizeOf(WNDCLASSEX)
wc\hbrBackground = #COLOR_WINDOW
wc\hCursor = LoadCursor_(0, #IDC_ARROW)
wc\lpfnWndProc = @WndProc()
wc\lpszClassName = @WindowClass

; register Window Class
If RegisterClassEx_(@wc) = 0
  MessageBox_(hWnd, "Can't register main window class.", "", #MB_ICONERROR)
  End
EndIf

; create window
hWnd = CreateWindowEx_(0, WindowClass, "Lorem ipsum", #WS_SYSMENU, 10, 50, 400, 200, 0, 0, 0, 0)
If hWnd = 0
  MessageBox_(hWnd, "Can't create main window.", "", #MB_ICONERROR)
  End
EndIf

; create button and set it's font
hButton1 = CreateWindowEx_(0, "Button", "Button 1", #WS_CHILD | #WS_VISIBLE, 10, 10, 100, 25, hWnd, 0, 0, 0)
SendMessage_(hButton1, #WM_SETFONT, GetStockObject_(#DEFAULT_GUI_FONT), 1)

; show window
ShowWindow_(hWnd, #SW_SHOWDEFAULT)
UpdateWindow_(hWndMain)

; messages handling loop
While GetMessage_(msg.MSG, #Null, 0, 0 )
  TranslateMessage_(msg)
  DispatchMessage_(msg)
Wend

; window events callback
Procedure.l WndProc(hWnd, Msg, wParam, lParam)
  Shared hButton1
 
  Select Msg
    Case #WM_COMMAND
      If hButton1 = lParam
        MessageBox_(hWnd, "Button 1 clicked!", "", #MB_OK)
      EndIf
    Case #WM_CLOSE
      DestroyWindow_(hWnd)
    Case #WM_DESTROY
      PostQuitMessage_(0) : Result  = 0
    Default
      Result  = DefWindowProc_(hWnd, Msg, wParam, lParam)
  EndSelect
 
  ProcedureReturn Result
EndProcedure

I'd be interested in anyone's comments on it.  Too bad PowerBASIC is left out of the 64 bit world.

5
64-bit SDK programming / Re: Drawing an audio waveform
« Last post by Patrice Terrier on December 20, 2021, 04:40:14 pm »
Here is the C++ skinned version, with a couple of changes.

Click on the top left icon (with the left or right mouse button) to change the skin wallpaper background.

6
TCLib / Re: Crash Fix For x64: Charles Petzold's PoePoem Ch 10 Project
« Last post by Frederick Harris on December 19, 2021, 12:03:16 am »
Code: [Select]
/*----------------------------
   PoePoem1.rc resource script

   rc /r /v PoePoem1.rc
   WindRes -i PoePoem1.rc -o PoePoem1_res.obj -v
  ----------------------------*/
#include "PoePoem1.h"

PoePoem1    ICON  PoePoem1.ico
AnnabelLee  TEXT  PoePoem1.txt

STRINGTABLE
{
 IDS_APPNAME, "PoePoem1"
 IDS_CAPTION, """Annabel Lee"" by Edgar Allen Poe"
 IDS_POEMRES, "AnnabelLee"
}


Code: [Select]
/*-----------------------
   PoePoem1.h header file
  -----------------------*/
#define IDS_APPNAME 0
#define IDS_CAPTION 1
#define IDS_POEMRES 2


Code: [Select]
// Main.h
#ifndef Main_h
#define Main_h

#define dim(x)                          (sizeof(x) / sizeof(x[0]))
#define TYPEFACE_NAME                   L"Times New Roman"
#define FONT_SIZE                       12

struct                                  WndEventArgs
{
 HWND                                   hWnd;
 WPARAM                                 wParam;
 LPARAM                                 lParam;
 HINSTANCE                              hIns;
};

LRESULT CALLBACK fnWndProc_OnCreate     (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnSize       (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnVScroll    (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnHScroll    (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnMouseWheel (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnPaint      (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnDestroy    (WndEventArgs& Wea); 

struct                                  EVENTHANDLER
{
 unsigned int                           iMsg;
 LRESULT                                (CALLBACK* fnPtr)(WndEventArgs&);
};

const EVENTHANDLER                      EventHandler[]=
{
 {WM_CREATE,                            fnWndProc_OnCreate},
 {WM_SIZE,                              fnWndProc_OnSize},
 {WM_VSCROLL,                           fnWndProc_OnVScroll},
 {WM_HSCROLL,                           fnWndProc_OnHScroll},
 {WM_MOUSEWHEEL,                        fnWndProc_OnMouseWheel},
 {WM_PAINT,                             fnWndProc_OnPaint},
 {WM_DESTROY,                           fnWndProc_OnDestroy}
};

struct ScrollData
{
 wchar_t**                              pPtrs;
 int                                    iNumLines;
 int                                    cxChar;
 int                                    cxCaps;
 int                                    cyChar;
 int                                    cxClient;
 int                                    cyClient;
 int                                    iMaxWidth;
};

#endif

Code: [Select]
//#define TCLib      // PoePoem1.cpp
#ifndef UNICODE      // cl  PoePoem1.cpp /O1 /Os /MT  user32.lib gdi32.lib PoePoem1.res                 // 99,328 Bytes;
   #define UNICODE   // cl  PoePoem1.cpp /O1 /Os /GR- /GS- TCLib.lib user32.lib gdi32.lib PoePoem1.res  // 10,752 Bytes
#endif               // g++ PoePoem1.cpp PoePoem1_res.obj -o PoePoem1_gcc.exe -mwindows -m64 -s -Os     // 25,600 Bytes
#ifndef _UNICODE
   #define _UNICODE
#endif
#include <Windows.h>
#ifdef TCLib
   #include "string.h"
   #include "stdio.h"
   #include "memory.h"
#else
   #include <string.h>
   #include <stdio.h>
   #include <wchar.h>
#endif
#include "Main.h"
#include "PoePoem1.h"


LRESULT CALLBACK fnWndProc_OnCreate(WndEventArgs& Wea)
{
 ScrollData* pScrDta   = NULL;
 char*       pText     = NULL;
 wchar_t*    pWText    = NULL;
 wchar_t*    p         = NULL;
 HGLOBAL     hResource = NULL;
 HANDLE      hHeap     = NULL;
 HRSRC       hFoundRes = NULL;
 HDC         hdc       = NULL;
 HFONT       hFont     = NULL;
 size_t      iStrLen   = 0;
 char        szPoemRes[16];
 wchar_t     szBuffer[128];
 TEXTMETRIC  tm;

 Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;              // Get HINSTANCE From CREATESTRUCT Object
 LoadStringA(Wea.hIns,IDS_POEMRES,szPoemRes,sizeof(szPoemRes)); // Get Name Of Custom TEXT Resource "AnnabelLee" (It's Asci)
 hFoundRes = FindResourceA(Wea.hIns,szPoemRes,"TEXT");          // Find Custom Resource In Binary Executable
 hResource = LoadResource(Wea.hIns,hFoundRes);                  // Get HANDLE To Resource, So We Can Get Pointer To It.
 pText=(char*)LockResource(hResource);                          // Get Pointer To First Byte Of Custom Resource
 
 hHeap=GetProcessHeap();                                        // Will Need To Allocate Memory For A ScrollData Object
 pScrDta=(ScrollData*)HeapAlloc                                 // (See Main.h) So As To Eliminate Charles Petzold's
 (                                                              // Static And Global C Variables.  We'll Store The
  hHeap,HEAP_ZERO_MEMORY,sizeof(ScrollData)                     // Allocated Object As Part Of The App's WNDCLASSEX
 );                                                             // cbWndExtra Bytes, Which Is Kind Of A C Ism For A
 if(!pScrDta)                                                   // C++ Class Member Variable.  If -1 Is Returned From
    return -1;                                                  // WM_CREATE Handler Code, The CreateWindowEx() Call
 SetWindowLongPtr(Wea.hWnd,0,(LONG_PTR)pScrDta);                // In WinMain() Will Fail, And App Will Terminate.

 hdc = GetDC(Wea.hWnd);                                         // To Get Scroll Bars And Scrolling Working, One Needs
 hFont=CreateFont                                               // To Know, For Example, The Size (Height) Of A Char-
 (                                                              // acter In Relation To The Size Of The Client Area Of
   -1*(FONT_SIZE*GetDeviceCaps(hdc,LOGPIXELSY))/72,             // The Window Where Text Is To Be Displayed And Scrolled.
   0,0,0,FW_SEMIBOLD,0,0,0,ANSI_CHARSET,0,0,DEFAULT_QUALITY,    // So We Need To Create A Font That The App Will Use,
   0,TYPEFACE_NAME                                              // And Get It's Metrics Using GetTextMetrics().  We'll
 );                                                             // Store This Info In The ScrollData Persisted Object,
 if(!hFont)                                                     // And Let It Up To The WM_SIZE Handler Code To Deal
    return -1;                                                  // With The Screen Size Stuff.  We Gotta Be Careful To
 HFONT hTmp=(HFONT)SelectObject(hdc,hFont);                     // Delete The Dynamically Allocated Resource.
 GetTextMetrics(hdc, &tm);
 pScrDta->cxChar = tm.tmAveCharWidth;
 pScrDta->cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * pScrDta->cxChar / 2;
 pScrDta->cyChar = tm.tmHeight + tm.tmExternalLeading;
 DeleteObject(SelectObject(hdc,hTmp));
 ReleaseDC(Wea.hWnd, hdc);
 
 iStrLen=MultiByteToWideChar(CP_ACP,0,pText,-1,NULL,0);         // We Need To Convert Asci "Annabel Lee" Poem Read From
 pWText=(wchar_t*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iStrLen*2);  // Executable Resource To Wide Character For GUI Output
 MultiByteToWideChar(CP_ACP,0,pText,-1,pWText,iStrLen);         // In WM_PAINT Handler Code.  We'll Use MultiByteToWideChar().
 
 p=pWText;      // Run Through Annabel Lee Poem Counting Line Feed Characters Because, For The Scroll Logic This App
 while(*p)      // Employs Using TextOut() In WM_PAINT Handler Code, We Need To Break The Poem Up Into An Array Of Lines. 
 {              // We'll Persist The Count Obtained Here In Our ScrollData Object, Which Becomes Part Of The
   if(*p==13)   // WNDCLASSEX::cbWndExtra Bytes.  This Eliminates Charles Petzold's statics and globals.
      pScrDta->iNumLines++;
   p++;
 }
 pScrDta->pPtrs=(wchar_t**)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,pScrDta->iNumLines*sizeof(wchar_t**));
 if(pScrDta->pPtrs)
 {                                              // This Code Will Break The CR-LF Delimited Poem Lines From The Resources
    size_t j;                                   // In The Executable Into An Array Of Character Pointers -- All Contained
    p=pWText;                                   // Within The ScrollData Object.  Wouldn't Surprise Me If There Was
    for(size_t i=0; i<pScrDta->iNumLines; i++)  // Something Or Other In The C++ Specific Part Of The Standard Library To
    {                                           // Do This Easier, But Since I'm A Hard Core C Coder Code Like This Doesn't
        wmemset(szBuffer,'\0',128);             // Bother Me. Especially Since I Can Build This x64 Using My Own Custom C
        j=0;                                    // Runtime In 10 K, As Opposed To A Megabyte Sized Executable If Linked
        while(true)                             // Against MS's LibCmt.  So What The Code Does Is Run Through The Poem Byte
        {                                       // By Byte Detecting When A NewLine Sequence (CR-LF) Has Delimited A Line.
           if(*p==10)                           // Up Until That Point, Ordinary Printable Characters Are Accumulated In
              p++;                              // szBuffer[], But When A Newline Is Encountered The CR Is Replaced By A       
           if(*p==13)                           // NULL Byte Terminating The String In szBuffer.  Then We Make A Memory Alloc-
           {                                    // ation For The Delimited Line And Store It In ScrollData.
              szBuffer[j]=L'\0';
              iStrLen=wcslen(szBuffer);
              if(iStrLen>pScrDta->iMaxWidth)
                 pScrDta->iMaxWidth=iStrLen;
              pScrDta->pPtrs[i]=(wchar_t*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iStrLen*2+2);
              wcscpy(pScrDta->pPtrs[i],szBuffer);
              p++;
              break;
           }
           szBuffer[j]=*p;
           p++,j++;
           
        }
    }
    pScrDta->iMaxWidth=pScrDta->iMaxWidth*pScrDta->cxChar;
 }
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnSize(WndEventArgs& Wea)
{
 ScrollData* pScrDta=NULL;
 SCROLLINFO si;

 pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
 if(pScrDta)
 {
    pScrDta->cxClient = LOWORD(Wea.lParam);
    pScrDta->cyClient = HIWORD(Wea.lParam);
    si.cbSize = sizeof(si) ;
    si.fMask  = SIF_RANGE | SIF_PAGE;
    si.nMin   = 0;
    si.nMax   = pScrDta->iNumLines - 1;
    si.nPage  = pScrDta->cyClient / pScrDta->cyChar;
    SetScrollInfo(Wea.hWnd, SB_VERT, &si, TRUE);
    si.cbSize = sizeof(si);
    si.fMask  = SIF_RANGE | SIF_PAGE;
    si.nMin   = 0;
    si.nMax   = pScrDta->iMaxWidth / pScrDta->cxChar;
    si.nPage  = pScrDta->cxClient / pScrDta->cxChar;
    SetScrollInfo(Wea.hWnd, SB_HORZ, &si, TRUE);
 }
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnVScroll(WndEventArgs& Wea)
{
 ScrollData* pScrDta=NULL;
 SCROLLINFO si;

 pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
 if(pScrDta)
 {
    si.cbSize = sizeof(si) ;// Get all the vertial scroll bar information
    si.fMask  = SIF_ALL ;
    GetScrollInfo(Wea.hWnd, SB_VERT, &si);
    int iVertPos = si.nPos; // Save the position for comparison later on
    switch (LOWORD(Wea.wParam))
    {
      case SB_TOP:
           si.nPos = si.nMin ;
           break ;
      case SB_BOTTOM:
           si.nPos = si.nMax ;
           break ;
      case SB_LINEUP:
           si.nPos -= 1 ;
           break ;
      case SB_LINEDOWN:
           si.nPos += 1 ;
           break ;
      case SB_PAGEUP:
           si.nPos -= si.nPage ;
           break ;
      case SB_PAGEDOWN:
           si.nPos += si.nPage ;
           break ;
      case SB_THUMBTRACK:
           si.nPos = si.nTrackPos ;
           break ;
      default:
           break ;
    }
    si.fMask = SIF_POS ;
    SetScrollInfo(Wea.hWnd, SB_VERT, &si, TRUE);
    GetScrollInfo(Wea.hWnd, SB_VERT, &si);
    if(si.nPos != iVertPos)
    {
       ScrollWindow(Wea.hWnd, 0, pScrDta->cyChar*(iVertPos-si.nPos), NULL, NULL);
       UpdateWindow(Wea.hWnd);
    }
 }
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnHScroll(WndEventArgs& Wea)
{
 ScrollData* pScrDta=NULL;
 SCROLLINFO si;

 pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
 if(pScrDta)
 {
    si.cbSize = sizeof (si);// Get all the horizontal scroll bar information
    si.fMask  = SIF_ALL;
    GetScrollInfo(Wea.hWnd, SB_HORZ, &si) ;// Save the position for comparison later on
    int iHorzPos = si.nPos;
    switch (LOWORD(Wea.wParam))
    {
      case SB_LINELEFT:
           si.nPos -= 1 ;
           break ;
      case SB_LINERIGHT:
           si.nPos += 1 ;
           break ;
      case SB_PAGELEFT:
           si.nPos -= si.nPage ;
           break ;
      case SB_PAGERIGHT:
           si.nPos += si.nPage ;
           break ;
      case SB_THUMBTRACK:              // case SB_THUMBPOSITION:
           si.nPos = si.nTrackPos ;
           break ;
      default :
           break ;
    }
    si.fMask = SIF_POS;
    SetScrollInfo(Wea.hWnd, SB_HORZ, &si, TRUE);
    GetScrollInfo(Wea.hWnd, SB_HORZ, &si);
    if(si.nPos != iHorzPos)
       ScrollWindow(Wea.hWnd, pScrDta->cxChar*(iHorzPos-si.nPos), 0, NULL, NULL);
 }
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnMouseWheel(WndEventArgs& Wea)
{
 int zdelta=GET_WHEEL_DELTA_WPARAM(Wea.wParam);
 if(zdelta>0)
 {
    for(int i=0; i<10; i++)
        SendMessage(Wea.hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEUP,0),0);
 }
 else
 {
    for(int i=0; i<10; i++)
        SendMessage(Wea.hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),0);
 }
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnPaint(WndEventArgs& Wea)
{
 int x,y,iPaintBeg,iPaintEnd,iVertPos,iHorzPos;
 ScrollData* pScrDta=NULL;
 HFONT hFont=NULL;
 PAINTSTRUCT ps;
 SCROLLINFO si;
 HDC hdc;

 hdc = BeginPaint(Wea.hWnd, &ps);
 pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
 if(pScrDta)
 {
   
    hFont=CreateFont
    (
     -1*(FONT_SIZE*GetDeviceCaps(hdc,LOGPIXELSY))/72,0,0,0,FW_SEMIBOLD,0,0,0,ANSI_CHARSET,0,0,DEFAULT_QUALITY,0,TYPEFACE_NAME
    );
    HFONT hTmp=(HFONT)SelectObject(hdc,hFont);
    si.cbSize = sizeof (si) ;// Get vertical scroll bar position
    si.fMask  = SIF_POS ;
    GetScrollInfo(Wea.hWnd, SB_VERT, &si), iVertPos = si.nPos;
    GetScrollInfo(Wea.hWnd, SB_HORZ, &si), iHorzPos = si.nPos;
    if(iVertPos+ps.rcPaint.top/pScrDta->cyChar>0)
       iPaintBeg=iVertPos + ps.rcPaint.top / pScrDta->cyChar;
    else
       iPaintBeg=0;
    if(iVertPos + ps.rcPaint.bottom / pScrDta->cyChar < pScrDta->iNumLines - 1)
       iPaintEnd=iVertPos + ps.rcPaint.bottom / pScrDta->cyChar;
    else
       iPaintEnd=pScrDta->iNumLines-1;
    for(int i = iPaintBeg; i<= iPaintEnd; i++)
    {
        x = pScrDta->cxChar * (1 - iHorzPos);
        y = pScrDta->cyChar * (i - iVertPos);
        TextOut(hdc, x, y, pScrDta->pPtrs[i], (int)wcslen(pScrDta->pPtrs[i]));
    }
    DeleteObject(SelectObject(hdc,hTmp));
 }
 EndPaint(Wea.hWnd, &ps);
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 ScrollData* pScrDta = NULL;
 HANDLE hHeap        = NULL;
 
 hHeap=GetProcessHeap();
 pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
 if(pScrDta->pPtrs)
 {
    for(size_t i=0; i<pScrDta->iNumLines; i++)
        HeapFree(hHeap,0,pScrDta->pPtrs[i]);
    HeapFree(hHeap,0,pScrDta->pPtrs);
 }
 HeapFree(hHeap,0,pScrDta);
 PostQuitMessage(0);
 
 return 0;
}


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(size_t i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(Wea);
     }
 }

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}

 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
 char       szAppName[64];
 char       szCaption[64];
 wchar_t    szWAppName[16];
 wchar_t    szWCaption[40];
 HWND       hWnd = NULL;
 WNDCLASSEX wc   = {0};
 MSG        msg;
 
 LoadStringA(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));
 LoadStringA(hInstance, IDS_CAPTION, szCaption, sizeof(szCaption));
 MultiByteToWideChar(CP_UTF8,0,szAppName,-1,szWAppName,16);
 MultiByteToWideChar(CP_UTF8,0,szCaption,-1,szWCaption,40);
 wc.cbSize        = sizeof(wc);
 wc.style         = CS_HREDRAW | CS_VREDRAW;
 wc.lpfnWndProc   = fnWndProc;
 wc.cbClsExtra    = 0;
 wc.cbWndExtra    = sizeof(void*);
 wc.hInstance     = hInstance;
 wc.hIcon         = LoadIcon(hInstance, szWAppName);
 wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wc.lpszMenuName  = NULL;
 wc.lpszClassName = szWAppName;
 wc.hIconSm       = LoadIcon(hInstance, szWAppName);
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szWAppName,szWCaption,WS_OVERLAPPEDWINDOW|WS_VISIBLE,300,150,500,800,NULL,NULL,hInstance,NULL);
 UpdateWindow(hWnd);
 while(GetMessage(&msg, NULL, 0, 0))
 {
   TranslateMessage(&msg) ;
   DispatchMessage(&msg) ;
 }
 
 return (int)msg.wParam;
}

     I’ve played with this on and off for a couple weeks, and have quite a few versions on my laptops.  To use my code to build the apps you may want to change filenames to suit yourself.  And you’ll need Charles’ PoePoem.ico and PoePoem.txt or PoePoem.asc files.  You’ll need to change file names in a lot of places to get it to all work out, come up with the right icon, etc.  Easiest thing to do would be to use my file names, and just rename his PoePoem.ico and PoePoem.txt or PoePoem.asc files.

7
TCLib / Crash Fix For x64: Charles Petzold's PoePoem Ch 10 Project
« Last post by Frederick Harris on December 18, 2021, 11:53:35 pm »
     Charles Petzold's Chapter 10 project PoePoem builds OK with x64 but crashes on startup.  It doesn't 'hang' in memory requiring Task Manager to end it - it simply terminates with no GUI window becoming visible.  After extensively debugging it I found the source of the crash and can provide fixes for it.  I believe it's a Data Execution Prevention (DEP) issue specific to x64, but whether I'm right or not about my diagnosis of ultimate causes, my fix will work.

     Anyway, in terms of background, in all my years of API SDK coding I never availed myself of string tables or custom resources in my code or resource file usage.  Petzold covers this so when I wanted to experiment and learn about it I naturally pulled out my Petzold Windows Programming books, of which I have two - his 4th edition Windows 95 book which is pure asci, and his Windows 98 5th edition where he tried to convert everything to Microsoft's version of UNICODE.   I guess since I learned Windows API coding with his Windows 95 book, I started with his C code from that book.

     Before I get started though, let me say that for possible copyright and intellectual property reasons, I don't want to post any of Charles' original code here.  My programs are quite heavily modified C++ versions of his that use my coding styles and conventions, and quite a lot of different code, and I feel OK about posting these, which I'll do.  However, if you don't have his disks, whether it's legal or ethical or not, I do believe Charles' original C code programs can be found on github, and perhaps elsewhere on the internet.  Here is a github link....

https://github.com/yottaawesome/programming-windows-5th-edition

     Basically, what the PoePoem project is about, is embedding Edgar Allen Poe’s "Annabell Lee" poem (48 lines) in the resource section of an executable.  Also, he uses a string table to hold the name of the app, the caption to appear in the single window created, and the name of the custom resource to be embedded in the executable…

Code: [Select]
// PoePoem5.h header file
#define IDS_APPNAME 0
#define IDS_CAPTION 1
#define IDS_POEMRES 2

Code: [Select]
/*----------------------------
   PoePoem5.rc resource script

   rc /r /v PoePoem5.rc
  ----------------------------*/
#include "PoePoem5.h"

PoePoem5    ICON  PoePoem5.ico
AnnabelLee  TEXT  PoePoem5.txt

STRINGTABLE
{
 IDS_APPNAME, "PoePoem5"
 IDS_CAPTION, """Annabel Lee"" by Edgar Allen Poe"
 IDS_POEMRES, "AnnabelLee"
}

So in WinMain() Charles calls Api function LoadString twice to obtain his szAppName[] and szCaption[] variables from the string data embedded in the resource section of the executable….

Code: [Select]
LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));
LoadString(hInstance, IDS_CAPTION, szCaption, sizeof(szCaption));

With these in hand he can do his CreateWindow() call to create his main program window….

Code: [Select]
hWnd=CreateWindow(szAppName,szCaption,WS_OVERLAPPEDWINDOW,400,200,500,700,NULL,NULL,hInstance,NULL);

Of course, at that point code execution will enter a WM_CREATE handler, where Charles runs through his typical drill of extracting TEXTMETRIC data so as to initialize variables he’ll need to set up scrolling.  As is typical of his code, he declares quite a few globals and statics to persist data across Window Procedure function calls.  After doing that, and still in WM_CREATE code, he gets down to the major business of the app, which is to extract Poe’s poem embedded in Asci text file format from the resource section of the executable.  Here is a reasonable facsimile of the code he runs...

Code: [Select]
HRSRC     hSrc        = NULL;
HGLOBAL   hResource   = NULL;
HINSTANCE hInst       = NULL;
Char*     pText       = NULL;
Int       iNumLines   = 0;

hinst     = GetModuleHandle(NULL);                                   // Get HINSTANCE Of App
HSrc      = FindResource(hInst, TEXT ("AnnabelLee"), TEXT ("TEXT")); // Find Text Resource “AnnabelLee"
hResource = LoadResource(hInst, hSrc, TEXT ("TEXT"));                // Load Resource “AnnabelLee”
pText     = (char*)LockResource(hResource);                          // Get char* To Resource
while(*pText != '\\' && *pText != '\0')                              // Run Through Text Data Of Poem Until
{                                                                    // A Forward Slash Or NULL Char Is Hit,
   if(*pText == '\n')                                                // Counting Newlines Along The Way. 
      iNumLines ++;                                                  // AnsiNext() Is Obsolete, But Is Same
   pText = AnsiNext(pText);                                          // As pText++, i.e., Move To Next Char.
}                                                                    // *pText = '\0' Is A Memory Write
*pText = '\0';                                                       // Operation, And It Crashes The App!

     Some context is necessary to understand all this.  Charles decided to use DrawText() to draw the poem in the client area of the app’s window.   He likely chose DrawText() instead of TextOut() because DrawText() translates CRLFs and TextOut() doesn't.  Here is an abbreviated copy of the first and last few verses of the poem (it really is a nice poem, too)....

It was many and many a year ago,
   In a kingdom by the sea,
That a maiden there lived whom you may know
   By the name of Annabel Lee;
And this maiden she lived with no other thought
   Than to love and be loved by me.

I was a child and she was a child
   In this kingdom by the sea,
But we loved with a love that was more than love --
   I and my Annabel Lee --
With a love that the winged seraphs of Heaven
   Coveted her and me.
....
....
....
For the moon never beams, without bringing me dreams
   Of the beautiful Annabel Lee;
And the stars never rise, but I feel the bright eyes
   Of the beautiful Annabel Lee:
And so, all the night-tide, I lie down by the side
Of my darling -- my darling -- my life and my bride,
   In her sepulchre there by the sea --
   In her tomb by the sounding sea.
 
                                       [May, 1849]
/

Note the forward slash at the end of the poem.  I don't know why Charles felt it necessary to add that.  But it is there in the original text file that he embeds in the resources, and in his code he attempts to remove it so it doesn't show up in his DrawText() output.  As you can see in his while loop, hitting the forward slash character is one of the conditions that causes the while loop to terminate, at which point the next line of code is a memory write operation like so where he attempts to place a NULL byte where the slash existed....

Code: [Select]
*pText = '\0';   CRASHES!!!!!

Now, there's nothing wrong, usually, with such memory write operations using pointers - I do it all the time, but here it crashes.  In fact, it doesn't matter where one attempts to write memory in that text file - from the first byte all the way to the last.  It crashes the app no matter what.  Just for further information, here is a byte dump of the end of the 1659 byte file....

1643   32   
1644   32   
1645   32   
1646   91   [
1647   77   M
1648   97   a
1649   121   y
1650   44   ,
1651   32   
1652   49   1
1653   56   8
1654   52   4
1655   57   9
1656   93   ]
1657   13   
1658   10   
1659   47   /
1660   0   

Both Windows 'Properties' and a wcslen() call within the code itself reveal the file to be 1659 bytes.  So what I did to fix the program, and solve the crash, is to simply remove Charles' memory write operation, and modify his DrawText() function call so as to not write the forward slash character.  Here is Charles' DrawText() call in his WM_PAINT handler code...

Code: [Select]
DrawText(hdc, pText, -1, &rect, DT_EXTERNALLEADING);

The -1 for the 3rd parameter tells Windows to assume a NULL terminated string - which gets the '\\' character printed if it's there.  And if we don't over-write it with a NULL, so the app won't crash, it'll be there.  To get rid of it just do a wcslen() call instead where we subtract -3 from the string length, which yields us 1659 - 3 = 1656.  That will get rid of the slash, a line feed, and a carriage return, and back us out to the closing bracket ']' character (see byte dump above).  No more memory write operations and no more crashes!  Here then, would be the fix...

Code: [Select]
DrawText(hdc, pText, wcslen(pText)-3, &rect, DT_EXTERNALLEADING);


Here is my first version of Charles' fixed program, which I've converted to wide character....

Code: [Select]
// PoePoem5.h header file
#define IDS_APPNAME 0
#define IDS_CAPTION 1
#define IDS_POEMRES 2

Code: [Select]
/*----------------------------
   PoePoem5.rc resource script
   rc /r /v PoePoem5.rc
  ----------------------------*/
#include "PoePoem5.h"

PoePoem5    ICON  PoePoem5.ico
AnnabelLee  TEXT  PoePoem5.txt

STRINGTABLE
{
 IDS_APPNAME, "PoePoem5"
 IDS_CAPTION, """Annabel Lee"" by Edgar Allen Poe"
 IDS_POEMRES, "AnnabelLee"
}

Code: [Select]
// Main.h
#ifndef Main_h
#define Main_h

#define dim(x)                          (sizeof(x) / sizeof(x[0]))

#ifndef max
#define max(a,b)                        (((a) > (b)) ? (a) : (b))
#endif

#ifndef min
#define min(a,b)                        (((a) < (b)) ? (a) : (b))
#endif

struct                                  WndEventArgs
{
 HWND                                   hWnd;
 WPARAM                                 wParam;
 LPARAM                                 lParam;
 HINSTANCE                              hIns;
};

LRESULT CALLBACK fnWndProc_OnCreate     (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnSize       (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnSetFocus   (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnVScroll    (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnPaint      (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnDestroy    (WndEventArgs& Wea); 

struct                                  EVENTHANDLER
{
 unsigned int                           iMsg;
 LRESULT                                (__stdcall* fnPtr)(WndEventArgs&);
};

const EVENTHANDLER                      EventHandler[]=
{
 {WM_CREATE,                            fnWndProc_OnCreate},
 {WM_SIZE,                              fnWndProc_OnSize},
 {WM_SETFOCUS,                          fnWndProc_OnSetFocus},
 {WM_VSCROLL,                           fnWndProc_OnVScroll},
 {WM_PAINT,                             fnWndProc_OnPaint},
 {WM_DESTROY,                           fnWndProc_OnDestroy}
};

struct                                  ScrollData
{
 HWND                                   hScroll;
 int                                    iPosition;
 int                                    cxChar;
 int                                    cyChar;
 int                                    cyClient;
 int                                    iNumLines;
 int                                    xScroll;
 int                                    iVisible;
 int                                    iRange;
};

struct                                  ResourceData
{
 HGLOBAL                                hResource;
 wchar_t*                               pWText;
 char                                   szPoemRes[16];
};

struct                                  AppData
{
 ScrollData                             sd;
 ResourceData                           rd;
};

#endif 

Code: [Select]
//#define TCLib        // PoePoem5.cpp
#ifndef UNICODE        // cl PoePoem5.cpp /O1 /Os /MT  user32.lib gdi32.lib PoePoem5.res                 // 97,792 Bytes;
   #define UNICODE     // g++ PoePoem5.cpp -luser32 -lkernel32 -lgdi32  -mwindows -m64 -s -Os            // 24,576 Bytes;
#endif                 // cl PoePoem5.cpp /O1 /Os /GR- /GS- TCLib.lib user32.lib gdi32.lib PoePoem5.res  //  9,728 Bytes;
#ifndef _UNICODE       // This code is a redo of Charles Petzold's PoePoem Project from Chapter 10 of his 5th edition
   #define _UNICODE    // book.  His original code builds for me in x64 but crashes on startup.  I think it may be a DEP
#endif                 // (Data Execution Prevention) issue, in that Petzold's original code attempts to modify, i.e.,
#include <Windows.h>   // 'write', memory within the resource section of the built executable.  I believe his original
#ifdef TCLib           // code will build/run x86, where DEP can either be turned off, or an app 'white listed' so as to
   #include "string.h" // get around DEP.  Apparently, x64 closes this loop hole, as I tried to 'white list' it, but
   #include "stdio.h"  // received an operating system message such that x64 binaries can't be 'white listed', or have
   #include "memory.h" // DEP turned off.  If I ever did know that I may have forgotten it, as I haven't really looked
#else                  // at or studied DEP issues for a long time.  Anyway, in my opinion this redo of mine was justified
   #include <string.h> // in that Charles' original code was really a very minimal UNICODE adaptation of his original 
   #include <stdio.h>  // Windows 95 version of his 4th edition book, which was pure ansi.  Seems reasonable to me to pull
   #include <wchar.h>  // ansi (UTF-8) text data into an otherwise UNICODE app, as most text files are still single byte
#endif                 // data, which saves space.   But Charles original code in his UNICODE adaptation still output
#include "Main.h"      // the text, i.e., Poe's poem, in ansi format specifically using DrawTextA().  So what I did in
#include "PoePoem5.h"  // this 'redo' is load the resources with the API 'A' functions - had no choice in this really...


LRESULT CALLBACK fnWndProc_OnCreate(WndEventArgs& Wea)
{
 HDC        hDC       = NULL;   // ...as the resource data was pulled into the executable as ansi, but then I immediately
 HINSTANCE  hInst     = NULL;   // converted everything to wide character using MultiByteToWideChar() Api function.  Also,
 AppData*   pAppData  = NULL;   // I converted everything to C++ (at least my way of doing C++), and eliminated all
 HANDLE     hHeap     = NULL;   // Charles' static and global variables, of which I take a somewhat dim view.  To under-
 HRSRC      hFoundRes = NULL;   // stand my code, see ScrollData, ResourceData, and AppData objects in Main.h.  These
 char*      pText     = NULL;   // objects encapsulate all Charles' globals and statics from his C code.  I HeapAlloc() 
 char*      p         = NULL;   // memory in WM_CREATE handler code (just left and down) to persist this data across
 TEXTMETRIC tm        =  {0};   // function calls, and I release memory in WM_DESTROY handler code.  Further, my code
 int        iStrLen   =   0 ;   // eliminates the switch logic construct in the Window Procedure to map incomming....
 
 hHeap    = GetProcessHeap();                                             //  ...messages to the code (event procedure)
 Wea.hIns = ((LPCREATESTRUCT)Wea.lParam)->hInstance;                      // which handles each respective message.
 pAppData = (AppData*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(AppData));  // Otherwise, a good bit of the code here is
 if(!pAppData)                                                            // right out of Charles' WM_CREATE code.
    return -1;                                                            // Except note below left my use of
 SetWindowLongPtr(Wea.hWnd,0,(LONG_PTR)pAppData);                         // MultiByteToWideChar() to convert Poe's
 hDC = GetDC(Wea.hWnd);                                                   // poem in the resource data to Windows
 GetTextMetrics(hDC,&tm);                                                 // UNICODE, and store it in heap memory, as
 pAppData->sd.cxChar = tm.tmAveCharWidth;                                 // opposed to utilizing it in it's original
 pAppData->sd.cyChar = tm.tmHeight + tm.tmExternalLeading;                // ansi form in the resource data. 
 ReleaseDC(Wea.hWnd, hDC);
 pAppData->sd.xScroll=GetSystemMetrics(SM_CXVSCROLL);
 pAppData->sd.hScroll=CreateWindow(L"scrollbar",NULL,WS_CHILD|WS_VISIBLE|SBS_VERT,0,0,0,0,Wea.hWnd,(HMENU)1,Wea.hIns,NULL);
 LoadStringA(Wea.hIns,IDS_POEMRES,pAppData->rd.szPoemRes,sizeof(pAppData->rd.szPoemRes));
 hFoundRes=FindResourceA(Wea.hIns,pAppData->rd.szPoemRes,"TEXT");
 pAppData->rd.hResource = LoadResource(Wea.hIns,hFoundRes);
 pText=(char*)LockResource(pAppData->rd.hResource);
 p=pText;
 while(*pText != '\0')           // This code runs through the resource data containing Poe's Annabel Lee poem counting
 {                               // CR-LFs so as to obtain the number of lines in the file, which I believe is somewhere
   if(*pText == '\n')            // around 48.  Note I eliminated Charles' AnsiNext() function (you'll see that if you
      pAppData->sd.iNumLines++;  // have Charles' original code), as it's obsolete, and I don't have a clue what good it
   pText++;                      // ever served.  The number of lines is needed to set up scrolling.
 }
 pText = p;
 SetScrollPos(pAppData->sd.hScroll,SB_CTL,0,FALSE);                         // Here's where we'll use MultiByteToWideChar()
 iStrLen=MultiByteToWideChar(CP_ACP,0,pText,-1,NULL,0);                     // to convert ansi data to wide character. 
 pAppData->rd.pWText=(wchar_t*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iStrLen*2); // Note use of said function to obtain buffer
 MultiByteToWideChar(CP_ACP,0,pText,-1,pAppData->rd.pWText,iStrLen);        // size for UNICODE converted string. 
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnSize(WndEventArgs& Wea)  // The WM_SIZE handler code is important to set up the scrolling
{                                                     // functionality of the app.  One needs to know, for example, the
 AppData* pAppData = NULL;                            // size relationships between the height of a character, and the
                                                      // height of the client area of the window where text will be drawn.
 pAppData=(AppData*)GetWindowLongPtr(Wea.hWnd,0);
 pAppData->sd.iVisible = HIWORD(Wea.lParam)/pAppData->sd.cyChar;
 pAppData->sd.iRange  = pAppData->sd.iNumLines - pAppData->sd.iVisible + 1;
 MoveWindow
 (
  pAppData->sd.hScroll,
  LOWORD(Wea.lParam)-pAppData->sd.xScroll,
  0,
  pAppData->sd.xScroll,
  pAppData->sd.cyClient=HIWORD(Wea.lParam),
  TRUE
 );
 SetScrollRange(pAppData->sd.hScroll,SB_CTL,0,pAppData->sd.iRange,FALSE); 
 SetFocus(Wea.hWnd);
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnSetFocus(WndEventArgs& Wea)
{
 AppData* pAppData = NULL;
 
 pAppData=(AppData*)GetWindowLongPtr(Wea.hWnd,0);
 SetFocus(pAppData->sd.hScroll);
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnVScroll(WndEventArgs& Wea)
{
 AppData* pAppData = NULL;
 int iMin;
 int iMax;

 pAppData=(AppData*)GetWindowLongPtr(Wea.hWnd,0);
 switch(Wea.wParam)
 {
   case SB_TOP:
     pAppData->sd.iPosition = 0;
     break;
   case SB_BOTTOM:
     pAppData->sd.iPosition=pAppData->sd.iRange;
     break;
   case SB_LINEUP:
     pAppData->sd.iPosition = pAppData->sd.iPosition - 1;
     break;
   case SB_LINEDOWN:
     if(pAppData->sd.iPosition<pAppData->sd.iRange)
        pAppData->sd.iPosition = pAppData->sd.iPosition + 1;
     break;
   case SB_PAGEUP:
     pAppData->sd.iPosition = pAppData->sd.iPosition - pAppData->sd.cyClient / pAppData->sd.cyChar;
     break;
   case SB_PAGEDOWN:
     pAppData->sd.iPosition = pAppData->sd.iPosition + pAppData->sd.cyClient / pAppData->sd.cyChar;
     if(pAppData->sd.iPosition>pAppData->sd.iRange)
        pAppData->sd.iPosition=pAppData->sd.iRange;
     break;
   case SB_THUMBPOSITION:
     pAppData->sd.iPosition = LOWORD(Wea.lParam);
     break;
 }
 iMin = min(pAppData->sd.iPosition,pAppData->sd.iRange);
 iMax = max(0, iMin);
 pAppData->sd.iPosition = max(0, min(pAppData->sd.iPosition, pAppData->sd.iRange));
 if(pAppData->sd.iPosition != GetScrollPos(pAppData->sd.hScroll, SB_CTL))
 {
    SetScrollRange(pAppData->sd.hScroll,SB_CTL,0,pAppData->sd.iRange,FALSE); 
    SetScrollPos(pAppData->sd.hScroll, SB_CTL, pAppData->sd.iPosition, TRUE);
    InvalidateRect(Wea.hWnd, NULL, TRUE); 
 } 

 return 0;
}


LRESULT CALLBACK fnWndProc_OnPaint(WndEventArgs& Wea)   // Charles decided he wanted to use DrawText() for his app, and to
{                                                       // clip drawing rectangles to implement scrolling.  This is all well
 AppData*    pAppData = NULL;                           // and good, but DrawText() reacts poorly to nulls in the string to
 HDC         hdc      = NULL;                           // be drawn.  Gibberish characters get drawn.  So what Charles
 PAINTSTRUCT ps;                                        // decided to do was to overwrite the terminating NULL char in the
 RECT        rc;                                        // resource data in the executable.  Here's his exact code....
                                                                    //
 hdc      = BeginPaint(Wea.hWnd, &ps);                              //   while (*pText != '\\' && *pText != '\0')
 pAppData = (AppData*)GetWindowLongPtr(Wea.hWnd,0);                 //   {
 GetClientRect(Wea.hWnd, &rc);                                      //      if(*pText == '\n')
 rc.left  = rc.left + pAppData->sd.cxChar;                          //         iNumLines ++ ;
 rc.top   = rc.top+pAppData->sd.cyChar*(1-pAppData->sd.iPosition);  //      pText = AnsiNext (pText) ;
 DrawText                                                           //   }
 (                                                                  //   *pText = '\0' ;
  hdc,                                                              //
  pAppData->rd.pWText,                                  // The line *pText = '\0' is of course a 'write memory' operation, and
  wcslen(pAppData->rd.pWText) - 2,                      // it is the line which crashes the app built as x64.  Of course, x64
  &rc,                                                  // write memory pointer operations are totally valid, but only if that
  DT_EXTERNALLEADING                                    // memory isn't within resource data or other executable code in the
 );                                                     // loaded *.exe file, apparently.  It seems computer programmer
 EndPaint(Wea.hWnd, &ps);                               // etiquette has gone through some evolution between Charles' writtings
                                                        // around Win98 and the advent of DEP with WinXP and later!  So what I
 return 0;                                              // did to fix this was simply to subtract a '-2' from the length of the
}                                                       // pWText string to be drawn, which eliminated the '/' and the NULL.


LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 AppData* pAppData = NULL;

 pAppData=(AppData*)GetWindowLongPtr(Wea.hWnd,0);
 FreeResource(pAppData->rd.hResource);
 HeapFree(GetProcessHeap(),0,pAppData);
 PostQuitMessage(0);
 
 return 0;
}


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(size_t i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(Wea);
     }
 }

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
 HWND       hWnd       = NULL;
 WNDCLASSEX wc         =  {0};
 char       szAppName    [10];
 char       szCaption    [35];
 wchar_t    szWAppName   [16];
 wchar_t    szWCaption   [40];
 MSG        msg;

 LoadStringA(hInstance, IDS_APPNAME, szAppName, sizeof (szAppName));  // It's ansi data in resource section of executable,
 LoadStringA(hInstance, IDS_CAPTION, szCaption, sizeof (szCaption));  // so the 'A' functions are needed to load it.  But
 MultiByteToWideChar(CP_UTF8,0,szAppName,-1,szWAppName,16);           // we'll immediately convert it to wide chars using
 MultiByteToWideChar(CP_UTF8,0,szCaption,-1,szWCaption,40);           // MultiByteToWideChar().
 wc.cbSize        = sizeof(wc),                          wc.style        = CS_HREDRAW | CS_VREDRAW;
 wc.lpfnWndProc   = fnWndProc,                           wc.cbClsExtra   = 0;
 wc.cbWndExtra    = sizeof(void*),                       wc.hInstance    = hInstance;
 wc.hIcon         = LoadIcon(hInstance, szWAppName),     wc.hCursor      = LoadCursor(NULL, IDC_ARROW);
 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH), wc.lpszMenuName = NULL;
 wc.lpszClassName = szWAppName,                          wc.hIconSm     = LoadIcon(hInstance, szWAppName);
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szWAppName,szWCaption,WS_OVERLAPPEDWINDOW|WS_VISIBLE,400,200,500,700,NULL,NULL,hInstance,NULL);
 while(GetMessage(&msg, NULL, 0, 0))
 {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
 }
 
 return (int)msg.wParam;
}

Believe it or not, I have another version for you!  Another way to fix the memory write failure is to make a copy of the poem's memory somewhere other than in the resource section of the executable, using malloc(), new(), or HeapAlloc(), copy the poem to there, and remove the slash character.  MultiByteToWideChar() can be used once one has the 'writable' memory.  But I think with this version of the program I did one better, that is, I copied the poem lines to an array of lines and used TextOut() for each line instead of DrawText().  I've always personally preferred TextOut() to DrawText() anyway.  And in any case, I think the scroll logic and efficiency is better when one isn't clipping rectangles for scrolling.  Here is another version using this technique (next post)....
8
64-bit SDK programming / Drawing an audio waveform
« Last post by Patrice Terrier on December 18, 2021, 08:02:38 pm »
This 64-bit C++ project allows you to draw an audio waveform like in Audacity.

Use :
Drag & drop an audio file in mp3, ogg, wav format.
Or click on the Play button to use the default music.

The "CreateWaveform" procedure decodes the audio channel with BASS_StreamCreateFile (Bass.dll)
and uses ZI_CreateDIBSection (GDImage64.DLL) to represent the PixelArray.

The "Animate" function then takes care of visualizing the progress of the audio stream as the music is played.
The blinking cursor can be moved forward or backward, with progress being updated accordingly.


9
64-bit SDK programming / Load DLL from memory (using RCDATA)
« Last post by Patrice Terrier on December 03, 2021, 01:52:16 pm »
I have used for years this PB code to load a DLL from memory.

LoadDLLfromMemory
is able to load a DLL completely from memory, without storing it on the disk first.

This code is based on the work of Semen and Lothar posted long ago on the PowerBASIC forum.

Code: [Select]
'+--------------------------------------------------------------------------+
'|                                                                          |
'|                          (LoadDLLfromMemory)                             |
'|                                                                          |
'|                         Author Patrice TERRIER                           |
'|                         copyright(c) 2007-2014                           |
'|                           www.zapsolution.com                            |
'|                        pterrier@zapsolution.com                          |
'|                                                                          |
'+--------------------------------------------------------------------------+
'|                  Project started on : 00-06-2007 (MM-DD-YYYY)            |
'|                        Last revised : 01-02-2014 (MM-DD-YYYY)            |
'+--------------------------------------------------------------------------+

#COMPILE EXE "test.exe"
#RESOURCE "mydll.pbr"

macro const = macro

const IMAGE_ORDINAL_FLAG                = &H80000000
const IMAGE_NUMBEROF_DIRECTORY_ENTRIES  = 16
const IMAGE_NT_OPTIONAL_HDR32_MAGIC     = &H10b

const IMAGE_DOS_SIGNATURE    = &H5A4D      ' MZ
const IMAGE_OS2_SIGNATURE    = &H454E      ' NE
const IMAGE_OS2_SIGNATURE_LE = &H454C      ' LE
const IMAGE_VXD_SIGNATURE    = &H454C      ' LE
const IMAGE_NT_SIGNATURE     = &H00004550  ' PE00

const IMAGE_DIRECTORY_ENTRY_EXPORT         = 0  ' // Export Directory
const IMAGE_DIRECTORY_ENTRY_IMPORT         = 1  ' // Import Directory
const IMAGE_DIRECTORY_ENTRY_RESOURCE       = 2  ' // Resource Directory
const IMAGE_DIRECTORY_ENTRY_EXCEPTION      = 3  ' // Exception Directory
const IMAGE_DIRECTORY_ENTRY_SECURITY       = 4  ' // Security Directory
const IMAGE_DIRECTORY_ENTRY_BASERELOC      = 5  ' // Base Relocation Table
const IMAGE_DIRECTORY_ENTRY_DEBUG          = 6  ' // Debug Directory
'// IMAGE_DIRECTORY_ENTRY_COPYRIGHT        =  7   // (X86 usage)
const IMAGE_DIRECTORY_ENTRY_COPYRIGHT      =  7  ' // (X86 usage)
const IMAGE_DIRECTORY_ENTRY_ARCHITECTURE   = 7  ' // Architecture Specific Data
const IMAGE_DIRECTORY_ENTRY_GLOBALPTR      = 8  ' // RVA of GP
const IMAGE_DIRECTORY_ENTRY_TLS            = 9  ' // TLS Directory
const IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    = 10  ' // Load Configuration Directory
const IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   = 11  ' // Bound Import Directory in headers
const IMAGE_DIRECTORY_ENTRY_IAT            = 12  ' // Import Address Table
const IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   = 13  ' // Delay Load Import Descriptors
const IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14  ' // COM Runtime descriptor
const IMAGE_SIZEOF_SHORT_NAME = 8

const MEM_COMMIT             = &H1000???
const PAGE_READONLY          = &H02???
const PAGE_READWRITE         = &H04???
const PAGE_EXECUTE_READ      = &H20???
const PAGE_EXECUTE_READWRITE = &H40???

const DLL_PROCESS_DETACH     = 0
const DLL_PROCESS_ATTACH     = 1
const MEM_RELEASE            = &H8000???
const RT_RCDATA              = 10

type IMAGE_EXPORT_DIRECTORY dword fill
    Characteristics       as dword
    TimeDateStamp         as dword
    MajorVersion          as word
    MinorVersion          as word
    nName                 as dword
    nBase                 as dword
    NumberOfFunctions     as dword
    NumberOfNames         as dword
    AddressOfFunctions    as dword   ' // RVA from base of image
    AddressOfNames        as dword   ' // RVA from base of image
    AddressOfNameOrdinals as dword   ' // RVA from base of image
end type

' // Size = 64 bytes
type IMAGE_DOS_HEADER byte
    e_magic    as word   ' word // Magic number
    e_cblp     as word   ' word // Bytes on last page of file
    e_cp       as word   ' word // Pages in file
    e_crlc     as word   ' word // Relocations
    e_cparhdr  as word   ' word // Size of header in paragraphs
    e_minalloc as word   ' word // Minimum extra paragraphs needed
    e_maxalloc as word   ' word // Maximum extra paragraphs needed
    e_ss       as word   ' word // Initial (relative) SS value
    e_sp       as word   ' word // Initial SP value
    e_csum     as word   ' word // Checksum
    e_ip       as word   ' word // Initial IP value
    e_cs       as word   ' word // Initial (relative) CS value
    e_lfarlc   as word   ' word // File address of relocation table
    e_ovno     as word   ' word // Overlay number
    e_res(3)   as word   ' word[4] // Reserved words
    e_oemid    as word   ' word // OEM identifier (for e_oeminfo)
    e_oeminfo  as word   ' word // OEM information; e_oemid specific
    e_res2(9)  as word   ' word[10] // Reserved words
    e_lfanew   as long   ' long // File address of new exe header
end type

type IMAGE_FILE_HEADER dword fill
    Machine              as word
    NumberOfSections     as word
    TimeDateStamp        as dword
    PointerToSymbolTable as dword
    NumberOfSymbols      as dword
    SizeOfOptionalHeader as word
    Characteristics      as word
end type

type IMAGE_DATA_DIRECTORY dword
    VirtualAddress as dword
    nSize          as dword
end type

type IMAGE_OPTIONAL_HEADER dword fill
    ' Standard fields.
    Magic                       as word
    MajorLinkerVersion          as byte
    MinorLinkerVersion          as byte
    SizeOfCode                  as dword
    SizeOfInitializedData       as dword
    SizeOfUninitializedData     as dword
    AddressOfEntryPoint         as dword
    BaseOfCode                  as dword
    BaseOfData                  as dword
    ' NT additional fields.
    ImageBase                   as dword
    SectionAlignment            as dword
    FileAlignment               as dword
    MajorOperatingSystemVersion as word
    MinorOperatingSystemVersion as word
    MajorImageVersion           as word
    MinorImageVersion           as word
    MajorSubsystemVersion       as word
    MinorSubsystemVersion       as word
    Win32VersionValue           as dword
    SizeOfImage                 as dword
    SizeOfHeaders               as dword
    CheckSum                    as dword
    Subsystem                   as word
    DllCharacteristics          as word
    SizeOfStackReserve          as dword
    SizeOfStackCommit           as dword
    SizeOfHeapReserve           as dword
    SizeOfHeapCommit            as dword
    LoaderFlags                 as dword
    NumberOfRvaAndSizes         as dword
    DataDirectory(IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1) as IMAGE_DATA_DIRECTORY
end type

type IMAGE_NT_HEADERS dword
    Signature      as dword
    FileHeader     as IMAGE_FILE_HEADER
    OptionalHeader as IMAGE_OPTIONAL_HEADER
end type

' // Size = 4 bytes
type SYSTEM_INFO_UNION_STRUCT word
    wProcessorArchitecture as word   ' word wProcessorArchitecture
    wReserved              as word   ' word wReserved
end type

' // Size = 4 bytes
union SYSTEM_INFO_UNION dword
    dwOemId as dword   ' dword dwOemId;          // Obsolete field...do not use
    SYSTEM_INFO_UNION_STRUCT
end union

' // Size = 36 bytes
type SYSTEM_INFO dword fill
    SYSTEM_INFO_UNION
    dwPageSize                  as dword   ' dwPageSize
    lpMinimumApplicationAddress as dword   ' lpMinimumApplicationAddress
    lpMaximumApplicationAddress as dword   ' lpMaximumApplicationAddress
    dwActiveProcessorMask       as dword   ' dwActiveProcessorMask
    dwNumberOfProcessors        as dword   ' dwNumberOfProcessors;
    dwProcessorType             as dword   ' dwProcessorType
    dwAllocationGranularity     as dword   ' dwAllocationGranularity
    wProcessorLevel             as word    ' wProcessorLevel
    wProcessorRevision          as word    ' wProcessorRevision
end type

union MiscUnionISH dword
    PhysicalAddress as dword   ' dword
    VirtualSize     as dword   ' dword
end union

union IMAGE_SECTION_HEADER_NAME_UNION
    Name as string * IMAGE_SIZEOF_SHORT_NAME   ' byte
    '// for compatibility wirh the PB headers
    bName(IMAGE_SIZEOF_SHORT_NAME - 1) as byte
end union

' // Size = 40 bytes
type IMAGE_SECTION_HEADER dword fill
    IMAGE_SECTION_HEADER_NAME_UNION
    Misc                 as MiscUnionISH
    VirtualAddress       as dword
    SizeOfRawData        as dword
    PointerToRawData     as dword
    PointerToRelocations as dword
    PointerToLinenumbers as dword
    NumberOfRelocations  as word
    NumberOfLinenumbers  as word
    Characteristics      as dword
end type

type IMAGE_IMPORT_BY_NAME
    Hint                   as word
    ImpName                as asciiz * 254
end type

type IMAGE_IMPORT_DESCRIPTOR
    OriginalFirstThunk     as dword
    TimeDateStamp          as dword
    ForwarderChain         as dword
    pName                  as dword
    FirstThunk             as dword
end type

type IMAGE_BASE_RELOCATION
    VirtualAddress as dword
    SizeOfBlock as dword
end type

declare sub GetSystemInfo lib "KERNEL32.DLL" ALIAS "GetSystemInfo" (byref lpSystemInfo as SYSTEM_INFO)
declare function VirtualAlloc lib "KERNEL32.DLL" ALIAS "VirtualAlloc" (byval lpAddress as dword, byval dwSize as dword, byval flAllocationType as dword, byval flProtect as dword) as dword
declare function VirtualProtect lib "KERNEL32.DLL" ALIAS "VirtualProtect" (byval lpAddress as dword, byval dwSize as dword, byval flNewProtect as dword, byref lpflOldProtect as dword) as long
declare function VirtualFree lib "KERNEL32.DLL" ALIAS "VirtualFree" (byval lpAddress as dword, byval dwSize as dword, byval dwFreeType as dword) as long
declare sub MoveMemory lib "KERNEL32.DLL" ALIAS "RtlMoveMemory" (pDestination as any, pSource as any, byval cbLength as long)
declare function GetModuleHandle lib "KERNEL32.DLL" ALIAS "GetModuleHandleA" (lpModuleName as asciiz) as dword
declare function FindResource lib "KERNEL32.DLL" ALIAS "FindResourceA" (byval hInstance as dword, lpName as asciiz, lpType as asciiz) as long
declare function SizeofResource lib "KERNEL32.DLL" ALIAS "SizeofResource" (byval hInstance as dword, byval hResInfo as dword) as long
declare function LockResource lib "KERNEL32.DLL" ALIAS "LockResource" (byval hResData as dword) as dword
declare function LoadResource lib "KERNEL32.DLL" ALIAS "LoadResource" (byval hInstance as dword, byval hResInfo as dword) as long

declare function GetProcAddress lib "KERNEL32.DLL" alias "GetProcAddress" (byval hModule as dword, lpProcName as asciiz) as long
declare function LoadLibrary lib "KERNEL32.DLL" alias "LoadLibraryA" (lpLibFileName as asciiz) as long
'===========================================================================
declare function EntryPoint(byval hInstance as dword, byval Reason as dword, byval Reserved as dword) as long

function GetProcAddressDirectly (byval lpImageDosHeader as IMAGE_DOS_HEADER ptr, FuncName as asciiz) as dword
    local lpImageNtHeaders as IMAGE_NT_HEADERS ptr
    local lpImageExportDirectory as IMAGE_EXPORT_DIRECTORY ptr
    local lpNameOrdinals as word ptr
    local lpName, lpFunctions as dword ptr
    local lpFuncName, lpExpFuncName as asciiz ptr
    local i, j as dword

    if (@lpImageDosHeader.e_magic <> IMAGE_DOS_SIGNATURE) then
        function = 1: exit function ' invalid DOS signature
    end if
    lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
    if (@lpImageNtHeaders.Signature <> IMAGE_NT_SIGNATURE) then
        function = 1: exit function ' invalid NT signature
    end if
    if (@lpImageNtHeaders.FileHeader.SizeOfOptionalHeader <> sizeof(@lpImageNtHeaders.OptionalHeader) or _
        @lpImageNtHeaders.OptionalHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC) then
        exit function
    end if

    lpImageExportDirectory = @lpImageNtHeaders.OptionalHeader.DataDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT).VirtualAddress
    if (lpImageExportDirectory = 0) then
        exit function
    end if
    lpImageExportDirectory = lpImageExportDirectory + lpImageDosHeader

    lpNameOrdinals = @lpImageExportDirectory.AddressOfNameOrdinals + lpImageDosHeader
    lpName         = @lpImageExportDirectory.AddressOfNames        + lpImageDosHeader
    lpFunctions    = @lpImageExportDirectory.AddressOfFunctions    + lpImageDosHeader

    lpFuncName = varptr(FuncName)

    if (HI(WORD, lpFuncName)) then ' Name
        for i = 0 to @lpImageExportDirectory.NumberOfFunctions - 1
            if (@lpFunctions[i]) then
                for j = 0 to @lpImageExportDirectory.NumberOfNames - 1
                   if (@lpNameOrdinals[j] = i) then
                       lpExpFuncName = @lpName[j] + lpImageDosHeader
                       if (@lpExpFuncName = FuncName) then
                           function = @lpFunctions[i] + lpImageDosHeader
                           exit function
                       end if
                   end if
                next
            end if
        next
    else
        for i = 0 to @lpImageExportDirectory.NumberOfFunctions - 1
            if (lpFuncName = @lpImageExportDirectory.nBase + i) then
                if (@lpFunctions[i]) then
                    function = @lpFunctions[i] + lpImageDosHeader
                end if
                exit function
            end if
        next
    end if
end function

function LoadDllfromMemory (byval lpRawDll as dword, byval RawDllSize as dword, lpImageDll as dword) as long
    local nError as long
    local sSysInfo as SYSTEM_INFO
    local lpImageDosHeader        as IMAGE_DOS_HEADER ptr
    local lpImageNtHeaders        as IMAGE_NT_HEADERS ptr
    local lpImageSectionHeader    as IMAGE_SECTION_HEADER ptr
    local lpImageImportDescriptor as IMAGE_IMPORT_DESCRIPTOR ptr
    local lpImageImportByName     as IMAGE_IMPORT_BY_NAME ptr
    local lpImageBaseRelocTable   as IMAGE_BASE_RELOCATION ptr
    local lpDllName               as asciiz ptr
    local szDllName               as string
    local lpTypeOffset            as word ptr
    local TpOffset                as word
    local lpFuncNameRef, lpFuncAddr, lpLink as dword ptr
    local ImagePages, hDll, fOldProtect, i, j, k, Pg, Pg1, Pg2, Addr1, Addr2 as dword
    local Protection() as byte

    lpImageDosHeader = lpRawDll
    if (RawDllSize < sizeof(IMAGE_DOS_HEADER)) then
        nError = 1: goto BailOut
    end if
    if (@lpImageDosHeader.e_magic <> IMAGE_DOS_SIGNATURE) then
        nError = 1: goto BailOut ' invalid DOS signature
    end if
    if (RawDllSize < @lpImageDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS)) then
        nError = 1: goto BailOut
    end if
    lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
    if (@lpImageNtHeaders.Signature <> IMAGE_NT_SIGNATURE) then
        nError = 1: goto BailOut ' invalid NT signature
    end if
    if (@lpImageNtHeaders.FileHeader.SizeOfOptionalHeader <> sizeof(@lpImageNtHeaders.OptionalHeader) or _
        @lpImageNtHeaders.OptionalHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC) then
        nError = 1: goto BailOut
    end if
    if (@lpImageNtHeaders.FileHeader.NumberOfSections < 1) then
        nError = 1: goto BailOut
    end if

    for i = 0 to IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1
        if (@lpImageNtHeaders.OptionalHeader.DataDirectory(i).VirtualAddress <> 0) then
            select case as long i
            case IMAGE_DIRECTORY_ENTRY_EXPORT     ' Export Directory
            case IMAGE_DIRECTORY_ENTRY_IMPORT     ' Import Directory
            case IMAGE_DIRECTORY_ENTRY_RESOURCE   ' Resource Directory
            case IMAGE_DIRECTORY_ENTRY_BASERELOC  ' Base Relocation Table
            case IMAGE_DIRECTORY_ENTRY_IAT        ' Import Address Table
            case else ' Strange for PB
                nError = 2: goto BailOut
            end select
        end if
    next

    lpImageSectionHeader = lpImageNtHeaders + sizeof(IMAGE_NT_HEADERS)
    k = lpImageSectionHeader - lpImageDosHeader + sizeof(IMAGE_SECTION_HEADER) * @lpImageNtHeaders.FileHeader.NumberOfSections
    j = k
    for i = 0 to @lpImageNtHeaders.FileHeader.NumberOfSections - 1
        j = max(j, @lpImageSectionHeader[i].VirtualAddress + @lpImageSectionHeader[i].SizeOfRawData)
    next

    GetSystemInfo(sSysInfo)
    ImagePages = j \ sSysInfo.dwPageSize
    if (j Mod sSysInfo.dwPageSize) then incr ImagePages
    lpImageDll = VirtualAlloc(byval 0, cdwd(ImagePages * sSysInfo.dwPageSize), MEM_COMMIT, PAGE_EXECUTE_READWRITE)
    if (lpImageDll = 0) then
        nError = 5: goto BailOut
    end if

    MoveMemory(byval lpImageDll, byval lpRawDll, k)
    for i = 0 to @lpImageNtHeaders.FileHeader.NumberOfSections - 1
        MoveMemory(byval cdwd(lpImageDll + @lpImageSectionHeader[i].VirtualAddress), _
                   byval cdwd(lpRawDll + @lpImageSectionHeader[i].PointerToRawData), @lpImageSectionHeader[i].SizeOfRawData)
    next

    '// Switch to new image
    lpImageDosHeader = lpImageDll
    lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
    lpImageSectionHeader = lpImageNtHeaders + sizeof(IMAGE_NT_HEADERS)

    '// Start Original code BY Lothar
    lpImageImportDescriptor = @lpImageNtHeaders.OptionalHeader.DataDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT).VirtualAddress
    if (lpImageImportDescriptor <> 0) then
        lpImageImportDescriptor = lpImageImportDescriptor + lpImageDosHeader

        while @lpImageImportDescriptor.OriginalFirstThunk <> 0 or @lpImageImportDescriptor.FirstThunk <> 0 ' Dlls
            lpDllName = @lpImageImportDescriptor.pName + lpImageDosHeader
            szDllName = @lpDllName
            hDll = GetModuleHandle(byval strptr(szDllName))

            if (hDll = 0) then hDll = LoadLibrary(byval strptr(szDllName))
            if (hDll = 0) then
                nError = 2: goto BailOut '// Can't find
            end if

            lpFuncNameRef = @lpImageImportDescriptor.OriginalFirstThunk + lpImageDosHeader

            lpFuncAddr = @lpImageImportDescriptor.FirstThunk + lpImageDosHeader
            if lpFuncNameRef = lpImageDosheader then lpFuncNameRef = lpFuncAddr

            while @lpFuncNameRef <> 0
                lpImageImportByName = @lpFuncNameRef + lpImageDosHeader

                if (@lpFuncNameRef and IMAGE_ORDINAL_FLAG) then
                    @lpFuncAddr = GetProcAddress(hDll, byval @lpFuncNameRef and &HFFFF???)
                else
                    @lpFuncAddr = GetProcAddress(hDll, @lpImageImportByName.ImpName)
                end if

                if (@lpFuncAddr = 0) then
                    nError = 2: goto BailOut
                end if
                incr lpFuncAddr
                incr lpFuncNameRef
            wend
            incr lpImageImportDescriptor
        loop

    end if
    '// End Original code BY Lothar

    lpImageBaseRelocTable = @lpImageNtHeaders.OptionalHeader.DataDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC).VirtualAddress
    if (lpImageBaseRelocTable <> 0) then
        lpImageBaseRelocTable = lpImageBaseRelocTable + lpImageDosHeader
        while @lpImageBaseRelocTable.VirtualAddress <> 0
            lpTypeOffset = lpImageBaseRelocTable + sizeof(IMAGE_BASE_RELOCATION)
            while lpTypeOffset < lpImageBaseRelocTable + @lpImageBaseRelocTable.SizeOfBlock
                TpOffset = @lpTypeOffset and &HF000??
                if (TpOffset = &H3000) then
                    lpLink = lpImageDosHeader + @lpImageBaseRelocTable.VirtualAddress + (@lpTypeOffset and &HFFF??)
                    @lpLink = @lpLink - @lpImageNtHeaders.OptionalHeader.ImageBase + lpImageDosHeader
                elseif TpOffSet = 0 then
                else
                    nError = 3: goto BailOut ' Uknown type
                end if
                incr lpTypeOffset
            wend
            lpImageBaseRelocTable = lpImageBaseRelocTable + @lpImageBaseRelocTable.SizeOfBlock
        wend
    end if

    redim Protection(ImagePages - 1)

    for i = 0 to @lpImageNtHeaders.FileHeader.NumberOfSections
        if (i = @lpImageNtHeaders.FileHeader.NumberOfSections) then
            Addr1 = 0: Addr2 = k: j = &H60000000??? '// PAGE_EXECUTE_READ
        else
            Addr1 = @lpImageSectionHeader[i].VirtualAddress
            Addr2 = @lpImageSectionHeader[i].SizeOfRawData
            j = @lpImageSectionHeader[i].Characteristics
        end if
        Addr2 = Addr1 + Addr2 - 1
        Pg1 = Addr1 \ sSysInfo.dwPageSize
        Pg2 = Addr2 \ sSysInfo.dwPageSize
        for Pg = Pg1 to Pg2
            if (j and &H20000000???) then Protection(Pg) = Protection(Pg) or 1 ' Execute
            if (j and &H40000000???) then Protection(Pg) = Protection(Pg) or 2 ' Read
            if (j and &H80000000???) then Protection(Pg) = Protection(Pg) or 4 ' Write
        next
    next
    Addr1 = lpImageDosHeader
    for Pg = 0 to ImagePages - 1
        select case as long Protection(Pg)
        case 2: fOldProtect = PAGE_READONLY
        case 3: fOldProtect = PAGE_EXECUTE_READ
        case 6: fOldProtect = PAGE_READWRITE
        case else: fOldProtect = PAGE_EXECUTE_READWRITE ' Ignore strange combinations
        end select
        if (fOldProtect <> PAGE_EXECUTE_READWRITE) then
           if (VirtualProtect (byval Addr1, sSysInfo.dwPageSize, fOldProtect, fOldProtect) = 0) then
               nError = 4: goto BailOut
           end if
        end if
        Addr1 = Addr1 + sSysInfo.dwPageSize
    next

    i = @lpImageNtHeaders.OptionalHeader.AddressOfEntryPoint + lpImageDosHeader
    CALL DWORD i using EntryPoint (lpImageDosHeader, DLL_PROCESS_ATTACH, 0)

BailOut:
    function = nError
end function

function Unload_DLL (byval lpImageDosHeader as IMAGE_DOS_HEADER ptr) as dword
    local lpImageNtHeaders as IMAGE_NT_HEADERS ptr
    local i as dword

    lpImageNtHeaders = lpImageDosHeader + @lpImageDosHeader.e_lfanew
    i = @lpImageNtHeaders.OptionalHeader.AddressOfEntryPoint + lpImageDosHeader
    CALL DWORD i using EntryPoint (lpImageDosHeader, DLL_PROCESS_DETACH, 0)
    function = VirtualFree (byval lpImageDosHeader, 0, MEM_RELEASE)
end function

function Load_DLL(lpName as ASCIIZ) as dword
    local nError as long
    local hLib, hResource, buffersize, pResourceData, hInstance as DWORD
    hInstance = GetModuleHandle("")
    hResource = FindResource(hInstance, lpName, byval RT_RCDATA)
    if (hResource) then
        buffersize = SizeofResource(hInstance, hResource)
        if (buffersize > 0) then
            pResourceData = LockResource(LoadResource(hInstance, hResource))
            if (pResourceData) then
                nError = LoadDllfromMemory(byval pResourceData, buffersize, hLib)
                if (nError) then hLib = 0
            end if
        end if
    end if
    function = hLib
end function

function Load_DLL((lpName as ASCIIZ)) as dword
is used to load the DLL from resource.


I would like to convert this code to C++ 64-bit, but before doing it,
I am aking here, in case somebody did it already for Visual Studio ?
10
TCLib / Form8a -- Non-TCLib Version Of Form8 Project
« Last post by Frederick Harris on December 01, 2021, 04:01:59 pm »
#if 0
Here is a version of Form8 that doesn't use TCLib but rather can be built against Microsoft's CRT.  The story
on this is I first learned to do this stuff, i.e., get printf working in GUI apps, way back in the late 90s
early 2000s time frame when we were all using Win95, Win98, or Win2000. At that time I was mostly using VC6
from Visual Studio 98 and Bloodshed's Dev-Cpp with Mingw.  The technique worked well with any of PowerBASIC's
compilers also.  I thought I was good forever with these techniques.

The next version of Visual Studio I used was my Visual Studio 2008 Professional.  It broke my code.  However,
throwing a few hours at the new stdio.h header I was able to figure out the changes Microsoft made, tweak my
code, and get it working again - and I hoped once and for all.

Well, it didn't work out that way once again.  The next version of Visual Studio I obtained was Visual Studio
Community 2015 (I get a new version of Visual Studio every 10 years, whether I need it or not).  It broke my
code again.  This time I threw several days at trying to figure out the changes Microsoft made but I failed
miserably.  I couldn't get it working and I've given up on it.

And that really bugged me.  Bad.  I'm not accostomed to failing and giving up on things that are important to
me.  But it occurred to me that my original solution using VC6 from Visual Studio 98 might be made to work with
Visual Studio's VC19 compiler/linker using the same techniques I used to create my TCLib.lib.  Afterall,
msvcrt.dll was the C Runtime for that old version of Visual Studio where I first got this technique working.
So I thought, why not just get function pointers to _open_osfhandle, _fdopen, and the all important _iob[]
FILE* array from msvcrt.dll, and implement it all using Visual Studio 2019?  By the way, _iob[] stands for
Input/Output Blocks.  So I tried that and it works fine with Microsoft's latest (non-beta) Visual Studio.

But there's probably - almost certainly, an easier and better way to do it.  It's just that I can't figure it
out.  As I said, I threw several days at it in total, and came up empty.  Personal problem, I know.  So if
someone here smarter than me can figure it out and post the method here I'll gladly take it and run with it.
I'm not proud!
 
Only downside to the code below is you can't use Microsoft's stdio.h or <cstdio>.  Absolutely can't!  If you
attempt it you'll get errors to the effect that symbols such as _open_osfhandle, _fdopen, etc., are being
redefined from functions to function pointers.  So if you need anything from stdio.h other than printf, fclose,
or getchar, you are going to have to do as I did in the code below and get it from msvcrt.dll as a function
pointer and use it that way.  To me, that's no big deal, but I wanted to point that out.
#endif

Code: [Select]
// Form8a.cpp
// This non-TCLib (Links Against LibCmt) GUI program creates a debug console at the beginning of WinMain().
// Do not #include stdio.h or <cstdio>.  Won't work, because of definitions and function pointer prototypes
// I put in ConsoleIO.h to work against msvcrt.dll.
// cl Form8a.cpp  /O1 /Os /MT /GA /FeForm8a.exe User32.lib
// Size: 94,720 Bytes VC19 (VStudio 2019);
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE   
   #define _UNICODE
#endif   
#include <windows.h>
#include "Form8.h"
#include "ConsoleIO.h"


bool GUI_Console_IO(HMODULE& hDll, FILE*& hFileIn, FILE*& hFileOut)  // For Re-Initializing CRT Console IO In GUI Apps.
{                                                                    // With Windows, CRT Console IO File Descriptors Are
 HMENU   hSysMenu        = NULL;                                     // Zeroed Out At GUI Initialization.  That's why
 HANDLE  hStdOut         = NULL;                                     // C Runtime console i/o routines won't work.
 HANDLE  hStdIn          = NULL;
 FILE*   pFile           = NULL;
 int     hCrtOut         = 0;
 int     hCrtIn          = 0;
 
 if(!AllocConsole())                                     // Create Console
    return false;
 hDll            = LoadLibrary(L"msvcrt.dll");           // Obtain HANDLE To msvcrt.dll
 if(!hDll)
    return false;
 pFile           = (FILE*)GetProcAddress(hDll,"_iob");   // _iob Is An Array of FILE structs containing stdin, stdout, and stderr
 if(!pFile)
    return false;
 hStdOut         = GetStdHandle(STD_OUTPUT_HANDLE);
 hStdIn          = GetStdHandle(STD_INPUT_HANDLE); 
 _open_osfhandle = (int   (__cdecl*)(intptr_t osfhandle, int flags)) GetProcAddress(hDll,"_open_osfhandle");
 _fdopen         = (FILE* (__cdecl*)(int, const char* mode        )) GetProcAddress(hDll,"_fdopen"        );
 printf          = (int   (__cdecl*)(const char* pFormat, ...     )) GetProcAddress(hDll,"printf"         );
 getchar         = (char  (__cdecl*)(                             )) GetProcAddress(hDll,"getchar"        );
 fclose          = (int   (__cdecl*)(FILE* fp                     )) GetProcAddress(hDll,"fclose"         );
 
 // Open stdin
 hCrtIn   = _open_osfhandle((intptr_t)hStdIn,O_TEXT);    // Associates a C run-time file descriptor with an existing operating system file handle
 hFileIn  = _fdopen(hCrtIn,"r");                         // Associates a stream with a file that was previously opened for low-level I/O.
 pFile[0] = *hFileIn;                                    // pFile[0] is stdin FILE object.  This Call Re-Initializes It.
 
 // Open stdout
 hCrtOut  = _open_osfhandle((intptr_t)hStdOut,O_TEXT);   // Associates a C run-time file descriptor with an existing operating system file handle
 hFileOut = _fdopen(hCrtOut,"w");                        // Associates a stream with a file that was previously opened for low-level I/O.
 pFile[1] = *hFileOut;                                   // pFile[1] is stdout FILE object.  This Call Re-Initializes It.
 
 // Disable Close In Title Bar Of Console Window
 hSysMenu = GetSystemMenu(GetConsoleWindow(),0);
 DeleteMenu(hSysMenu,6,MF_BYPOSITION);
 
 return true;
}
 

LRESULT CALLBACK fnWndProc_OnCreate(WndEventArgs& Wea)
{
 HWND hButton = NULL;
 
 printf("  Entering fnWndProc_OnCreate()\n");
 printf("    Wea.hWnd = %u\n",Wea.hWnd);
 Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;
 hButton=CreateWindow(L"button",L"Write To Console",WS_CHILD|WS_VISIBLE,90,50,200,30,Wea.hWnd,(HMENU)IDC_BUTTON1,Wea.hIns,0);
 printf("  Leaving fnWndProc_OnCreate()\n\n");
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnCommand(WndEventArgs& Wea)
{
 double dblWeight = 178.0;
 
 printf("  Entering fnWndProc_OnCommand()\n");
 if(LOWORD(Wea.wParam)==IDC_BUTTON1)
    printf("    My Name Is Fred And I Weight %3.0f Pounds.\n",dblWeight);
 printf("  Leaving fnWndProc_OnCommand()\n\n");
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnClose(WndEventArgs& Wea)
{
 printf("  Entering fnWndProc_OnClose()\n");
 printf("    Wea.hWnd = %u\n",Wea.hWnd);
 DestroyWindow(Wea.hWnd);
 printf("  Leaving fnWndProc_OnClose()\n");
 
 return 0;
}


LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 printf("    Entering fnWndProc_OnDestroy()\n");
 printf("      Wea.hWnd = %u\n",Wea.hWnd);
 PostQuitMessage(0);
 printf("    Leaving fnWndProc_OnDestroy()\n");
 
 return 0;
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(Wea);
     }
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 wchar_t    szClassName[]=L"AllocateConsole";
 HMODULE    hDll       = NULL;
 FILE*      hFileOut   = NULL;
 FILE*      hFileIn    = NULL;
 MSG        messages;
 HWND       hWnd;
 WNDCLASSEX wc;
 
 if(GUI_Console_IO(hDll,hFileIn,hFileOut));
 {
    printf("Entering WinMain()\n");
    wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
    wc.cbSize=sizeof (WNDCLASSEX);               wc.style=0;
    wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
    wc.hIconSm=NULL;                             wc.hCursor=LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
    wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
    RegisterClassEx(&wc);
    hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,775,600,400,190,HWND_DESKTOP,0,hIns,0);
    printf("  hWnd = %u\n\n",hWnd);
    ShowWindow(hWnd,iShow);
    while(GetMessage(&messages,NULL,0,0))
    {
       TranslateMessage(&messages);
       DispatchMessage(&messages);
    }
    printf("Leaving WinMain()\n\n");
    printf("Press Any Key To Continue....");
    getchar();
    fclose(hFileIn);
    fclose(hFileOut);
    FreeLibrary(hDll);
    FreeConsole();
 }   
 
 return (int)messages.wParam;
}
Pages: [1] 2 3 ... 10