Sunday, August 8, 2010

MySQL Cluster on Windows - NDB API part 3 - The code

If you followed this series on Windows programing with NDB API on Windows, you should now have a reasonable MySQL Cluster with NDB API setup. And if you are not following it, and think that the binary MySQL Cluster build and some basic MySQL knowledge is all you need to start writing code: Think again! That sure isn't the case, so have a look at the previous blogs in this series:
MySQL Cluster on Windows - NDB API part 1
and:
MySQL Cluster on Windows - Not so NDB oriented: Set up a dev environment
In this part, the third, I'm going to show some code. Some of it may be very basic if you're a seasoned Windows Win32 developer, but at least you don't have to write the code yourself. What we about to embark on is to write a simple MySQL Clusre NDB MGM API application, the MGM API is the API that is used for managing MySQL Cluster, and doing this from a nice Windows based GUI seems like a good idea, at least to me. I want to show some code that is not only an example, but is also something useful and which serves as an example of NDB API in the Windows environment, but on the other hand is simple enough to be posted in it's entirety and is easy to read and extend.

So what the application presented here will do is to show the status of a running MySQL Cluster in a dialog window. It will be a dialog based application, and if you don't know what that means, let me elaborate a bit:
Usually, a full scale Windows GUI application creates a main Windows that contains the object objects in the application, and which pops up dialogs for configuration, attributes etc. Windows itself knows how to deal with a dialog, where a dialog is a binary resource, built from a textbased definition by a special Resource Compiler and which is managed by a Dialog procedure, written in C or C++.
Now, whereas there are tools to paint and manage the dialog box controls, and Windows know how to deal with these when it comes to repainting them, handle the keyboard, Tab'ing between object in a dialog etc., none of this is automated by Windows when using a normal Winow-based application.
So for a simple application, like the one we look at here, that will just pop up a Windows with some object that shows some status or something, you can create a dialog-based application, where the main Windows is really a dialog. And that is what we are going to do here.
So the main application Window, which is actually a Windows dialog, looks like this:


In the following code examples, just look at the the code, then copy it and paste it into a file using the suggested name. We will look at three files here:
  • NDBMonitor.c - This is the C file that contains the main code of the application. Here is the code that shows and manages the dialog on the application, and other supporting code. Although this is a C file, it must be compiled as C++, as some of the included files that the MGM API depends on (the MGM API is also in itself C-based) in the NDB API, assumes that C++ is used.
  • NDBMonitor.rc - A Resource file that contains the definition of the dialog that is shown by the application. This file is compiled by the resource compiler and is then attached to the finished executable as a binary resource.
  • resource.h - An include-file that defines the macros that names the objects in NDBMonitor.rc, in short, this is the link of names between NDBMonitor.c and NDBMonitor.rc.
So, lets look at NDBMonitor.c first. To use this example, copy this code and paste it into file called NDBMonitor.c in some directory of your choice:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include "resource.h"

HINSTANCE g_hInstance;

BOOL CALLBACK NDBMonitorDlgProc(HWND hDlg, UINT nMsg, WPARAM wParam, LPARAM lParam);
void FillNodeList(HWND hWnd, NdbMgmHandle hMgm);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
g_hInstance = hInstance;

DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL,
(DLGPROC) NDBMonitorDlgProc);

return 0;
} /* End of WinMain(). */


BOOL CALLBACK NDBMonitorDlgProc(HWND hDlg, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
char szBuf[256];
static NdbMgmHandle hMgm = NULL;

switch(nMsg)
{
case WM_INITDIALOG:
ndb_init();
break;

case WM_COMMAND:
switch LOWORD(wParam)
{
case IDOK:
ndb_end(0);
EndDialog(hDlg, IDOK);
break;

case IDC_BUTTON_CONNECT:
if(hMgm != NULL)
{
ndb_mgm_disconnect(hMgm);
hMgm = NULL;
SetDlgItemText(hDlg, IDC_BUTTON_CONNECT, "Connect");
EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_REFRESH), FALSE);

break;
}
GetDlgItemText(hDlg, IDC_EDIT_CONNECTSTRING, szBuf, sizeof(szBuf));
hMgm = ndb_mgm_create_handle();
ndb_mgm_set_connectstring(hMgm, szBuf);
if(ndb_mgm_connect(hMgm, 1, 10, 1) == -1)
{
MessageBox(hDlg, "Error connecting to Cluster", "Cluster error", MB_OK | MB_ICONSTOP);
ndb_mgm_destroy_handle(&hMgm);
hMgm = NULL;
}
else
{
SetDlgItemText(hDlg, IDC_BUTTON_CONNECT, "Disconnect");
EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_REFRESH), TRUE);
FillNodeList(hDlg, hMgm);
}
break;

