Beruflich Dokumente
Kultur Dokumente
TUTORIAL
BY Jacques Abada Version 1.0 October 22 2001
1 / 26
Programming MySQL with Visual C++ 6.0 Document entirely written with StarOffice 5.2 under Windows PDF file made with Acrobat 5 Copyright 2001, Jacques Abada Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; A copy of the license is included in the section entitled "GNU Free Documentation licence" in the file licence.pdf. The author remains the only owner of this text and accompanying source code. StarOffice is a registered trademark of Sun Microsystems Acrobat is a registered trademark of Adobe Corp. Windows is a registered trademark of Microsoft Corp. Staroffice is available for free download @: http://www.sun.com/staroffice/ Acrobat is a commercial software. This document is mainly available on my web site (http://www.arcanthea.com). But also on MySQL site: http://www.mysql.com in the contributions section. I wish to thank Paul Dubois for reviewing the english version of this text.
2 / 26
Introduction
The
goal of this tutorial is to explore some of the MySQL API functions through a Windows application written in C++, with Visual C++. I didn't make any particular portability effort, since I used the standard Visual C++ application framework, MFC, in order to handle the user interface controls' logic I wrapped with MySQL functions. Actually, the accompanying sample program shows how to use the MySQL functions with a C++ application framework. Thus, any C++ programmer can easily use the code typical to MySQL in any other framework he is comfortable with. In a program, there is basically the user interface and the useful code. The user interface is dependant, in that it changes from an operating system to the other and from a compiler to the other. The useful code, in the contrary, is common to all of them. A MySQL function like mysql_query() has the same behavior, from the programmer's point of view, whether it is called from a C, C++, Delphi program, under Windows or Linux operating system. The sample program I offer as an illustration to this tutorial, is a dialog based application, which can connect to a MySQL server (either locally or over a network), select a database from this server (provided the user has the rights to), and to display, either the structure or the data within it. Finally, an informations dialog box shows some information like the server and client versions, process ID, etc... This application is to demonstrate how easy it is to write a Windows application able to deal with data residing on a MySQL server residing on a Linux machine. A MySQL connection is transparent, since one has not to bother on how the connection is done at a lower level, the mysql_real_connect() deals with those details for us. The following text describes all the steps involved in the creation of the sample program. The reader must have a basic knowledge of C++, as well as a good understanding of the Visual C++ 6.0 development environment, and of course a good understanding of MySQL and SQL. On the MySQL subject, the reader should read the excellent book of Paul Dubois1. On Visual C++, here are some references which might be useful2. I have no knowledge of any book dealing with programming MySQL with Visual C++. All aspects of database programming with VC++ concern Microsoft proprietary technologies. On the internet, two sites3 in english deal with Visual C++. Also, the MySQL official site4 has many useful resources and links.
1 Paul Dubois: MySQL publi aux ditions New Riders. Edition franaise Campus Press (ISBN: 2-7440-0882-6). Cet ouvrage explique non seulement l'utilisation de MySQL mais prsente la programmation avec divers langages (PHP, C, Perl). 2 Mike Blaszczak: Professional MFC with Visual C++ 6, Wrox Press Collectif: MFC Programming with Visual C++ 6, collection Unleashed chez SAMS Kruglinski & autres: Atelier Visual C++ 6.0 Microsoft Press 3 Http://www.codeproject.com http://www.codeguru.com 4 Http://www.mysql.com
3 / 26
I Required configuration
Visual C++ 6.0 compiler MySQL client libs and include files version 3.23.4x A Windows work station (NT ou 2000) The sample application can access local or distant MySQL databases. Depending on where the databases reside, we'll need: 1. The databases are accessed over the network: In this case we just need the include files and libs. (lib\opt and \include directories in the MySQL installation tree). 2. The database is accessed locally: We'll need to install the server service and the client libs (basically the mysqld-nt service and the libs and include files). Connection can be done to the localhost. The best is to run the client application under Windows and access the databases over the network, preferably on a Linux or UNIX machine.
4 / 26
5 / 26
near the beginning of the file In case you forget to add the Winsock support, you can add it later. Just add the line #include <afxsock.h> under the last #endif in stdafx.h file, like in the next figure.
Mysql.h include a number of other include files (the MySQL include directory contains many but only mysql.h is needed.
6 / 26
AppWizard already has created a dialog box with two push buttons (OK and Cancel). These buttons are mapped to the framework functions CDialog::OnOK() and CDialog::OnCancel(), which call a default handler for when they are clicked. We'll delete those buttons and add the following controls: Control to insert Dialog Box A button labelled Connect A button labelled Disconnect A button labelled Infos A button labelled Quitter A combobox preceeded with a static text labelled Databases A combobox preceeded with a static text labelledTables A listview with Report mode property A radio button labelled Structure View A radio button labelled Data View Ressource ID IDD_MYESSAI_DIALOG IDC_CONNECT IDC_DISCONNECT IDC_INFOS IDOK IDC_DATABASES IDC_TABLES IDC_LV_DB IDC_STRUCTURE_VIEW IDC_DATA_VIEW
7 / 26
Programming MySQL with Visual C++ 6.0 The two radio button are mutually exclusive, i.e., none of them are checked at the same time. To achieve this we have to select both of them and check Auto in the controls's properties dialog box. We can try the desired behavior with the preview mode. Connection dialog box To create this dialog, we have to click with the right mouse button on the Dialog folder of the Resource View area, and choose Insert Dialog. We then resize this dialog to look like the one displayed below:
It will contain the following controls: Type of control Dialog Box An edit control with a static label Host An edit control with a static label User An edit control with a static label Password An edit control with a static label Port The OK button changed to Connect The Cancel button changed to Cancel Ressource ID IDD_CONNECT_DLG IDC_HOST IDC_USER IDC_PASSWORD IDC_PORT IDOK (*) IDCANCEL (*)
(*) These two buttons are automatically created by AppWizard. They have the IDOK and IDCANCEL default name. We shouldn't change those names.
8 / 26
The Cancel button will be deleted and the OK button will be leaved as is.
9 / 26
V Connection code
The goal here is to retrieve the information that the user has typed in the fields of the connection dialog box after he has clicked the Connect button. This button returns IDOK to the calling function. If the user clicks on the Cancel button, then IDCANCEL is returned. When the main dialog displays, the user typically clicks on the Connect button. This calls the CMySample::OnConnect(). This function creates a variable of type CConnexionDlg and, via this variable, displays the connection dialog box, with a call to CDialog::DoModal() function. We then test that IDOK is returned, asin the followinf code:
void CMySampleDlg::OnConnect() { // We pick up the user supplied data from the connection dialog // and we copy them in our member variables. CConnexionDlg dlg; if (dlg.DoModal() == IDOK)
We shall begin with creating a new class for our Connection dialog box. We need a class for this dialog. We also need a couple of member variables to map the various which will hold the user input. We finally need to access our dialog from within our main application dialog. Here are the steps: As soon as the the controls have been inserted into the connection dialog box, we should run ClassWizard. It will detect that the dialog is a new ressource and will offer to create a new class for it.
In a dialog like the one above, we will let the Create a new class option checked and click OK.
10 / 26
Programming MySQL with Visual C++ 6.0 The second dialog box will display to enter the new class name.
In the Name edit control, simply type CConnexionDlg and the File name edit will fill in automatically with a file name based on the new class name. To access this new dialog, we'll need to add the ConnexionDlg.h include file to MySampleDlg.cpp #include "ConnexionDlg.h" Then we'll create four data members to map each of our four edit controls. We run ClassWizard another time and choose the Member Variables folder.
11 / 26
And we'll successively select IDC_HOST, IDC_PASSWORD, IDC_PORT and IDC_USER ressource IDs, and click on the Add Variable button to create 1. 2. 3. 4. A member variable m_Host of type Cstring A member variable m_Password of type Cstring A member variable m_Port of type UINT and a member variable m_User of type CString
The IDC_PORT has to be initialized. Actually MySQL uses port 3306 as default port for the connection. Therefore, we'll change the value of m_Port data member in the contructor of CConnexionDlg class, like below:
CConnexionDlg::CConnexionDlg(CWnd* pParent /*=NULL*/) : CDialog(CConnexionDlg::IDD, pParent) { //{{AFX_DATA_INIT(CConnexionDlg) m_Host = _T(""); m_Password = _T(""); m_Port = 3306; m_User = _T(""); //}}AFX_DATA_INIT }
By default, m_Port is set to 0. We'll just replace this default value with 3306. And this new value will appear in our edit control as the default value. That's all we'll need to do to use our connection dialog. All the retrieval and use of the values typed in by the user are held by functions from our main CMySampleDlg class. 12 / 26
In MySampleDlg.h file, which we'll name myData in the private section of our CMySampleDlg declaration file (header file). To really connect to our server, we'll connect a function to the click message of the Connect button of the main dialog box. With the help of ClassWizard we'll call our function OnConnect(). Instead of writing all of the connection code in this handler function, we'll just display the connection dialog box, retrieve the user supplied input values and pass them to another function which will deal with the real connection. Then when we return from the real connection function, we'll display the databases list available on the server and place their names in a combo box. Have a look at the following code:
13 / 26
When DoModal() function returns, if the user chooses IDOK, the values of the four connection dialog edit controls are copied in the Host, User, Password and Port variables which we created in CMySampleDlg main dialog class (of type CString, CString, CString, UINT). Then, we pass those values to the Connexion() function. This function has the following prototype: BOOL CMySampleDlg::Connexion(CString& Host, CString& User, CString& Password, UINT Port); this function returns TRUE if the connection succeeds, and FALSE if it succeeds not. Returning TRUE makes us call the next function CMySampleDlg::GetDatabases(). This function has the following prototype: BOOL CMySampleDlg::GetDatabases(); and also returns TRUE for success and FALSE for failure. The last three lines of code activate or deactivate some buttons based on the results (enabling the Infos and Disconnect buttons and disabling the Connect button).. Let's have a look at the CMySampleDlg::Connect(CString&, CString&, CString&, UINT) function.
14 / 26
The first line initializes the myData variable through the call to the mysql_init(MYSQL*) function. This function takes a NULL parameter. In case of success it returns a connection handle and NULL in case of failure; we then call the mysql_real_connect() function, passing it the following parameters: 1. 2. 3. 4. 5. 6. 7. 8. adress of myData (connection handle) Value of the HOST field Value of the USER field Value of the PASSWORD field NULL (we don't open a default database) Value of the PORT field NULL (we don't use UNIX sockets) 0 (no flags).
About the flags, an interesting list is available in MySQL documentation at the mysql_real_connect() section. Take the time to read it. mysql_real_connect returns a valid connection handle, or NULL in case of failure. In this case we format an error message with the error number and the error text (obtained with the call to mysql_errno() and mysql_error() ) functions. Let's look at the GetDatabases() function.
15 / 26
We first created a boolean variable named IsConnected. This flag is set to FALSE at the beginning of the program, and is set to TRUE when a valid connection handle is retrieved. We use this flag all along our program to test that we always have a valid connection handle before calling other MySQL functions. We then create a variable res of type MYSQL_RES* and we retrieve the result set with the mysql_list_dbs() function. This function takes two parameters, the first is the connection handle and the second is a search pattern. Here we pass NULL to retrieve all the databases names. Then we fetch each row of the result set with the mysql_fetch_row() function, as many times as there are rows. When no more rows are available in the result set, the function simply returns NULL. During each loop of mysql_fetch_row(), we add the first row (row[0]) to the combo box. m_Databases is a data member of type CComboBox mapped to the combo box labelled Databases. We call the AddString member function to add a new string to the combo box. When we're done, we free the ressources used with a call to mysql_free_result(). At this point, we'll now add the code to display the table list in the second combo box. (mapped to m_Tables data member of type CComboBox). We have to know at first which database to select before showing its tables if any. Up to now, we have created a connection to the server and displayed the databases in a combo box. Now, we'll need to display the tables based on the database a user has chosen. We'll have to connect code to the CBN_SELCHANGE message sent by Windows when the selection in a combo box changes (e.g. when a user selects an item in the combo box). To achieve this, we'll have to retrieve the user selection (which is the name of the selected database), and pass it to the mysql_select_db() function. This function will then change the active database, and call the mysql_list_tables() function to display the tables list in our second combo box (labelled Tables). We miss another function to change the active database. We'll call it ChangeDB(). Its prototype is: BOOL CMySampleDlg::ChangeDB(CString& db_name);
16 / 26
Programming MySQL with Visual C++ 6.0 It take just one parameter, which is the name of the new database to use. The function returns TRUE if it succeeds. If it fails it returns an error message showing the error number and the error text. Here it is:
BOOL CMySampleDlg::ChangeDB(CString& db_name) { if (!IsConnected) return FALSE; int Err = mysql_select_db(myData, db_name); if (Err == 0) return TRUE; else { CString msg; msg.Format("L'accs la base %s a chou cause de l'erreur\nn: %d\n" "Texte de l'erreur : %s", db_name, mysql_errno(myData), mysql_error(myData)); MessageBox(msg, "Erreur", MB_ICONERROR); return FALSE; } }
17 / 26
Programming MySQL with Visual C++ 6.0 Now we have changed the active database, we can retrieve and display the name of the tables it contains. The mysql_list_tables() function is quite similar to mysql_list_dbs(), the first parameter is a valid connection handle, and the second parameter is a search pattern, here we just pass NULL to retrieve all the tables.
void CMySampleDlg::OnSelchangeDatabases() { if (!IsConnected) return; CString currDB; int nIndex = m_Databases.GetCurSel(); m_Databases.GetLBText(nIndex, currDB); if (!ChangeDB(currDB)) return; m_Tables.ResetContent(); MYSQL_RES *res; res = mysql_list_tables(myData, NULL); MYSQL_ROW row; while ( ( row = mysql_fetch_row(res))) { m_Tables.AddString(row[0]); } mysql_free_result(res); }
This code functions like the one used to retrieve the databases names. We use a variable of type MYSQL_RES* and we step through the result set returned by the mysql_list_tables() function. In our while loop, that is, as many lines as there are to fetch, we retrieve the first column and insert its contents to the combo box. At the end, we call mysql_free_result() to free the ressources.
18 / 26
We'll create a data member for each of the two radio buttons in order to access their properties. We first create a data member called m_bDataView of type CButton we'll map to the Structure View radio, and a data member called m_bStructureView also of type CButton, mapped to the Data View radio. In order to test whether one or the other is checked, we'll use the CButton::GetCheck() function, which will return an integer indicating the button state. A value of 1 indicates the control is checked. All occurs in the code of the CBN_SELCHANGE message handler for the combo box IDC_TABLES. We begin with retrieving the text selected by the user in the Databases combo box. We then format a query string containing this text. At this point, we test which radio button is checked. If Structure View is checked, then we format a query like describe nom_table ; if Data View is checked, then we format a query like select * from nom_table .
// Get the select text in the combo box int nIndex = m_Tables.GetCurSel(); m_Tables.GetLBText(nIndex, currTable); // Check whether Structure View or Data View is checked // and format the query accordingly if (m_bDataView.GetCheck() == 1) query.Format("SELECT * FROM %s", currTable); else if (m_bStructureView.GetCheck() == 1) query.Format("DESCRIBE %s", currTable);
We then run the query with the mysql_query() functions with the correct query string.
if (( mysql_query(myData, query) == 0)) { res = mysql_store_result(myData); num_fields = mysql_num_fields(res); fd = mysql_fetch_fields(res);
The mysql_query() function returns 0 in case of success. After the function returns, we call the mysql_store_result() function to retrieve the result set in a res variable of type MYSQL_RES*. We then retrieve the number of fields returned by the mysql_num_fields() function. This number is needed because we'll have to build the ListView headers based on the number of fields of a particular table. We do not know the nomber of columns a table contains in advance. And therefore, we'll need to check how many columns the query returns, and then create a function we'll call BuildListView() to display the correct 19 / 26
Programming MySQL with Visual C++ 6.0 number of ListView control headers and display the field name in each of them. This function is the following:
BOOL CMySampleDlg::BuildListView(UINT num_Cols, MYSQL_FIELD *fd) { UINT i; LVCOLUMN m_pCol; for (i = 0; i < num_Cols; i++) { m_pCol.pszText = fd[i].name; m_lvDbTable.InsertColumn(i, m_pCol.pszText, LVCFMT_LEFT, 100); } return TRUE; }
We mapped our listview to a data member named m_lvDbTable of type CListCtrl. The first parameter of the BuildListView() is the number of fields returned by the mysql_nul_fields() function. The second parameter is a variable of type MYSQL_FIELD*. This function builds as many headers as there are columns in the result set and displays the column name in the header section of the control. It returns TRUE to tell the caller that it is done with success. We'll now displays some nice lines in our tabular view. This is done with setting the extended style of our ListView control:
// Build the listview headers based on the number of columns returned if (!BuildListView(num_fields, fd)) return; // Set some nice grid lines and effect to the listview m_lvDbTable.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_GRIDLINES | LVS_EX_INFOTIP);
Now we're done with the building of our ListView headers and nice look and feel. We'll now fill in the control with data. We do this with the following code:
20 / 26
CString msg; msg.Format("Error %d in query %s\n%s", mysql_errno(myData), query, mysql_error(myData)); MessageBox(msg, "Error in Query", MB_ICONERROR); } // Now free the resources mysql_free_result(res);
As usual, we iterate through a while loop and call the mysql_fetch_row() function which retrieves the result set line by line. We then cycle through the fields to fill in the ListView one cell at the time. If something goes wrong, we format a message and display it. We then free the resources. The code we explained above is displayed completely below. The next step is about the informations dialog box. This box is far less complicated than the code we saw up to now. It shows some other useful MySQL functions.
21 / 26
// Get all the rows in the result set while ( (row = mysql_fetch_row(res))) { for (j = 0; j < num_fields; j++) { // Fill in the listview items and subitems lvItem.mask = LVIF_TEXT; lvItem.iItem = i; lvItem.iSubItem = j; lvItem.pszText = row[j]; m_lvDbTable.InsertItem(&lvItem); m_lvDbTable.SetItem(&lvItem); } i++; // Next Item
22 / 26
23 / 26
The code snipet above shows the use of the mysql_get_client_info(), mysql_get_host_info(), mysql_get_server_info(), mysql_get_thread_id(), and mysql_get_proto_info() functions. These functions respectively return the version of the client library, the connection type, the server version, the process ID and an integer identifying the protocol used by the connection. A good place to read more about the last function can be found in MySQL function.
24 / 26
The rest of the code reminds the mysql_list_processes(), which corresponds to the SHOW PROCESSLIST query. We'd get the same result in formatting the query and pass it to the mysql_query() function. The mysql_list_processes() function returns a result set, we'll have to decode before displaying it. The code for destroying our dialog is in the OnOK() function which is the handler for the OK button.
void CMyInfos::OnOK() { this->DestroyWindow(); }
25 / 26
Conclusion
We're done! Our sample application is finished. To run it, all we need is the executable file and the libmysql.dll file in the same directory. Nothing else. This small program is an introduction to what can be done with the MySQL API and a programming langage. C++ is not the only langage capable of using MySQL API. The most famous is undoubtedly PHP. PHP is used to make dynamic web sites. Under Windows, there exist many free tools (on the MySQL web site http://www.mysql.com): 1. 2. 3. 4. MyODBC: to work with databases via ODBC (Open Database Connectivity) MyOleDB: OleDB provider for MySQL, to be used with tools using the Visual Basic lingo. TMySQL: A Delphi component. Zeos: a series of Delphi and Kylix open source components.
I hope this tutorial will bring you a lot. Feel free to contact me if you find errors, or if you have any questions. Happy MySQL ! Jacques Abada
26 / 26