Author Topic: Address book  (Read 325 times)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 547
    • zapsolution
Address book
« on: July 26, 2017, 04:32:45 pm »
This is the C++ 64-bit transposition of the very {basic} PowerBASIC DDT "Adress book" example.



This version explores dynamic memory allocation without using vector or UDT array, relying on direct use of the core flat API to create a small standalone EXE of 30 Kb.

All the hassle of dynamic memory allocation is done by using several hidden memory LISTBOX, to perform:
Add, insert, delete, replace, find, sort.
This project could be extended to manipulate any database record structure, working with row and column.

This procedural SDK code, works the same with any programming language in either 32 or 64-bit.

Note: The C++ interface is exactly the same than the PB's DDT, it is up to you to add bells and whistles ;)

The UDT AddressType used for each record in the database
Code: [Select]
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];
};

Memory allocation:
This is where the hidden LISTBOX are created, using only the WS_CHILD style.
Code: [Select]
long CreateMemoryList(IN HWND hWnd) {
    long nRet = 0;
    DWORD dwStyle = WS_CHILD;
    long iCol;
    for (iCol = 0; iCol < C_MAX; iCol++) {
        gP.hCol[iCol] = CreateWindowEx(0, L"LISTBOX", L"", dwStyle, 0, 0, 0, 0, hWnd, (HMENU) iCol + 300, gP.hinstance, NULL);
    }
    if (IsWindow(gP.hCol[C_MAX - 1])) {
        DWORD bufferSize = FileSize(DATA_BASE_NAME);
        if (bufferSize) {
            DWORD ByttesReaded = 0;
            AddressType record = { 0 };
            DWORD recordsize = sizeof(AddressType);
            gP.currententry = 1;
            gP.maxentries = bufferSize / recordsize;
            gP.fromRow = LB_ERR;
            HANDLE hFile = CreateFile(DATA_BASE_NAME, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if (hFile != INVALID_HANDLE_VALUE) {

                for (long nRow = 0; nRow < gP.maxentries; ++nRow) {
                    ReadFile(hFile, &record, recordsize, &ByttesReaded, NULL);
                    AddRecord(record);
                }

                CloseHandle(hFile);
                nRet = -1;
            }
        }
    }
    return nRet;
}
If there are already records in the database, they are used to populate each LISTBOX with the AddRecord procedure.
Code: [Select]
void AddRecord(IN AddressType record) {
    List_Add(gP.hCol[C_Company], record.Company);
    List_Add(gP.hCol[C_LastName], record.LastName);
    List_Add(gP.hCol[C_FirstName], record.FirstName);
    List_Add(gP.hCol[C_Address1], record.Address1);
    List_Add(gP.hCol[C_Address2], record.Address2);
    List_Add(gP.hCol[C_Address3], record.Address3);
    List_Add(gP.hCol[C_CityName], record.CityName);
    List_Add(gP.hCol[C_StateName], record.StateName);
    List_Add(gP.hCol[C_ZipCode], record.ZipCode);
    List_Add(gP.hCol[C_Country], STRL(record.Country));
    List_Add(gP.hCol[C_Phone], record.Phone);
    List_Add(gP.hCol[C_Fax], record.Fax);
    List_Add(gP.hCol[C_Email], record.Email);
    List_Add(gP.hCol[C_Url], record.Url);
    List_Add(gP.hCol[C_Comments], record.Comments);
}

Record manager:
- CreateRecord
- AddRecord,
- AddUpdateRecord
- UpdateRecord
- DeleteRecord
- EraseRecord

Data allocation:
- GetRecordField
- DialogToUdt
- UdtToDialog

Utility:
- RecordSearch
- SaveDatabase
- PrintThisWindow (you must first define a default printer in Windows)
The PrintWindow API is used with PW_CLIENTONLY to print a hard copy of the client area only, using a CompatibleBitmap.
« Last Edit: July 27, 2017, 10:31:49 am by Patrice Terrier »