case IDC_BUTTON_REFRESH:
if(hMgm == NULL)
break;
FillNodeList(hDlg, hMgm);
break;
}
break;

case WM_SYSCOMMAND:
if(wParam == SC_CLOSE)
{
ndb_end(0);
if(hMgm != NULL)
ndb_mgm_disconnect(hMgm);
EndDialog(hDlg, IDOK);
}

default:
break;
}

return 0;
} /* End of NDBMonitorDlgProc(). */

void FillNodeList(HWND hWnd, NdbMgmHandle hMgm)
{
ndb_mgm_cluster_state *pState;
int i;
char *pType;
char *pAddr;
char szBuf[256];

if((pState = ndb_mgm_get_status(hMgm)) == NULL)
{
MessageBox(hWnd, "Error getting Cluster status", "NDB API Error", MB_OK | MB_ICONSTOP);
return;
}

SendDlgItemMessage(hWnd, IDC_LIST_NODES, LB_RESETCONTENT, 0, 0);
for(i = 0; i <>no_of_nodes; i++)
{
if(pState->node_states[i].node_type == NDB_MGM_NODE_TYPE_NDB)
pType = "NDB";
else if(pState->node_states[i].node_type == NDB_MGM_NODE_TYPE_MGM)
pType = "MGM";
else
pType = "API";

pAddr = pState->node_states[i].connect_address;

wsprintf(szBuf, "%s Node: %d %s%s Status %d", pType,
pState->node_states[i].node_id, pAddr == NULL ? "" : "on ",
pAddr == NULL ? "" : pAddr, pState->node_states[i].node_status);

SendDlgItemMessage(hWnd, IDC_LIST_NODES, LB_ADDSTRING, 0, (LPARAM) szBuf);
}

return;
} /* End of FillNodeList(). */


What is contained in this code are three main functions:
  • WinMain() - This is the main function for all Windows programs.
  • NDBMonitorDlgProc() - This is the function that manages the dialog itself.
  • FillNodeList() - This is a helper function that fills in the list in the dialog with the status of the nodes in the Cluster.
The next thing is a definition of the dialog box to be displayed itself. This code, which goes into the file NDBMonitor.rc, is simple and looks like this:
#include "resource.h"
#include "afxres.h"

IDD_DIALOG_MAIN DIALOGEX 0, 0, 310, 194
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION |
WS_SYSMENU
CAPTION "NDB Monitor"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,253,173,50,14
LISTBOX IDC_LIST_NODES,7,23,186,164,LBS_NOINTEGRALHEIGHT |
WS_VSCROLL | WS_TABSTOP
LTEXT "Connect string:",IDC_STATIC,7,7,55,14,SS_CENTERIMAGE
EDITTEXT IDC_EDIT_CONNECTSTRING,63,7,182,14,ES_AUTOHSCROLL
PUSHBUTTON "Connect",IDC_BUTTON_CONNECT,253,7,50,14
PUSHBUTTON "Refresh",IDC_BUTTON_REFRESH,199,173,50,14,WS_DISABLED
END
And finally, we need resource.h, which looks like this:
#define IDC_BUTTON_CONNECT              2
#define IDC_BUTTN_REFRESH 3
#define IDC_BUTTON_REFRESH 3
#define IDD_DIALOG_MAIN 101
#define IDC_LIST_NODES 1001
#define IDC_EDIT_CONNECTSTRING 1002

That's it for now. Next time we'll build this code into a finished application, by linking with the libraries we built ourselves previously.

/Karlsson

No comments: