Thursday, February 1, 2018

Use of C++ for Learn to Code GR application


Use of Windows Controls from C++

On January 22 I wondered if a language like C could invoke JavaScript of an html file or vice versa.  If so, then JavaScript could be used to display Windows forms and controls while C or some other language could be used for computations, reading and updating files, and the like with the ability to actually compile the C and have errors detected via the compile.

I soon decided against that approach since it didn't seem like cross language usage was possible.  So I thought I would find out how to directly invoke Windows control functions from C or C++ without the use of a visual compiler.

The next day I found a Microsoft post with sample code to display a window and spent the day getting it to work using the GNAT compiler for C and C++.  This involved finding where I could find the needed windows.h header file and working out why the Microsoft code wouldn't compile.

Another internet search found the answer as to where windows.h was located.  It said "Check it out under Program Files (x86)\Windows Kits\8.1\Include\um\Windows.h".  Doing a Windows Explorer search of the Program Files (x86) folder produced a number of other folders where I had these headers on my computer.  So I picked the one with the most recent date and copied it to a subfolder in the folder where I was building the new application and changed the GNAT project to include it.

However, the Microsoft example wouldn't compile and I found another function that I needed to include and that some of variables needed to be typecast to enable GNAT, at least, to successfully compile the project.  I also found that the GNAT folders contained the needed headers that, for some reason, the compiler hadn't used when I first started.

After that I could run the application and it would display the window with its three buttons in the upper right corner.  And the buttons worked correctly. But the maximize only had the original portion of the window with a white background and, after the restore of a minimize, the background was black instead of white.

Therefore, I neatened up the code (including added code to find what error I was getting before I solved the last refusal to compile problem when I had removed one statement).  No matter what I did trying to get an errorless compile with WHITE_BRUSH mentioned, I couldn't so I used 0 instead where the wingdi.h header defines WHITE_BRUSH as 0.

The result is as follows.

// from https://msdn.mircosoft.com/en-us/library/windows/desktop/ms633575(v=vs.85).aspx
// as modified

#include <windows.h>
#include <wingdi.h>

// Global variable

HINSTANCE hinst;

// Function prototypes.

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

// Application entry point.

int WINAPI WinMain(HINSTANCE hinstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
  MSG msg;

  BOOL result;
  result = InitApplication(hinstance);
  if (!result)
        return FALSE;

    if (!InitInstance(hinstance, nCmdShow))
        return FALSE;

    BOOL fGotMessage;
    while ((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0 && fGotMessage != -1)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
    UNREFERENCED_PARAMETER(lpCmdLine);
} // end WinMain

LRESULT CALLBACK MainWndProc( HWND hwnd,        // handle to window
                              UINT uMsg,        // message identifier
                              WPARAM wParam,    // first message parameter
                              LPARAM lParam)    // second message parameter
{ 
  switch (uMsg)
  {
    case WM_CREATE:
      // Initialize the window.
      return 0;

    case WM_PAINT:
      // Paint the window's client area.
      return 0;

    case WM_SIZE:
      // Set the size and position of the window.
      return 0;

    case WM_DESTROY:
      // Clean up window-specific data objects.
      return 0;

    //
    // Process other messages.
    //

    default:
      return DefWindowProc(hwnd, uMsg, wParam, lParam);
  }
  return 0;
} // end MainWndProc

BOOL InitApplication(HINSTANCE hinstance)
{
  WNDCLASSEX wcx;

  // Fill in the window class structure with parameters
  // that describe the main window.

  wcx.cbSize = sizeof(wcx);          // size of structure
  wcx.style = CS_HREDRAW | CS_VREDRAW;      // redraw if size changes
  wcx.lpfnWndProc = (WNDPROC) MainWndProc;  // points to window procedure
  wcx.cbClsExtra = 0;                // no extra class memory
  wcx.cbWndExtra = 0;                // no extra window memory
  wcx.hInstance = hinstance;         // handle to instance
  wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION); // predefined app. icon
  wcx.hCursor = LoadCursor(NULL, IDC_ARROW);   // predefined arrow
  wcx.hbrBackground = (HBRUSH)0;     // white background brush
  wcx.lpszMenuName =  "MainMenu";    // name of menu resource
  wcx.lpszClassName = "MainWClass";  // name of window class
  wcx.hIconSm = (HICON)LoadImage(hinstance, // small class icon
                                 MAKEINTRESOURCE(5),
                                 IMAGE_ICON,
                                 GetSystemMetrics(SM_CXSMICON),
                                 GetSystemMetrics(SM_CYSMICON),
                                 LR_DEFAULTCOLOR);

  // Register the window class.
  ATOM x = RegisterClassEx(&wcx);
  DWORD y;
  if (x==0)
  { y = GetLastError();
  }
 
  // return RegisterClassEx(&wcx);
  return (BOOL)x;
} // end InitApplication

BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
  HWND hwnd;

  // Save the application-instance handle.

  hinst = hinstance;

  // Create the main window.

  hwnd = CreateWindow
         ( "MainWClass",        // name of window class
           "Sample",            // title-bar string
           WS_OVERLAPPEDWINDOW, // top-level window
           CW_USEDEFAULT,       // default horizontal position
           CW_USEDEFAULT,       // default vertical position
           CW_USEDEFAULT,       // default width
           CW_USEDEFAULT,       // default height
           (HWND) NULL,         // no owner window
           (HMENU) NULL,        // use class menu
           hinstance,           // handle to application instance
           (LPVOID) NULL);      // no window-creation data

  if (!hwnd)
    return FALSE;

  // Show the window and send a WM_PAINT message to the window
  // procedure.

  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);
  return TRUE;

} // end InitInstance

While trying to find a way to repaint the screen after a resize so that the background would be white again instead of black, I found out that GNAT had the needed headers all along.  So a mystery why it didn't use its own windows.h and wingdi.h from the start.

I found that I could use
  wcx.hbrBackground = (HBRUSH)WHITE_BRUSH;     // white background brush
if I didn't use SetStockObject which the GNAT linker couldn't find. 

However, changing the window size with its hide button and then restoring it or with its full screen button still resulted in a black background.  Black for the extra screen space in the one case and black after restoring the hidden window in the other.  If the full screen is hidden and then restored, the entire screen is then black.

I added a case statement for WM_ERASEBKGND to the callback and fetched the class info.  It is invoked a few times.  But each time it returns that the HBRUSH color for the background is 0 and hence still white even thou the window is black after the restore from minimize.  So the class info attributes are unchanged but painting the window doesn't seem to be using them after a minimize and restore or a maximize.

I finally found the answer in another internet search where

c++ - How to write text into window? - Stack Overflow

https://stackoverflow.com/.../5946596/how-to-write-text-into-window
appeared as one of the options.  It had the use of
  wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
rather than what the Microsoft example had had.  This worked.  [Of course, the Microsoft example had had
  wcx.hbrBackground = GetStockObject(WHITE_BRUSH);
which didn't work with the GNAT compiler since it couldn't link in GetStockObject.]

The window was created with a white background.  Then after the minimize and restore it was still white.  And upon maximize the entire screen window was all white. 

Problem solved on Thursday afternoon.  So a good share of three days overall to display a window that works correctly using just C++ via the GNAT compiler with its GPS windows interface.

Next Step:

After getting the display of the window to work correctly I decided to implement the same application (Learn to Code GR) of my recent posts in non-visual C++.

As a first step I tried using the OpenFileDialog function of commdlg.h that allows the user to navigate folders and open a particular file so that the file wouldn't need to be hard coded.  In the visual C# application, this was done along with a Windows control to click to enter the event handler code from which this function could be invoked.  So I was thinking of having such a control on the window.  But first just invoking the function after the window had been displayed.

After numerous attempts I couldn't get a GNAT build to include the code corresponding to the commdlg.h header.

Therefore, I stopped work on that approach for the time being and decided to just implement the previous Learn to Code GR application in C++ with the needed file path hard coded in the application.

Since the application is in C++ I used classes to retain the file data for the different keys, their associated values, and the number of times a particular value was specified for a key.

The C++ application code is as follows.  As with the previous versions of the application that used languages that support the concept of a class, most of the code is classless with only the structures to retain the data encoded as a class.  However, unlike the others, this one only reads the file a record at a time rather than the entire file all at once.  The KeyTable class retains the data with multiple instances of the Key class for a particular key with its arrays to retain the key and arrays to retain the various values and the corresponding number of times the value was referenced (the sums array).

WinMain.cpp
// from https://msdn.mircosoft.com/en-us/library/windows/desktop/ms633575(v=vs.85).aspx
// as modified for the WinMain portion of this file.
#include <stdio.h>
#include <iostream>
#include <cstring>

#include <windows.h>
#include <wingdi.h>

#include <keytable.h>

// Global variable

HINSTANCE hinst;

// Function prototypes.

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

BOOL StartApp();

// Application entry point.

int WINAPI WinMain( HINSTANCE hinstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine,
                    int nCmdShow )
{
  MSG msg;

  BOOL fileOpenEvent;
  fileOpenEvent = TRUE; // act as if File control clicked

  BOOL result;
  result = InitApplication(hinstance);
  if (!result)
        return FALSE;

    if (!InitInstance(hinstance, nCmdShow))
        return FALSE;

    BOOL fGotMessage;
    while ((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0 && fGotMessage != -1)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if (fileOpenEvent)
        {
            fileOpenEvent = FALSE; // dummy indication that can only be used once
            // Start main portion of the application.
            result = StartApp();
        }

    }

    return msg.wParam;
    UNREFERENCED_PARAMETER(lpCmdLine);

} // end WinMain

// To treat Windows events
LRESULT CALLBACK MainWndProc( HWND hwnd,        // handle to window
                              UINT uMsg,        // message identifier
                              WPARAM wParam,    // first message parameter
                              LPARAM lParam )   // second message parameter
{

  PAINTSTRUCT ps;
  HDC hdc;

  switch (uMsg)
  {
    case WM_CREATE:
      // Initialize the window.
      return 0;

    case WM_PAINT:
    {
      // Paint the window's client area.
      return 0;
    }

    case WM_SIZE:
      // Set the size and position of the window.
      return 0;

    case WM_DESTROY:
      // Clean up window-specific data objects.
      return 0;

    // Process other messages.

    default:
      return DefWindowProc(hwnd, uMsg, wParam, lParam);
  }
  return 0;

} // end MainWndProc

BOOL InitApplication(HINSTANCE hinstance)
{
  WNDCLASSEX wcx;

  // Fill in the window class structure with parameters
  // that describe the main window.

  wcx.cbSize = sizeof(wcx);          // size of structure
  wcx.style = CS_HREDRAW | CS_VREDRAW;      // redraw if size changes
  wcx.lpfnWndProc = (WNDPROC) MainWndProc;  // points to window procedure
  wcx.cbClsExtra = 0;                // no extra class memory
  wcx.cbWndExtra = 0;                // no extra window memory
  wcx.hInstance = hinstance;         // handle to instance
  wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);    // predefined app. icon
  wcx.hCursor = LoadCursor(NULL, IDC_ARROW);      // predefined arrow
//wcx.hbrBackground = (HBRUSH)GetStockObject((int)WHITE_BRUSH); // white background brush
  wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // white background brush
  wcx.lpszMenuName =  "MainMenu";    // name of menu resource
  wcx.lpszClassName = "MainWClass";  // name of window class
  wcx.hIconSm = (HICON)LoadImage(hinstance, // small class icon
                                 MAKEINTRESOURCE(5),
                                 IMAGE_ICON,
                                 GetSystemMetrics(SM_CXSMICON),
                                 GetSystemMetrics(SM_CYSMICON),
                                 LR_DEFAULTCOLOR);

  // Register the window class.
  ATOM x = RegisterClassEx(&wcx);
  DWORD y;
  if (x==0)
  { y = GetLastError();
  }

  // return RegisterClassEx(&wcx);
  return (BOOL)x;
} // end InitApplication

BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
  HWND hwnd;

  // Save the application-instance handle.

  hinst = hinstance;

  // Create the main window.

  hwnd = CreateWindow
         ( "MainWClass",        // name of window class
           "LearnToCodeGR",     // title-bar string
           WS_OVERLAPPEDWINDOW, // top-level window
           CW_USEDEFAULT,       // default horizontal position
           CW_USEDEFAULT,       // default vertical position
           CW_USEDEFAULT,       // default width
           CW_USEDEFAULT,       // default height
           (HWND) NULL,         // no owner window
           (HMENU) NULL,        // use class menu
           hinstance,           // handle to application instance
           (LPVOID) NULL);      // no window-creation data

  if (!hwnd)
    return FALSE;

  // Show the window and send a WM_PAINT message to the window
  // procedure.

  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);
  return TRUE;

} // end InitInstance

/*
 * From here to the end is the C++ code for the LearnToCodeGR application.
 */

KeyTable keyTable; // KeyTable class object

// Data to be retained as the key and its associated value with the maximum
// number of references
int maxKey = 0;
int maxValue = 0;
int maxSum = 0;

int savedData[3];

// Report the key and value with the most references
void Report()
{
  // Report individual results
  printf("\n");
  printf("Report   ");
  printf("\n");

  for (int i = 0; i < keyTable.getKeyCount(); i++)
  {
    for (int j = 0; j < keyTable.getValueCount(i); j++)
    {
      std::string report = ("Key ");
      report = report + std::to_string(keyTable.getKey(i));
      report = report + (" Value ");
      report = report + std::to_string(keyTable.getValue(i,j));
      report = report + (" Sum ");
      report = report + std::to_string(keyTable.getSum(i,j));
      std::cout << report << '\n';
    } // end for loop
  } // end for loop

  // Items identifying the key with the largest number of a particular value
  int key = 0;
  int value = 0;
  int sum = 0;

  for (int i = 0; i < keyTable.getKeyCount(); i++)
  {
    for (int j = 0; j < keyTable.getValueCount(i); j++)
    {
      if (keyTable.getSum(i,j) > sum) // save key and value with greater sum
      {
        key = keyTable.getKey(i);
        value = keyTable.getValue(i,j);
        sum = keyTable.getSum(i,j);
      }
    } // end for loop
  } // end for loop

  printf("\n");
  std::string report = ("Key and Value with greatest Sum ");
  report = report + std::to_string(key) + " ";
  report = report + std::to_string(value) + " ";
  report = report + std::to_string(sum);
  std::cout << report << '\n';

  return;
} // end Report

// Update keyTable with data from a parsed line
void Update(int nKey, int nValue1, int nValue2)
{
  // This function first checks if the key is new and, if so, adds it to the
  // keys array.  It then does similar for the values associated with it.

  // Check whether the key is already in the table
  int keyIndex = -1;
  int valueIndex = -1;

  int keyCount = keyTable.getKeyCount();
  for (int i = 0; i < keyCount; i++)
  {
    if (keyTable.getKey(i) == nKey)
    {
      keyIndex = i;  // key already in table
      break; // exit loop
    }
  } // end loop
  if (keyIndex < 0) // key not in the table
  {
    keyIndex = keyTable.setKey(nKey); // add key
    valueIndex = keyTable.setValue(keyIndex,nValue1); // add value for key
    if (nValue1 != nValue2)
    { keyTable.setValue(keyIndex,nValue2); }
    else
    { keyTable.incSum(keyIndex,valueIndex); }
  }
  else // key in the table
  {
    int valueCount = keyTable.getValueCount(keyIndex);
    valueIndex = keyTable.setValue(keyIndex,nValue1); // add value for key
    if (nValue1 != nValue2)
    { keyTable.setValue(keyIndex,nValue2); }
    else
    { keyTable.incSum(keyIndex,valueIndex); }
  }

  return;

} // end Update

#define NINE 57 // ASCII character for digit 9
#define ZERO 48 // ASCII character for digit 0

// Convert character array to int
int toInt(char* data, int length) //iS, int iE)
{
  int index = length - 1; // loop in reverse
  int digit;
  int m = 1;      // multiplier for shift
  int number = 0; // Numeric result

  while (index >= 0) //iS)
  {
    digit = data[index] - ZERO; // convert ASCII to digit
    number = number + (m * digit);
    m = m * 10;
    index--;
  }

  return number;

} // end toInt

enum Scan //Fields
{ BYPASS, FKEY, VALUE1, VALUE2 };

// Extract the fields of interest from the file's line and retain the data
void Parse(int count, char* data)
{
  #define CR '\r' // carriage return
  #define HT '\t' // horizontal tab
  #define LF '\n' // line feed
  #define TRUE 1

  // Parse the supplied line of data from the file to obtain the three
  // fields of interest (key and two values), convert those fields to
  // integers, and then update a data structure (the KeyTable class) to
  // retain the data for evaluation when the complete file has been parsed.

  int keyValue = 0;
  int value1Value = 0;
  int value2Value = 0;

  char alphaDigits [10];
  int numDigits = 0;

  bool updateDone = false;

  int offset = 0; // offset into data
  Scan scanPhase = BYPASS;

  // Parse all bytes of record previously read from the file.  Each field of
  // interest is preceded by a horizontal tab.  The end of the final field
  // is marked by a CR LF or only a LF (NL) except the last record where
  // there are no more characters to be treated.
  while (offset < count)
  {
    switch (scanPhase)
    {
      case BYPASS:
        if (data[offset] == HT)
        {
          scanPhase = FKEY;
          keyValue = 0;
          value1Value = 0;
          value2Value = 0;
          numDigits = 0;
        }
        break;

      case FKEY:
        if (data[offset] != HT)
        {
          if ((data[offset] >= ZERO) & (data[offset] <= NINE))
          {
            alphaDigits[numDigits] = data[offset];
            numDigits++;
          }
        }
        else
        {
          keyValue = toInt(alphaDigits, numDigits);
          scanPhase = VALUE1;
          numDigits = 0;
        }
        break;

      case VALUE1:
        if (data[offset] != HT)
        {
          if ((data[offset] >= ZERO) & (data[offset] <= NINE))
          {
            alphaDigits[numDigits] = data[offset];
            numDigits++;
          }
        }
        else
        {
          value1Value = toInt(alphaDigits, numDigits);
          scanPhase = VALUE2;
          numDigits = 0;
        }
        break;

      case VALUE2:
        if ((data[offset] == LF) | (data[offset] == CR) | offset+1 >= count)
        {
          value2Value = toInt(alphaDigits, numDigits);
          // Update tables with the data from the record.
          Update( keyValue, value1Value, value2Value );
          updateDone = true;

          // Initialize for next record
          keyValue = 0;
          value1Value = 0;
          value2Value = 0;
          numDigits = 0;
          scanPhase = BYPASS;
        }
        else
        {
          if ((data[offset] >= ZERO) & (data[offset] <= NINE))
          {
            alphaDigits[numDigits] = data[offset];
            numDigits++;
          }
        }
        break;
    } // end switch

    // Complete processing of final record when not terminated by New Line
    offset++; // increment to next data buffer position
    if (offset >= count) // no more data
    {
      if (value2Value > 0) // last record fully parsed without trailing NL
      {
        if (!updateDone)
        {
          value2Value = toInt(alphaDigits, numDigits);
          Update( keyValue, value1Value, value2Value );
          updateDone = true;
        }
      }
      break; // exit loop
    }

  } // end loop

  return;

} // end Parse

BOOL StartApp()
// Open the LearnToCodeGR file and treat it.
{
  FILE* file;
  char buffer[30];
  file = fopen("C:\\Source\\LearnToCodeGR\\max-col-sum-by-Key.tsv", "r");

  int currPos;
  int lastPos = 0;
  int length;

  if(file == NULL)
  {
    perror("Error opening file");
    return FALSE;
  }
  else
  { // Read file records until end-of-file
    while (!feof(file))
    {
      fgets(buffer, 30, (FILE*)file);
      currPos = ftell(file);
      length = currPos - lastPos; // number of characters read into buffer
      lastPos = currPos;
      for (int i=0; i < length; i++) // output the
        printf("%c ", buffer[i]);    //   record to
      printf("\n");                  //   the console

      // Parse the line and update to retain all the necessary data.
      Parse(length, buffer);
    }
    fclose(file);

    // Report the results.
    Report();
  }
  return TRUE;
} // end StartApp

KeyTable.h
#ifndef KEYTABLECPP
#define KEYTABLECPP

// Key class
class Key
{
  public:
    Key(); // constructor

    int key;        // value of key
    int valueCount; // number of different values associated with key
    static const int VALUESSIZE = 20; // initial size of values & sums
                                      //  arrays for particular key
    int *values;    // will point to the array
    int *sums;      //  "

}; // end class Key

// KeyTable class
class KeyTable //: public Component
{

  public:
    KeyTable(); // constructor

    int getKey(int index);
    int getKeyCount();
    // Return number of different values for key at index
    int getValueCount(int index);

    int setKey(int key);
    int setValue(int index, int value);
    int getValue(int indexKey, int indexValue);
    int incSum(int indexKey, int indexValue);
    int getSum(int indexKey, int indexValue);

  private:
    int keyCount; // number of different keys
    static const int TABLESIZE = 30; // initial size of keys array
    Key* keys = new Key[TABLESIZE];  // instances of "struct" class

}; // end class KeyTable

#endif // KEYTABLECPP

KeyTable.cpp
#include <iostream>

#include "keytable.h"

Key::Key() // constructor
{
  valueCount = 0;
  key = 0;
  values = new int[VALUESSIZE];
  sums = new int[VALUESSIZE];
  for (int i = 0; i < 20; i++)
  {
    values[i] = -1; // so no match until a value added
    sums[i] = 0;    // so any first increment of the sum will be to 1
  }
} // end Key constructor


KeyTable::KeyTable() // constructor
{
  this->keyCount = 0;
} // end KeyTable constructor

// Return key at index
KeyTable::getKey(int index)
{
  int tempKey;
  if (index < KeyTable::keyCount)
  {
    int key = KeyTable::keys[index].key;
    return key;
  }
  else
  {
    return 0;
  }
} // end getKey

// Return number of keys in table
KeyTable::getKeyCount()
{
  return keyCount;
} // end getKeyCount

KeyTable::getValueCount(int index)
{
  return this->keys[index].valueCount;
} // end getValueCount

KeyTable::setKey(int key)
{
  for (int i = 0; i < keyCount; i++)
  {
    if (this->keys[i].key == key) // key already in table
    {
      return i;
    }
  } // end for loop
  if (this->keyCount < 30) // add key to table if room
  {
    this->keys[keyCount].key = key;
    int index = keyCount;
    keyCount++;
    return index;
  }
  else
  {
    std::cout << "Error: Too many different keys" << "\n";
    return -1;
  }
} // end setKey

// Add value for key at index, return value index
KeyTable::setValue(int index, int value)
{
  for (int i = 0; i < this->keys[index].valueCount; i++)
  {
    if ( this->keys[index].values[i] == value )
    {
      // Increment sum
      this->keys[index].sums[i]++;
      return i;
    }
  } // end for loop

  // Add value to array for key since didn't return for existing value
  this->keys[index].values[this->keys[index].valueCount] = value;
  this->keys[index].sums[this->keys[index].valueCount]++;
  int valueIndex = this->keys[index].valueCount;
  this->keys[index].valueCount++; // increment number of values for key
  return valueIndex;
} // end setValue

// Get value for key at indexes, return value
KeyTable::getValue(int indexKey, int indexValue)
{
  return this->keys[indexKey].values[indexValue];
} // end getValue

// Increment Sum for value of key at indexKey and value at indexValue
KeyTable::incSum(int indexKey, int indexValue)
{
  // Increment sum
  int count = this->keys[indexKey].sums[indexValue]++;

  return count;
} // end incSum

// Get sum for key at indexes, return sum
KeyTable::getSum(int indexKey, int indexValue)
{
  return this->keys[indexKey].sums[indexValue];
} // end getSum

The window that was created by the initial portion of the WinMain.cpp file is below followed by the last portion of the console output from running the application.




Next I will return to my attempt to populate the window with Windows controls using a non-visual version of C++.

No comments: