• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

SDK Address PB vs C++

Started by Patrice Terrier, December 08, 2014, 10:27:27 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Patrice Terrier

Here is the DDT Address project converted to plain low level flat API using the SDK syntax style.

The PowerBASIC version is 32-bit ANSI,
and it has been compiled with PB 9.05 and a specific include file to produce a final EXE size of 40 Kb.

The C++ version is 64-bit UNICODE,
and it has been compiled with VS 2010 to produce a final EXE size of 97 Kb.

The size of the original DDT code was 102 Kb.

The C++ is the exact transcription, line by line, of the PowerBASIC SDK version.

Added
"DPI aware version" have been added (32-bit and 64-bit).

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Patrice Terrier

#1
Here is the C++ DPI aware 64-bit version

Tools.h
#include <windows.h>
#include <iostream>
#include <Commctrl.h>
using namespace std;

#define $NULL  L""
#define $COMMA L","
#define $SPACE L" "

#define long_proc typedef long (__stdcall *zProc)
#define LOINT(a) ((SHORT)(a))
#define HIINT(a) ((SHORT)(((DWORD)(a) >> 16) & 0xFFFF))
#define MAKLNG(a, b) ((LONG) (((WORD) (a)) | ((DWORD) ((WORD) (b))) << 16))
#define UBOUND(T) ((LONG) T.size())

long zTrace(IN WCHAR* sPtr) {
    long nRet = 0;
    static HMODULE hDll;
    if (hDll == 0) {
        if (sizeof(LONG_PTR) == 8) {
            hDll = LoadLibrary(L"zTrace64");
        }
        else {
            hDll = LoadLibrary(L"zTrace32");
        }
    }
    if (hDll) {
        long_proc(WCHAR*);
        static zProc hProc;
        if (hProc == 0) { hProc = (zProc)GetProcAddress(hDll, "zTrace"); }
        if (hProc) { nRet = hProc(sPtr); }
    }
    return nRet;
}

WCHAR* STRL(IN long N) {
    static WCHAR longstr[33] = {0};
    _ltow_s(N, longstr, 10);
    return (WCHAR*)longstr;
}

wstring RTRIM$(IN wstring sBuf, IN wstring sChar) {
    wstring sResult = sBuf;
    LONG_PTR nLength = sBuf.length();
    if (nLength && (sChar.length())) {
        //sChar = sChar.substr(0, 1);
        while (nLength > 0) {
            // if (*sBuf.substr(nLength - 1, 1).c_str() == *sChar.c_str()) {
            // This is the sChar ANY search version
            if (std::wstring::npos != sChar.find(sBuf.substr(nLength - 1, 1))) {
                --nLength; }
            else {
                break;
            }
        }
        sResult = sBuf.substr(0, nLength);
    }
    return sResult;
}

wstring LTRIM$(IN wstring sBuf, IN wstring sChar) {
    wstring sResult = sBuf;
    LONG_PTR nLength = sBuf.length();
    if (nLength && (sChar.length())) {
        //sChar = sChar.substr(0, 1);
        long K = 0;
        while (K < nLength) {
            // if (*sBuf.substr(K, 1).c_str() == *sChar.c_str()) {
            // This is the sChar ANY search version
            if (std::wstring::npos != sChar.find(sBuf.substr(K, 1))) {
                ++K; }
            else {
                break;
            }
        }
        sResult = sBuf.substr(K, nLength);
    }
    return sResult;
}

wstring TRIM$(IN wstring sBuf, IN wstring sChar) {
    return LTRIM$(RTRIM$(sBuf, sChar), sChar);
}

long INSTR(IN long nIndex, IN wstring sMain, IN wstring sSearch) {
    long nRet = -1; // Not found
    long nLength = (long) (sMain.length());
    if (nLength && (sSearch.length())) {
        if (nIndex < 0) {
            nRet = (long) (sMain.rfind(sSearch, nLength + nIndex + 1)); }
        else {
            nRet = (long) (sMain.find(sSearch, nIndex));
        }
    }
    return nRet + 1;
}

long PARSECOUNT(IN wstring MainString, IN wstring MatchString) {
    long count = 0;
    size_t pos = 0;
    while((pos = MainString.find(MatchString, pos)) != std::string::npos) {
         ++count; ++pos;
    }
    return count;
}

wstring PARSE$(IN wstring sMain, IN wstring sDelim, IN long nIndex) {
    wstring sResult = $NULL;
    long nLength = (long) sDelim.length();
    if (nLength == 0) { sDelim = L","; ++nLength; } // Use comma "," as default delimiter
    sMain = RTRIM$(sMain, sDelim); sMain += sDelim;
    if (sMain.length() && nLength) {
        LONG_PTR prev_pos = 0, pos = 0, nCount = 0;
        while( (pos = sMain.find(sDelim, pos)) != std::wstring::npos ) {
            wstring substring(sMain.substr(prev_pos, pos - prev_pos));
            ++nCount;
            if (nCount == nIndex) { sResult = substring; break; }
            prev_pos = ++pos;
        }
    }
    return sResult;
}

long dpi(IN long nPix) {
    static float ratio;
    if (ratio == 0) {
        HDC hDC = GetDC(0); ratio = (GetDeviceCaps(hDC, LOGPIXELSX) / 96.0f); ReleaseDC(0, hDC);
    }
    return (long)(nPix * ratio);
}


Address.cpp
#include <windows.h>
#include <WinBase.h>
#include <Shlwapi.h>
#include <vector>

#include "Tools.h"

#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

#define ID_INDEX       100
#define ID_COMPANY     101
#define ID_LASTNAME    102
#define ID_FIRSTNAME   103
#define ID_ADDRESS1    104
#define ID_ADDRESS2    105
#define ID_ADDRESS3    106
#define ID_CITYNAME    107
#define ID_STATENAME   108
#define ID_ZIPCODE     109
#define ID_COUNTRY     110
#define ID_PHONE       111
#define ID_FAX         112
#define ID_EMAIL       113
#define ID_URL         114
#define ID_COMMENTS    115

#define ID_PRIOR       121
#define ID_NEXT        122
#define ID_ADD         123
#define ID_UPDATE      124
#define ID_FIND        125
#define ID_DELETE      126
#define ID_PRINT       127

#define ID_MENU_CLOSE  IDCANCEL
#define ID_MENU_CUT    201
#define ID_MENU_COPY   202
#define ID_MENU_PASTE  203
#define ID_MENU_HELP   204
#define ID_MENU_ABOUT  205

#define WM_APPLYFOCUS  WM_USER + 999

struct AddressType {
    WCHAR Company[64];
    WCHAR LastName[32];
    WCHAR FirstName[32];
    WCHAR Address1[64];
    WCHAR Address2[64];
    WCHAR Address3[64];
    WCHAR CityName[24];
    WCHAR StateName[4];
    WCHAR ZipCode[12];
    long Country;
    WCHAR Phone[24];
    WCHAR Fax[24];
    WCHAR Email[64];
    WCHAR Url[64];
    WCHAR Comments[1024];
};

struct PROP {
    HFONT     usefont;
    HINSTANCE hinstance;
    long      isrecmodified;
    long      isfilemodified;
    long      currententry;
    long      maxentries;
    WCHAR     findtext[MAX_PATH];
};

vector<AddressType> gAddressBook;
PROP gP;

HWND addLabel(IN HWND hParent, IN long nID, IN WCHAR* zCaption, IN long x, IN long y, IN long w, IN long h, IN DWORD dwStyle) {
    HWND hCtrl = 0;
    hCtrl = CreateWindowEx(0, L"Static", zCaption, dwStyle, dpi(x), dpi(y), dpi(w), dpi(h), hParent, (HMENU) nID, gP.hinstance, NULL);
    if (hCtrl) { SendMessage(hCtrl, WM_SETFONT, (WPARAM) gP.usefont, 0); }
    return hCtrl;
}

HWND addTextBox(IN HWND hParent, IN long nID, IN WCHAR* zCaption, IN long x, IN long y, IN long w, IN long h, IN DWORD dwStyle) {
    HWND hCtrl = 0;
    hCtrl = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", zCaption, dwStyle, dpi(x), dpi(y), dpi(w), dpi(h), hParent, (HMENU) nID, gP.hinstance, NULL);
    if (hCtrl) { SendMessage(hCtrl, WM_SETFONT, (WPARAM) gP.usefont, 0); }
    return hCtrl;
}

HWND addComboBox(IN HWND hParent, IN long nID, IN WCHAR* zCaption, IN long x, IN long y, IN long w, IN long h, IN DWORD dwStyle) {
    HWND hCtrl = 0;
    hCtrl = CreateWindowEx(0, L"ComboBox", zCaption, dwStyle, dpi(x), dpi(y), dpi(w), dpi(h), hParent, (HMENU) nID, gP.hinstance, NULL);
    if (hCtrl) { SendMessage(hCtrl, WM_SETFONT, (WPARAM) gP.usefont, 0); }
    return hCtrl;
}

HWND addButton(IN HWND hParent, IN long nID, IN WCHAR* zCaption, IN long x, IN long y, IN long w, IN long h, IN DWORD dwStyle) {
    HWND hCtrl = 0;
    hCtrl = CreateWindowEx(0, L"Button", zCaption, dwStyle, dpi(x), dpi(y), dpi(w), dpi(h), hParent, (HMENU) nID, gP.hinstance, NULL);
    if (hCtrl) { SendMessage(hCtrl, WM_SETFONT, (WPARAM) gP.usefont, 0); }
    return hCtrl;
}

void addCountries(IN HWND hCtrl) {
    WCHAR zItem[32] = { 0 };
    WCHAR* sCountry = L"United States,Afghanistan,Albania,Algeria,Andorra,Angola,Anguilla,Antarctica,Argentina,Armenia,Aruba,Australia,Austria,Azerbaijan,Bahamas,Bahrain,Bangladesh,Barbados,Belarus,Belgium,Belize,Benin,Bermuda,Bhutan,Bolivia,Botswana,Bouvet Island,BosniaHerzogovina,Brazil,Brunei,Bulgaria,Burkina Faso,Cambodia,Cameroon,Canada,Cape Verde,Cayman Islands,Central African Rep,Chad,Chile,China,Christmas Island,Colombia,Comoros,Congo,Cook Islands,Costa Rica,Croatia,Cuba,Cyprus,Czech Republic,Denmark,Djibouti,Dominica,Dominican Republic,East Timor,Ecuador,Egypt,El Salvador,Equatorial Guinea,Eritrea,Estonia,Ethiopia,Falkland Islands,Faroe Islands,Fiji,Finland,France,France Metropolitan,French Guiana,French Polynesia,French S. Territories,Gabon,Gambia,Georgia,Germany,Ghana,Gibraltar,Greece,Greenland,Grenada,Guadeloupe,Guam,Guatemala,Guinea,Guinea-Bissau,Guyana,Haiti,Honduras,Hong Kong,Hungary,Iceland,IN India,Indonesia,Iran,Iraq,Ireland,Israel,Italy,Ivory Coast,Jamaica,Japan,Jordan,Kazakhstan,Kenya,Kiribati,Korea (North),Korea (South),Kuwait,Kyrgyzstan,Laos,Latvia,Lebanon,Lesotho,Liberia,Libya,Liechtenstein,Lithuania,Luxembourg,Macau,Macedonia,Madagascar,Malawi,Malaysia,Maldives,Mali,Malta,Marshall Islands,Martinique,Mauritania,Mauritius,Mayotte,Mexico,Micronesia,Miquelon,Moldova,Monaco,Mongolia,Montserrat,Morocco,Mozambique,Myanmar,Namibia,Nauru,Nepal,Netherlands,Nevis,New Caledonia,New Guinea,New Zealand,Nicaragua,Niger,Nigeria,Niue,Norfolk Island,Norway,Oman,Pakistan,Palau,Panama,Paraguay,Peru,Philippines,Pitcairn,Poland,Portugal,Principe,Puerto Rico,Qatar,Reunion,Romania,Russian Federation,Rwanda,Saint Kitts,Saint Lucia,Samoa,San Marino,Sao Tome,Saudi Arabia,Senegal,Seychelles,Sierra Leone,Singapore,Slovak Republic,Slovenia,Solomon Islands,Somalia,South Africa,Spain,Sri Lanka,St. Helena,St. Pierre,Sudan,Suriname,Swaziland,Sweden,Switzerland,Syria,Taiwan,Tajikistan,Tanzania,Thailand,Tobago,Togo,Tokelau,Tonga,Trinidad,Tunisia,Turkey,Turkmenistan,Tuvalu,Uganda,Ukraine,United Arab Emirates,United Kingdom,United States,Uruguay,Uzbekistan,Vanuatu ,Vatican City State,Venezuela,Viet Nam,Virgin Islands (BR),Virgin Islands (US),Western Sahara,Yemen,Yugoslavia,Zaire,Zambia,Zimbabwe,Other";
    for (long K = 1; K <= PARSECOUNT(sCountry, L","); K++) {
        wcscpy_s(zItem, PARSE$(sCountry, L",", K).c_str());
        SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM) zItem);
    }
}

DWORD FileSize(IN WCHAR* szFileSpec) {
    WIN32_FIND_DATA fd = {0};
    DWORD nRet = 0;
    if (wcslen(szFileSpec)) {
        HANDLE hFind;
        hFind = FindFirstFile(szFileSpec, &fd);
        if (hFind != INVALID_HANDLE_VALUE) {
            FindClose(hFind);
            nRet = fd.nFileSizeLow;
        }
    }
    return nRet;
}

void SetButtonStatus(IN HWND hWnd) {
    wstring sTxt;

    // If any current text is modified, make the ADD & Update buttons active
    if (gP.isrecmodified) {
        EnableWindow(GetDlgItem(hWnd, ID_ADD), TRUE);
        EnableWindow(GetDlgItem(hWnd, ID_UPDATE), TRUE);
    } else {
        EnableWindow(GetDlgItem(hWnd, ID_ADD), FALSE);
        EnableWindow(GetDlgItem(hWnd, ID_UPDATE), FALSE);
    }

    // Update the Index control text
    sTxt = STRL(gP.currententry); sTxt.append(L" / "); sTxt.append(STRL(gP.maxentries));
    SetWindowText(GetDlgItem(hWnd, ID_INDEX), (WCHAR*) sTxt.c_str());

    // Do we have any records? is not, disable the Prior/Next buttons, etc
    if (gP.maxentries) {
        // If there are records, allow the Find/Delete buttons
        EnableWindow(GetDlgItem(hWnd, ID_FIND), TRUE);
        EnableWindow(GetDlgItem(hWnd, ID_DELETE), TRUE);

        // We have records, so set the Prior button status according to the current index position
        if (gP.currententry < 2) {
            EnableWindow(GetDlgItem(hWnd, ID_PRIOR), FALSE);
        } else {
            EnableWindow(GetDlgItem(hWnd, ID_PRIOR), TRUE);
        }
        // Update the } control button
        if (gP.currententry >= gP.maxentries) {
            EnableWindow(GetDlgItem(hWnd, ID_NEXT), FALSE);
        } else {
            EnableWindow(GetDlgItem(hWnd, ID_NEXT), TRUE);
        }
    } else {
        EnableWindow(GetDlgItem(hWnd, ID_PRIOR), FALSE);
        EnableWindow(GetDlgItem(hWnd, ID_NEXT), FALSE);
        EnableWindow(GetDlgItem(hWnd, ID_FIND), FALSE);
        EnableWindow(GetDlgItem(hWnd, ID_DELETE), FALSE);
    }
}

void UdtToDialog(IN HWND hWnd, IN long Index) {
    Index--; // C++ array is 0 based
    AddressType Temp = { 0 };
    long SaveState = 0;

    if ((Index > -1) && (Index < UBOUND(gAddressBook))) {
        RtlMoveMemory(&Temp, &gAddressBook[Index], sizeof(AddressType));
    }

    // When we change the edit control data, we generate EN_UPDATE messages...
    // Due to the design of our GUI interface, we would end up with the ADD and UPDATE
    // buttons becoming enabled just because we loaded a new UDT record into the dialog.
    // to overcome this, we set the "modified" flag to TRUE, so the main callback
    // function ingores the "update" notication messages.

    SaveState = gP.isrecmodified;
    gP.isrecmodified = TRUE;

    SetWindowText(GetDlgItem(hWnd, ID_COMPANY),   RTRIM$(Temp.Company, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_LASTNAME),  RTRIM$(Temp.LastName, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_FIRSTNAME), RTRIM$(Temp.FirstName, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_ADDRESS1),  RTRIM$(Temp.Address1, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_ADDRESS2),  RTRIM$(Temp.Address2, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_ADDRESS3),  RTRIM$(Temp.Address3, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_CITYNAME),  RTRIM$(Temp.CityName, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_STATENAME), RTRIM$(Temp.StateName, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_ZIPCODE),   RTRIM$(Temp.ZipCode, $SPACE).c_str());
    SendMessage  (GetDlgItem(hWnd, ID_COUNTRY),   CB_SETCURSEL, Temp.Country - 1, 0);
    SetWindowText(GetDlgItem(hWnd, ID_PHONE),     RTRIM$(Temp.Phone, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_FAX),       RTRIM$(Temp.Fax, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_EMAIL),     RTRIM$(Temp.Email, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_URL),       RTRIM$(Temp.Url, $SPACE).c_str());
    SetWindowText(GetDlgItem(hWnd, ID_COMMENTS),  RTRIM$(Temp.Comments, $SPACE).c_str());

    // Restore the previous state
    gP.isrecmodified = SaveState;
}

void DialogToUdt (IN HWND hWnd, IN long Index) {
    Index--; // C++ array is 0 based
    AddressType Temp = { 0 };

    if ((Index > -1) && (Index < UBOUND(gAddressBook))) {
        RtlMoveMemory(&Temp, &gAddressBook[Index], sizeof(AddressType));
        GetWindowText(GetDlgItem(hWnd, ID_COMPANY), Temp.Company, sizeof(Temp.Company));
        GetWindowText(GetDlgItem(hWnd, ID_LASTNAME), Temp.LastName, sizeof(Temp.LastName));
        GetWindowText(GetDlgItem(hWnd, ID_FIRSTNAME), Temp.FirstName, sizeof(Temp.FirstName));
        GetWindowText(GetDlgItem(hWnd, ID_ADDRESS1),Temp.Address1, sizeof(Temp.Address1));
        GetWindowText(GetDlgItem(hWnd, ID_ADDRESS2), Temp.Address2, sizeof(Temp.Address2));
        GetWindowText(GetDlgItem(hWnd, ID_ADDRESS3), Temp.Address3, sizeof(Temp.Address3));
        GetWindowText(GetDlgItem(hWnd, ID_CITYNAME), Temp.CityName, sizeof(Temp.CityName));
        GetWindowText(GetDlgItem(hWnd, ID_STATENAME), Temp.StateName, sizeof(Temp.StateName));
        GetWindowText(GetDlgItem(hWnd, ID_ZIPCODE), Temp.ZipCode, sizeof(Temp.ZipCode));
        Temp.Country = (long) SendMessage(GetDlgItem(hWnd, ID_COUNTRY), CB_GETCURSEL, 0, 0) + 1;
        GetWindowText(GetDlgItem(hWnd, ID_PHONE), Temp.Phone, sizeof(Temp.Phone));
        GetWindowText(GetDlgItem(hWnd, ID_FAX), Temp.Fax, sizeof(Temp.Fax));
        GetWindowText(GetDlgItem(hWnd, ID_EMAIL), Temp.Email, sizeof(Temp.Email));
        GetWindowText(GetDlgItem(hWnd, ID_URL), Temp.Url, sizeof(Temp.Url));
        GetWindowText(GetDlgItem(hWnd, ID_COMMENTS), Temp.Comments, sizeof(Temp.Comments));

        RtlMoveMemory(&gAddressBook[Index], &Temp, sizeof(AddressType));
    }
}

void AddUpdateRecord (IN HWND hWnd, IN WPARAM wParam) {
    // This function is only called if gP.isrecmodified is TRUE
    WCHAR LastName[32] = { 0 };
    AddressType Temp = { 0 };

    // Get the Last Name field
    GetWindowText(GetDlgItem(hWnd, ID_LASTNAME), LastName, sizeof(LastName));

    // Check if Last Name entered; at this stage will not check for duplicate entries
    if (wcslen(LastName) == 0) {
        // Display error message dialog
        MessageBox(hWnd, L"You must specify a Last Name!", $NULL, MB_ICONEXCLAMATION);
        // Move focus to the offending control
        SetFocus(GetDlgItem(hWnd, ID_LASTNAME));
        return;
    }

    // Now determine if ADD or UPDATE buttons clicked
    if ((LOINT(wParam) == ID_ADD) || (gP.maxentries < 1)) { // It's the Add button, or we have no existing entries
        // Extend the UDT array and insert the new address data
        gP.maxentries++;
        gP.currententry = gP.maxentries;
        gAddressBook.resize(gP.maxentries);
    }

    // Get the data from the dialog
    DialogToUdt(hWnd, gP.currententry);

    // Reset the modified flag
    gP.isrecmodified = FALSE;

    // Flag data as modified
    gP.isfilemodified = TRUE;

    // Update the dialog controls
    SetButtonStatus(hWnd);

}

void DeleteRecord (IN HWND hWnd) {

    // Only permit a deletion if the index is valid
    if ((gP.currententry < 1) || (gP.currententry > gP.maxentries)) { return; }

    // Confirm deletion
    if (MessageBox(hWnd, L"Are you sure you want to delete this record?",
                   L"Delete Record", MB_ICONQUESTION | MB_OKCANCEL) == IDCANCEL) { return; }

    // Now perform the removal of the record and update our index variables
    //array delete gAddressBook[gP.currententry];
    gAddressBook.erase(gAddressBook.begin() + gP.currententry -1);
    gP.maxentries--;
    gP.currententry = min(gP.currententry, gP.maxentries);
    gAddressBook.resize(max(1, gP.maxentries));

    // Update our status flags & refresh the window
    gP.isrecmodified = FALSE;
    gP.isfilemodified = TRUE;
    SetButtonStatus(hWnd);
    UdtToDialog(hWnd, gP.currententry);

}

LRESULT CALLBACK FindDialogProc (IN HWND hWnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam) {
    long K = 0, wID = 0;
    HWND hParent = 0;
    wstring sFindText;
    WCHAR szRecord[sizeof(AddressType)] = { 0 };

    switch (uMsg) {
    case WM_COMMAND:
         wID = LOINT(wParam);
         switch (wID) {
         case IDCANCEL:
              if (HIINT(wParam) == BN_CLICKED) {
                  SendMessage(hWnd, WM_CLOSE, 0, 0);
                  return 0;
              }
              break;

         case ID_FIND:
              // Retrieve the search text if the edit control contents change.
              // We save it in a global string variable so we can recreate the
              // Find dialog with the same search text if it is relaunched again.
              if (HIINT(wParam) == EN_UPDATE) {  // The text was changed
                  // Get the text and trim away eventual leading/trailing spaces
                  GetWindowText(GetDlgItem(hWnd, ID_FIND), gP.findtext, sizeof(gP.findtext));
                  wcscpy_s(gP.findtext, TRIM$(gP.findtext, $SPACE).c_str());
              }
              return 0;

         case IDOK:  // Ok button
              // Now initiate a search as long as the search text is available
              if ((HIINT(wParam) == BN_CLICKED) && (wcslen(gP.findtext))) {
                  // Get main dialog handle
                  hParent = GetParent(hWnd);
                  // Start search from end record and loop from end to start
                  sFindText = CharUpper(gP.findtext);
                  for (K = gP.currententry; K < gP.maxentries; K++) {
                      RtlMoveMemory(&szRecord, &gAddressBook[K], sizeof(AddressType));
                      CharUpper(szRecord);
                      if (INSTR(0, szRecord, sFindText)) {
                          gP.isrecmodified = FALSE;
                          gP.currententry = K + 1;
                          UdtToDialog(hParent, gP.currententry);
                          SetButtonStatus(hParent);
                          return 0;
                      }
                  }

                  // No match between index and end, so restart from beginning of file
                  for (K = 0; K < gP.currententry; K++) {
                      RtlMoveMemory(&szRecord, &gAddressBook[K], sizeof(AddressType));
                      CharUpper(szRecord);
                      if (INSTR(0, szRecord, sFindText)) {
                          gP.isrecmodified = FALSE;
                          gP.currententry = K + 1;
                          UdtToDialog(hParent, gP.currententry);
                          SetButtonStatus(hParent);
                          return 0;
                      }
                  }
                  // Hmmm... no matches at all!
                  // By disabling the Modeless Find dialog while the MSGBOX is shown,
                  // we ensure the parent dialog keeps its zOrder and regains focus
                  // when the Find dialog is closed, else Windows may lose track of
                  // it and send it to the back..
                  EnableWindow(hWnd, FALSE);
                  MessageBox(hParent, L"Find record", L"No occurrences found.", MB_ICONERROR | MB_TASKMODAL);
                  EnableWindow(hWnd, TRUE);
              }
              return 0;
         }
         break;

    case WM_DESTROY:
         EnableWindow(GetParent(hWnd), 1);
         // Close the main window
         PostQuitMessage(0);
         return 0;
    }

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

long FindDialog (IN HWND hParent) {
    long nRet = 0;
    long x = 0, y = 0, w = 0, h = 0, K = 0, nWidth = 0, nHeight = 0, IsInitialized = 0;
    HWND hWnd = 0, hCtrl = 0;
    WNDCLASSEX wcx = { 0 };
    MSG msg = { 0 } ;
    RECT rc = { 0 }, rw = { 0 };
    DWORD dwStyle = 0, dwExStyle = 0;

    WCHAR zClass[] = L"TOOLFIND";
    wcx.cbSize = sizeof(wcx);
    IsInitialized = GetClassInfoEx(gP.hinstance, zClass, &wcx);
    if (!IsInitialized) {
        wcx.style         = 0;
        wcx.lpfnWndProc   = FindDialogProc;
        wcx.cbClsExtra    = 0;
        wcx.cbWndExtra    = 0;
        wcx.hInstance     = gP.hinstance;
        wcx.hIcon         = NULL;
        wcx.hCursor       = LoadCursor(NULL, IDC_ARROW);
        wcx.hbrBackground = (HBRUSH) COLOR_BTNSHADOW;
        wcx.lpszMenuName  = NULL;
        wcx.lpszClassName = zClass;
        wcx.hIconSm       = wcx.hIcon;
        if (RegisterClassEx(&wcx)) { IsInitialized = TRUE; }
    }

    if (IsInitialized) {

        SetRect(&rc, 0, 0, dpi(231), dpi(112));
        // Note: indeed we don't need AdjustWindowRectEx, because we use do not use a non-client area
        // but it won't hurt anything to keep it, in case we change our mind ;)
        dwStyle = WS_POPUP | WS_CAPTION | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU;
        dwExStyle = WS_EX_TOOLWINDOW;
        AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle);
        w = rc.right - rc.left; h = rc.bottom - rc.top;
        hWnd = CreateWindowEx(dwExStyle, zClass, L"Find Record", dwStyle, 0, 0, w, h, hParent, 0, gP.hinstance, NULL);
        if (hWnd) {
            // Add the controls
            hCtrl = addLabel(hWnd, -1, L"Find &Text:", dpi(3), dpi(30), dpi(55), dpi(16), WS_CHILD | WS_VISIBLE | SS_RIGHT);
            hCtrl = addTextBox(hWnd, ID_FIND, gP.findtext, dpi(61), dpi(26), dpi(154), dpi(22), WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL);
            SetFocus(hCtrl);
            dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
            hCtrl = addButton(hWnd, IDOK, L"&Find Next", dpi(86), dpi(76), dpi(60), dpi(23), dwStyle);
            hCtrl = addButton(hWnd, IDCANCEL, L"&Cancel", dpi(155), dpi(76), dpi(60), dpi(23), dwStyle);

            // Center the child popup in the parent window.
            GetWindowRect(hParent, &rw); nWidth = rw.right - rw.left; nHeight = rw.bottom - rw.top;
            GetWindowRect(hWnd, &rc); w = rc.right - rc.left; h = rc.bottom - rc.top;
            x = rw.left + ((nWidth - w) / 2);
            y = rw.top + ((nHeight - h) / 2);
            MoveWindow(hWnd, x, y, w, h, 0);

            EnableWindow(hParent, 0); // Disable the parent to make it a modal tool window, see also WM_DESTROY
            ShowWindow(hWnd, SW_SHOW);

            while (GetMessage(&msg, NULL, 0, 0)) {
                if (IsDialogMessage(hWnd, &msg) == 0) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }

            BringWindowToTop(hParent);
            nRet = (long) msg.wParam;
        }
    }
    return nRet;
}

void ShowButtons(IN HWND hWnd, IN long Visibility) {
    for (long  K = ID_PRIOR; K <= ID_PRINT; K++) {
        ShowWindow(GetDlgItem(hWnd, K), Visibility);
    }
    ShowWindow(GetDlgItem(hWnd, IDCANCEL), Visibility);
}

long PrintThisWindow(IN HWND hWnd) {
    WCHAR szBuffer[MAX_PATH] = { 0 };
    HDC hDCprint = 0;
    DWORD nBufferSize = MAX_PATH;
    long nRet = 0;
    if (GetDefaultPrinter(szBuffer, &nBufferSize)) {
        hDCprint = CreateDC(NULL, szBuffer, NULL, NULL);
        if (hDCprint) {
            RECT rc = { 0 };
            DOCINFO dinfo = { 0 };
            long hRes  = GetDeviceCaps(hDCprint, HORZRES);
            long vRes  = GetDeviceCaps(hDCprint, VERTRES);

            long offsetX = 15;
            long offsetY = 60;
            GetClientRect(hWnd, &rc);

            HDC hScreenDC = GetDC(0);
            HDC hDC = CreateCompatibleDC(hScreenDC);
            HBITMAP hBmp = CreateCompatibleBitmap(hScreenDC, rc.right, rc.bottom);
            SelectObject(hDC, hBmp);

            // Hide the buttons
            ShowButtons(hWnd, SW_HIDE);
            // Windows do the WM_PRINTCLIENT for us
            PrintWindow(hWnd, hDC, PW_CLIENTONLY);
            // Show the buttons
            ShowButtons(hWnd, SW_SHOW);

            float rCoef = min((hRes - (offsetX * 2)) / (float) rc.right, (vRes - (offsetY * 2)) / (float) rc.bottom);
            long fxSize = (long) (rc.right * rCoef);
            long fySize = (long) (rc.bottom * rCoef);
            long xtop = (hRes - fxSize) / 2 + offsetX;
            long ytop = (vRes - fySize) / 2 + offsetY;

            dinfo.cbSize = sizeof(dinfo);
            dinfo.lpszDocName = NULL;
            dinfo.lpszOutput = NULL;
            if (StartDoc(hDCprint, &dinfo) > 0) {
                if (StartPage(hDCprint) > 0) {
                    // Paint the bitmap
                    if (hBmp) {

                        BITMAP bm = { 0 };
                        HDC dcBitmap = CreateCompatibleDC(0);

                        GetObject(hBmp, sizeof(bm), &bm);
                        BITMAPINFO bi = { 0 };
                        bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
                        bi.bmiHeader.biWidth = bm.bmWidth;
                        bi.bmiHeader.biHeight = bm.bmHeight;
                        bi.bmiHeader.biPlanes = 1;
                        bi.bmiHeader.biBitCount = bm.bmBitsPixel;
                        bi.bmiHeader.biCompression = BI_RGB;

                        // Calculate space needed for the dib bits
                        DWORD nBufSize = (bm.bmWidth + 1) * (bm.bmBitsPixel / 8);
                        nBufSize = ((nBufSize + 3) / 4) * 4;
                        nBufSize = nBufSize * bm.bmHeight;

                        HGLOBAL ghnd = GlobalAlloc(GMEM_MOVEABLE, nBufSize);
                        LPVOID gptr = GlobalLock(ghnd);

                        if (GetDIBits(dcBitmap, hBmp, 0, bm.bmHeight, gptr, &bi, 0)) {
                            nRet = StretchDIBits(hDCprint, xtop, ytop, fxSize, fySize, 0, 0,
                                                 bm.bmWidth, bm.bmHeight, gptr, &bi, 0, SRCCOPY);
                        }

                        GlobalUnlock(ghnd);
                        GlobalFree(ghnd);
                        DeleteDC(dcBitmap);

                    }

                    if (EndPage(hDCprint) > 0) {
                        EndDoc(hDCprint);
                    }
                }
            }

            // Release
            DeleteDC(hDC);
            DeleteObject(hBmp);
            ReleaseDC(0, hScreenDC);

            DeleteDC(hDCprint);
        }

    }
    return nRet;
}

LRESULT CALLBACK WndProc(IN HWND hWnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam) {

    static HWND hFocus;

    switch (uMsg) {

    case WM_NCACTIVATE:
         // Save the handle of the control that has the focus
         if (wParam == 0) { hFocus = GetFocus(); }
         break;

    case WM_SETFOCUS:
         // Post a message to set the focus later, since some
         // Windows actions can steal it if we set it here
         if (hFocus) {
             PostMessage(hWnd, WM_APPLYFOCUS, (WPARAM) hFocus, 0);
             hFocus = 0;
         }
         break;

    case WM_APPLYFOCUS:
         // Set the focus
         if (wParam) { SetFocus((HWND) wParam); }
         return 0;

    case WM_COMMAND:
         if ((HIINT(wParam) == EN_UPDATE) // Any edit control was changed
             || ((LOINT(wParam) == ID_COUNTRY) && (HIINT(wParam) == CBN_SELCHANGE))) {   // The country combobox was changed
             // if this is the first "change" message, we need to update the controls in the dialog box.
             if (gP.isrecmodified == 0) {
                 gP.isrecmodified = TRUE;
                 SetButtonStatus(hWnd);
             }
         }

         // Messages from controls and menu items are handled here.
         switch (LOINT(wParam)) {
         case IDCANCEL:
              // if the Escape key has been pressed...
              if (HIINT(wParam) == BN_CLICKED) {
                  // Shut down the application by sending a WM_CLOSE message
                  SendMessage(hWnd, WM_CLOSE, 0, 0);
                  return 0;
              }

         case ID_PRIOR:
              gP.currententry = max(gP.currententry - 1, 1);
              // Disregard any existing editing if one of these button clicked
              gP.isrecmodified = FALSE;
              // Update the dialog
              UdtToDialog(hWnd, gP.currententry);
              // Update the Prior/} control button status
              SetButtonStatus(hWnd);
              return 0;

         case ID_NEXT:
              gP.currententry = min(gP.currententry + 1, gP.maxentries);
              // Disregard any existing editing if one of these button clicked
              gP.isrecmodified = FALSE;
              // Update the dialog
              UdtToDialog(hWnd, gP.currententry);
              // Update the Prior/} control button status
              SetButtonStatus(hWnd);
              return 0;

         case ID_FIND:
              FindDialog(hWnd);
              return 0;

         case ID_ADD:
         case ID_UPDATE:
              AddUpdateRecord(hWnd, wParam);
              return 0;

         case ID_DELETE:
              DeleteRecord(hWnd);
              return 0;

         case ID_PRINT:
              if (PrintThisWindow(hWnd)) {
                  MessageBox(hWnd, L"\nAddress printed successfully.", L"Info", MB_ICONINFORMATION);
              } else {
                  MessageBox(hWnd, L"\nUnable to print this address.", L"Printer error", MB_ICONEXCLAMATION);
              }
              return 0;

         case ID_MENU_HELP:
              MessageBox(hWnd, L"Help not implemented", L"Info", MB_ICONINFORMATION);
              return 0;

         case ID_MENU_ABOUT:
              MessageBox(hWnd, L"\nAddress Book by PowerBASIC, Inc.\n\nSDK translation by Patrice Terrier.", L"Address Book", MB_ICONINFORMATION);
              return 0;
         }
         break;

    case WM_DESTROY:
         PostQuitMessage(0);
         return 0;

    }

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

int WINAPI wWinMain(IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, IN LPTSTR lpCmdLine, IN int nCmdShow) {
    long nRet = 0;
    HWND hWnd = 0, hCtrl = 0;
    WNDCLASSEX wcx = { 0 };
    HANDLE hFile = 0;
    DWORD ByttesReaded = 0, BytesWritten = 0;
    MSG msg = { 0 };

    gP.usefont = (HFONT) GetStockObject(DEFAULT_GUI_FONT); // Tahoma
    gP.hinstance = hInstance;

    WCHAR szFilename[] = L"UADDRESS.DAT";
    DWORD BufferSize = FileSize(szFilename);
    if (BufferSize) {
        gP.currententry = 1;
        gP.maxentries = BufferSize / sizeof(AddressType);
        gAddressBook.resize(gP.maxentries);
        hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile != INVALID_HANDLE_VALUE) {
            ReadFile(hFile, &gAddressBook[0], BufferSize, &ByttesReaded, NULL);
            CloseHandle(hFile);
        }
    }

    WCHAR szClassName[] = L"SDKADDRESS64";
    wcx.cbSize = sizeof(wcx);
    long IsInitialized = GetClassInfoEx(hInstance, szClassName, &wcx);
    if (!IsInitialized) {
        wcx.style         = CS_HREDRAW | CS_VREDRAW;
        wcx.lpfnWndProc   = WndProc;
        wcx.cbClsExtra    = 0;
        wcx.cbWndExtra    = 0;
        wcx.hInstance     = hInstance;
        wcx.hIcon         = LoadIcon  (NULL, IDI_APPLICATION);
        wcx.hCursor       = LoadCursor(NULL, IDC_ARROW);
        wcx.hbrBackground = (HBRUSH) COLOR_BTNSHADOW;
        wcx.lpszMenuName  = NULL;
        wcx.lpszClassName = szClassName;
        wcx.hIconSm       = wcx.hIcon;
        if (RegisterClassEx(&wcx)) { IsInitialized = TRUE; }
    }
    if (IsInitialized) {
        long w = dpi(606); long x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
        long h = dpi(462); long y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
        hWnd = CreateWindowEx(WS_EX_CONTROLPARENT | WS_EX_WINDOWEDGE, szClassName, L"Address Book - flat API SDK version",
                    WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU | DS_3DLOOK | DS_NOFAILCREATE | DS_SETFONT | DS_CENTER,
                    x, y, w, h, 0, 0, hInstance, NULL);
        if (hWnd) {
            DWORD dwLabelStyle = WS_CHILD | WS_VISIBLE | SS_RIGHT;
            DWORD dwEditStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL;

            hCtrl = addLabel(hWnd, - ID_INDEX, L"Record Nº", 455, 8, 90, 13, dwLabelStyle);
            hCtrl = addLabel(hWnd, ID_INDEX, L"0 / 0", 549, 7, 38, 16, WS_CHILD | WS_VISIBLE | SS_SUNKEN | SS_CENTER);

            hCtrl = addLabel(hWnd, - ID_COMPANY, L"Company", 8, 31, 90, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_COMPANY, $NULL, 102, 28, 485, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_LASTNAME, L"Last Name", 8, 54, 90, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_LASTNAME, $NULL, 102, 51, 225, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_FIRSTNAME, L"First", 329, 54, 27, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_FIRSTNAME, $NULL, 362, 51, 225, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_ADDRESS1, L"Address", 8, 78, 90, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_ADDRESS1, $NULL, 102, 75, 485, 19, dwEditStyle);

            hCtrl = addTextBox(hWnd, ID_ADDRESS2, $NULL, 102, 99, 485, 19, dwEditStyle);
            hCtrl = addTextBox(hWnd, ID_ADDRESS3, $NULL, 102, 124, 485, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_CITYNAME, L"City", 8, 151, 90, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_CITYNAME, $NULL, 102, 148, 174, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_STATENAME, L"State/Province", 282, 151, 75, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_STATENAME, $NULL, 362, 148, 67, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_ZIPCODE, L"Zip or Postal Code", 8, 177, 90, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_ZIPCODE, $NULL, 102, 174, 60, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_COUNTRY, L"Country", 193, 177, 38, 13, dwLabelStyle);
            hCtrl = addComboBox(hWnd, ID_COUNTRY, $NULL, 235, 174, 194, 170, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_HASSTRINGS | CBS_NOINTEGRALHEIGHT);
            addCountries(hCtrl);

            hCtrl = addLabel(hWnd, - ID_PHONE, L"Phone", 8, 206, 90, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_PHONE, $NULL, 102, 203, 225, 20, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_FAX, L"Fax", 329, 206, 27, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_FAX, $NULL, 362, 203, 225, 20, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_EMAIL, L"Email", 8, 234, 90, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_EMAIL, $NULL, 102, 231, 225, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_URL, L"www", 331, 234, 27, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_URL, $NULL, 362, 231, 225, 19, dwEditStyle);

            hCtrl = addLabel(hWnd, - ID_COMMENTS, L"Comments", 8, 260, 90, 13, dwLabelStyle);
            hCtrl = addTextBox(hWnd, ID_COMMENTS, $NULL, 102, 257, 485, 105, dwEditStyle | WS_VSCROLL | ES_MULTILINE | ES_WANTRETURN);

            DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
            hCtrl = addButton(hWnd, ID_PRIOR,  L"<<  Prior",  16, 379, 60, 22, dwStyle);
            hCtrl = addButton(hWnd, ID_NEXT,   L"Next  >>",   89, 379, 60, 22, dwStyle);
            hCtrl = addButton(hWnd, ID_FIND,   L"Find",      162, 379, 60, 22, dwStyle);
            hCtrl = addButton(hWnd, ID_ADD,    L"Add",       235, 379, 60, 22, dwStyle);
            hCtrl = addButton(hWnd, ID_UPDATE, L"Update",    308, 379, 60, 22, dwStyle);
            hCtrl = addButton(hWnd, ID_DELETE, L"Delete",    381, 379, 60, 22, dwStyle);
            hCtrl = addButton(hWnd, ID_PRINT,  L"Print",     454, 379, 60, 22, dwStyle);
            hCtrl = addButton(hWnd, IDCANCEL,  L"Close",     527, 379, 60, 22, dwStyle);
            SetButtonStatus(hWnd);

            HMENU hMenu = CreateMenu();
               HMENU hPopMenu = CreatePopupMenu();
               AppendMenu(hMenu, MF_POPUP, (LONG_PTR) hPopMenu, L"File");
                  AppendMenu(hPopMenu, 0, 2, L"Close");
               hPopMenu = CreatePopupMenu();
               AppendMenu(hMenu, MF_POPUP, (LONG_PTR) hPopMenu, L"Help");
                  AppendMenu(hPopMenu, 0, ID_MENU_HELP, L"Help");
                  AppendMenu(hPopMenu, MF_SEPARATOR, 0, $NULL);
                  AppendMenu(hPopMenu, 0, ID_MENU_ABOUT, L"About");
            SetMenu(hWnd, hMenu);

            UdtToDialog(hWnd, gP.currententry);

            ShowWindow(hWnd, nCmdShow);
            UpdateWindow(hWnd);

            while (GetMessage(&msg, NULL, 0, 0)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }

            // Save our data file if any changes were made
            if (gP.isfilemodified) {
                //array sort gAddressBook[1] FOR gP.maxentries,
                //           from 1 to len(gAddressBook[1].Company + gAddressBook[1].LastName)
                hFile = CreateFile(szFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
                if (hFile != INVALID_HANDLE_VALUE) {
                    WriteFile(hFile, &gAddressBook[0], gP.maxentries * sizeof(AddressType), &BytesWritten, NULL);
                    CloseHandle(hFile);
                }
            }

            nRet = (long) msg.wParam;
        }
    }
    return nRet;
}
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Patrice Terrier

Note: When dealing with pixel display you can just rely on either GetDeviceCaps(hDC, LOGPIXELSX) or GetDeviceCaps(hDC, LOGPIXELSY), because they will ALWAYS return the same value.

I have never seen a display using non-square pixels.  ???

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Patrice Terrier

#3
Hutch--

The full DPI aware projects (32-bit and 64-bit) have been attached to the first post of this thread.

...

Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com