Better Understanding of
Computer Programming
Saikat Basak
© Saikat Basak. All right reserved.
First Online Edition: March 2001
Second Online Edition: December 2001 Third Online Edition: June 2004
Electronic version of this book is available at www.enselsoftware.com
Downloading the PDF/DOC version of this book is allowed. But any modification of the book is a violation of applicable copyright law.
Disclaimer
The author had made every effort in the preparation of this book to ensure the accuracy of the information. However, the information contained in this book is provided without warranty, either express or implied. The author will not be held liable for any damage caused or alleged to be caused either directly or indirectly by this book.
For the latest version of this book, please see at the web site.
Contents
1. INTRODUCTION ... 4
2. HOW TO RETURN A SINGLE VALUE FROM A FUNCTION?... 6
3. HOW TO RETURN MULTIPLE VALUES FROM A FUNCTION? ... 8
4. RECURSIVE FUNCTION ... 14 5. WRITING DLL IN VB.NET ... 17 6. WRITING DLL IN C/C++... 30 7. FILE HANDLING ... 46 8. POINTER PARADOXES IN C ... 55 9. STRING HANDLING IN C++ ... 64 10. FUNCTION OVERLOADING ... 66
11. OBJECT ORIENTED PROGRAMMING SYSTEM (OOPS) ... 68
12. THE WORLD OF DISTRIBUTED COMPUTING – COM AND CORBA... 81
13. DATABASE CONNECTIVITY ... 92
14. PROGRAM ARCHITECTURE – LAYERED APPROACH... 109
15. WHAT LANGUAGE AND CLIENT/SERVER YOU SHOULD USE? ... 118
16. SOFTWARE ENGINEERING... 125
17. EXPLORING EXTREME PROGRAMMING CONCEPTS ... 129
18. PROGRAMMING TIPS ... 131
19. PROGRAMMING IN SPREADSHEET ... 136
1. Introduction
What’s special about this book? Well, a lot! I designed the book to be read online. You can download this book FREE of cost and read at your computer anytime! You can directly copy the source codes from here and run them in your favorite compiler!
This book is for a little bit experienced programmers. Here I’ve discussed and tried to clarify some difficulties faced by programmers. I didn’t teach any programming language from scratch here. I assume that you know at least any one of C/C++, Java or Visual Basic or any other standard programming language. Although a large part of this book deals with VB/.NET and C++, other language programmers will also find the information useful. Wherever possible, I tried to make the discussion in language independent way. However, to give examples one must use some languages. So I mainly used VB and C/++. I also made a comparison of object-oriented concept among various languages.
Object oriented programming is a hot topic nowadays and very few books discuss them from practical viewpoint. (Such books are available in C++ and Java but they are rare for Visual Basic) Here I presented same object oriented program source code in different languages and discussed how they do differ.
I tried to explain everything in a way as much easily as possible. However, in many cases, I assumed your prior knowledge of some concept. If you need to know the basic of any language, please consult any book dedicated to that particular language.
All real world programs are huge and they can’t be discussed in a single book. However, I delineated how to write elegant, maintainable code. Whatever be your platform or language, you need to follow certain conventions.
While reading the book, I expect active participation from your part. Reading like a storybook won’t help. You must understand each concept properly. If you face any problem, please feel free to contact me. I’m just an email away! My email is [email protected] and my web site is located at www.enselsoftware.com.
You might notice that the chapters in the book have been arranged in a little bit haphazard manner! Well, I deliberately did so in order to make you not feeling bored!
You must not forget that nothing is more important than a good algorithm. Your program’s performance mainly depends on the algorithm you adopted rather than on the language you are using for development!
Since this book discusses many topics at once, I often advised you to read some other books on those particular topics so that you get a better grasp of the concepts.
2. How to return a single value from a function?
Functions are one of the most frequently used components in any programming language. Typically a function takes some arguments, performs some calculation and then returns the result to the calling function. However, there are differences between C and VB on how the functions return values. In C, by default, a function always returns a single value (or no value at all i.e. void). But in VB, a function must return a single value. In C, we may force a function returning multiple values by using pointers. But in VB, we shall have to use Sub Procedures to return multiple values. The Sub Procedure can return no value, single value or multiple values. So, in VB, function is a special kind of sub procedure, which returns single value. In C, function always passes argument by value. To make it pass argument by reference, we need to pass the addresses of its arguments, (which is known as pointer). But in VB6, function (and procedures as well) always pass values by reference (i.e. address) unless specifically told to pass by value. However, in VB.NET, it is passed by value by default as in C. In VB.NET, you must specify ByRef explicitly to pass by reference.
First we shall see how to return single value from a function in C and then in VB. Study the following simple C code.
Code 2-1
/* program to find the area of a rectangle */ #include<stdio.h>
int find_area(int l,int w); /* prototype optional in C */ void main(void)
{
int length, width,area; puts("Enter length"); scanf("%d",&length); puts("Enter width"); scanf("%d",&width); area = find_area(length,width); printf("Area is %d\n",area); }
/* the prototype of function is
return_type function_name (type argument_1,…, type argument_n) */
{
int a; a = l * w; return a; }
The same function is written in Visual BASIC.NET.
Code 2-2
Public Function find_area(length, width) as Integer Return length * width
End Function
Assume name of the command button is Button1.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
l = InputBox("Enter length") w = InputBox("Enter width") area = find_area(l, w)
MsgBox ("Area = " & area) End Sub
Universal approach to problem solving –
Step 1: Define the problem to be solved.
Step 2: Decompose the problem into more easily solved sub-problems. Step 3: Repeat step 2 until the sub-problems can be solved directly. Step 4: Integrate the solutions to solve larger problem.
3. How to return multiple values from a function?
Remember solution of a quadratic equation? It may have two roots (equal or unequal) or no real root. We shall return two roots of any typical quadratic equation. Study the following C code first.
Code 3-1
/* a quadratic equation solver in C */
/* demonstrates returning of multiple values from functions using pointers */
#include <stdio.h> #include <math.h>
int solve_quad(int p, int q, int r, float *q1, float *q2); void main(void)
{
int a,b,c;
float root1,root2; int status;
puts("Enter a,b,c for ax²+bx+c=0"); scanf("%d,%d,%d",&a,&b,&c);
status = solve_quad(a,b,c,&root1,&root2); if(status==1)
{
puts("The solution is");
printf("%.2f, %.2f\n",root1,root2);
printf("Stored at addresses: %x and %x\n", &root1, &root2);
} else
puts("No real root"); }
int solve_quad(int p, int q, int r, float *q1, float *q2) {
float determ; determ=q*q-4*p*r;
if(determ<0) /* no real root */ {
*q1=0; *q2=0;
return 0; }
else /* real root exists */ { *q1=((-q)+sqrt(determ))/(2*p); *q2=((-q)-sqrt(determ))/(2*p); return 1; } }
As a recapitulation, the basic idea of pointer is as follows: float *q ‘points’ to the float value stored at an address ‘q’. Thus the ‘*’ means ‘contents stored at’.
The same program is written in Visual BASIC as shown in code 3-2. The program has a form and a command button. See the similarities and differences between a C and VB program.
Code 3-2
Option Explicit
Public Sub solve_quadratic(ByVal a As Integer, ByVal b As Integer, ByVal c As Integer, ByRef root1 As Single, ByRef root2 As Single)
Dim determ As Integer
determ = b ^ 2 - 4 * a * c If determ < 0 Then
MsgBox ("No real root") ElseIf determ >= 0 Then
root1 = (-b + Sqr(determ)) / (2 * a) root2 = (-b - Sqr(determ)) / (2 * a) End If
End Sub
Assume name of the command button is Command1
Dim p As Integer, q As Integer, r As Integer Dim s1 As Single, s2 As Single
p = InputBox("Enter a for equation ax²+bx+c=0") q = InputBox("Enter b for equation ax²+bx+c=0") r = InputBox("Enter c for equation ax²+bx+c=0")
Call solve_quadratic(ByVal p, ByVal q, ByVal r, s1, s2) MsgBox ("Roots are " & s1 & " and " & s2)
End Sub
For a sample run, use following values: a = 1, b = -5, c = 6. The roots are 3 and 2.
In VB, the arguments of functions or procedures are always passed by reference by default (up to version 6) and by reference from version 7 (i.e. VB.NET). If you want them to be passed by value, then you must use the keyword ‘ByVal’ in front of each argument that you want to be passed by ‘value’. But in VB.NET, arguments are passed by value by default! Another thing, in VB, the keyword ‘Single’ is equivalent to ‘float’ in C/C++! Don’t forget this! Anyway, it is indeed a good practice in VB to always explicitly write either ByVal or ByRef in function or procedure declaration.
In VB, a Function always returns a single value. Whereas, Sub procedures can return zero, one or more values.
Now we shall see how to write the same program in Java! I am showing here an entire applet code. However, the main part of the multiple value returning is shown blue.
Code 3-3
// a quadratic equation solver import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.applet.*;
/* <applet code= "QuadSolverApp" width=160 height=250> </applet>
*/
public class QuadSolverApp extends JApplet implements ActionListener { JTextField jtfa,jtfb,jtfc,jtfr1,jtfr2; JLabel jlt,jla,jlb,jlc,jlr1,jlr2; JButton jb;
public void init() {
resize(160,250);
Container contentPane = getContentPane(); contentPane.setLayout(new FlowLayout());
// add text fields & lables
jlt = new JLabel("Quadratic Equation Solver"); contentPane.add(jlt);
jla = new JLabel("Enter a"); contentPane.add(jla);
jtfa = new JTextField(8); contentPane.add(jtfa); jlb = new JLabel("Enter b"); contentPane.add(jlb); jtfb = new JTextField(8); contentPane.add(jtfb); jlc = new JLabel("Enter c"); contentPane.add(jlc); jtfc = new JTextField(8); contentPane.add(jtfc); jlr1 = new JLabel("Root 1"); contentPane.add(jlr1); jtfr1 = new JTextField(8); contentPane.add(jtfr1); jlr2 = new JLabel("Root 2"); contentPane.add(jlr2); jtfr2 = new JTextField(8); contentPane.add(jtfr2); // add button
JButton jb = new JButton("Solve"); // button label jb.setActionCommand("SolveEquation"); // button action command
jb.addActionListener(this); contentPane.add(jb);
}
public void actionPerformed(ActionEvent ae) {
if(ae.getActionCommand()=="SolveEquation") // button action command
{
// create a new instance of SolveClass
SolveClass sc = new SolveClass(); SolveClass sc2;
// call Solve method of SolveClass sc2 =
sc.Solve(Float.parseFloat(jtfa.getText()),Float.parseFloat(jtf b.getText()),Float.parseFloat(jtfc.getText()));
if(sc2.root1==0) {
jtfr1.setText("No real root"); jtfr2.setText("No real root"); }
else {
jtfr2.setText(String.valueOf(sc2.root2)); } } } } class SolveClass {
static double root1,root2;
SolveClass Solve(double a, double b, double c) {
double determinant;
SolveClass scf = new SolveClass();
determinant = b*b-4*a*c;
if(determinant > 0) {
System.out.println("Two real roots");
scf.root1 = (-b + Math.sqrt(determinant))/(2*a); scf.root2 = (-b - Math.sqrt(determinant))/(2*a); } else { scf.root1 = 0; scf.root2 = 0;
System.out.println("No real root"); }
return scf;
} // end of method Solve } // end of class SolveClass
In Java , basic data types (like integer, float etc.) are always passed by value. Only objects can be passed by reference in Java. Observe that two achieve this task, we had to create two similar object instances inside main. Also, Java doesn’t support pointers. Pretty peculiar, ha!
What is the difference between a function and a procedure? Consider the following C program.
int func (int x) { return x*x; }
It simply returns the square of an integer. Now see the same code written differently.
void func (int x, int *y) { *y = x*x; }
// value of integer stored at y will be accessible even // outside function because we pass it by reference
Did you now realize the difference? Both functions perform essentially the same thing. In second function, instead of explicitly returning a value, we are passing the same by reference (i.e. why we use pointers!). That means, in C, though we have only functions, using pointers we can perform the same task as those of procedures in VB or Oracle.
Since VB, Java, Oracle etc. don't have pointers, they use 'function' and 'procedure' both to perform the essentially same task as I’ve shown above. Internally whenever you use 'procedure' in Java, VB or Oracle, all parameters are passed/returned by reference by default. But in VB you do have the option to control whether you want it to be passed by value or by reference.
We’ll see another version of code 3-1 in chapter 24 when we discuss pointers in detail.
4. Recursive function
A recursive function is really a wonderful thing! If you know how to use it, you can save lots of lines of code. Following is a VB code to find out the sum of series 1 + 2 + 3 + … + N.
Code 4-1
Option Explicit
Public Function Sum(i As Integer) As Integer If i = 1 Then Sum = 1 Else Sum = i + Sum(i - 1) EndIf End Function
Assume a command button as Command1 in the form.
Dim n, Total As Integer
n = InputBox("Enter N for 1 + 2 + ... + N") Total = Sum(n)
MsgBox ("Sum is " & Total)
Please don’t input –ve value! If you input very large N (say 10000), you might get “Out of Stack space” error. How can you increase stack space?
Most books close the chapter on recursion by just giving you factorial program. There are several worthwhile applications of recursions in real life problems. Here I am going to show you how you can recursively traverse a tree using recursion.
A tree is a non-linear data structure (ouch)! Arrays are linear data structures. You can travel along an array using a straight line; obviously you can’t do that in case of trees!
Consider the tree shown in figure 4-1. We need to visit every node of the tree once and fetch the information from that node. Code 4-2 describes the algorithm for doing that.
Figure 4-1 Code 4-2
Function TraverseNode (StartNode as Node) If StartNode has Children Then
For each Child
Get node information
Call TraverseNode (Child) Next
Else
Exit Function End If
The above algorithm makes a pre-order traversal of the tree – in the following sequence – 1, 2, 5, 6, 3, 7, 8, 9, 4, 10, 11, 12.
To learn more about Trees and traversal methods, please see a Data Structure textbook.
Code 4-3 shows the VB.NET code for above algorithm. To test it – insert a tree view control in a sample VB.NET project. Add one command button as well. Write code to add some elements in the tree view (that code is not shown here). Only the code for tree traversal is shown below.
Code 4-3
Private Sub TraverseTree(ByVal n As TreeNode) MessageBox.Show(n.Text)
Dim aNode As TreeNode For Each aNode In n.Nodes TraverseTree(aNode) 2 1 3 4 5 6 7 8 9 10 11 12
Next End Sub
To check the code, you can call it (say inside a button) as
TraverseTree(TreeViewName.SelectedNode)
The code adds some sample nodes. You can add some more of your own. The
TraverseTree procedure displays information about every node. Instead of msgbox, you can use Print #1, to write the node information in a file so that
you can re-load the entire tree by just reading the file. The same procedure, with little modification, can be used to search for an item in the tree.
Do you now realize the power of recursion?
Exercise 4-1
Write a program for permutation using recursion. For example, if you give input as “abc” it will show all possible permutation of the word i.e. abc, acb, bac, bca, cba, cab.
5. Writing DLL in VB.NET What is DLL?
DLL stands for Dynamic Link Library. This is a feature of Windows family of operating systems and OS/2 that allows executable routines to be stored separately as files with DLL extensions and to be loaded only when needed by a program.
A dynamic-link library has several advantages. First, it does not consume any memory until it is used. Second, because a dynamic-link library is a separate file, a programmer can make corrections or improvements to only that module without affecting the operation of the calling program or any other dynamic-link library. Finally, a programmer can use the same dynamic-link library with other programs.
Let’s examine the topic in more detail. You’re familiar with writing #include<stdio.h> in your C programs. Aren’t you? In a similar way, you can define some of your functions, which you often use in many programs, in your own header file. (Note: I am saying header file for brevity only. It can be any file for example BAS module file in VB etc.)
Suppose I often need to solve quadratic equation. So I decided to make it a library function. For this purpose, I thought of including the following code in my own header file “MyFile.h”.
Code 5-1
int solve_quad(int p, int q, int r, float *q1, float *q2) { float determ; determ=q*q-4*p*r; *q1=((-q)+sqrt(determ))/(2*p); *q2=((-q)-sqrt(determ))/(2*p); return 1; }
Now I wrote ten applications where I used this solve_quad function by just stating #include “MyFile.h” and then calling the function as status = solve_quad(a,b,c,&root1,&root2). Suddenly I realized that if the determinant is less than zero, then my programs were going to crash!
Code 5-2
int solve_quad(int p, int q, int r, float *q1, float *q2) {
float determ;
determ=q*q-4*p*r;
if(determ<0) /* no real root */ {
*q1=0; *q2=0;
return 0;
}
else /* real root exists */ { *q1=((-q)+sqrt(determ))/(2*p); *q2=((-q)-sqrt(determ))/(2*p); return 1; } }
However, the trouble starts now! To update all those applications compiled with code 5-1 by Code 5-2, I need to re-compile each of them! So I have to re-build all the executables that were using code 5-1. Obviously this is a big problem!
Now suppose I wrote the code 5-1 in a DLL named “MyFild.dll”. Then while compiling my applications those use code 5-1, I told them that I was going to supply the solve_quad function dynamically from MyFile.dll. After this as usual I discovered my folly and replaced code 5-1 with code 5-2 in MyFile.dll. However, at this time, I don’t need to rebuild all the executables again. All I have to do is to update the old MyFile.dll (that were using code 5-1) with new MyFile.dll (which has been compiled with code 5-2)! Doesn’t it seem nice, really?
You might ask what would have happened if I changed the function look like
double solve_quad(int p, int q, int r, float *q1, float *q2) in
code 5-2? Well, in that case DLL trick wouldn’t have worked! DLLs require once you told your client applications that you are going to provide a particular ‘interface’ (for this point of time you can assume that interface is somewhat similar to function prototype), you can’t change that. If you do so, you need to re-compile all your applications to with new version of your DLL. So interface works like a binding ‘contract’ between your DLL and client applications. As long as you change the bodies of your function/procedure without changing the interface, DLL works fine.
Now I think you have understood the usefulness of DLLs. When we discuss COM later, you will realize that DLLs are meant for more efficient code re-use along with many other benefits such as efficient memory usage. Why? Because if several applications use the same DLL, only one copy of that DLL will be loaded into memory. When the operating system sees that the DLL is no longer being used by any application, it will be removed from memory. Also, use of DLLs results in small executable file size compared with statically linked file.
In Windows, all of an application’s code, data, resources and dynamic memory reside within an application’s process. Moreover it is not possible for an application to address data residing outside its own process. Because of this when a DLL is loaded it must somehow reside in the process of the application that loaded the DLL; if more than one application loads the same DLL, it must reside in each application’s process.
Through memory mapping, Windows is able to load the DLL once into global heap and then map the address range of the DLL into address space of each application that loads it. This is shown in figure 5-1.
Figure 5-1
In this chapter we’ll see how to make a DLL in VB and C/++. Global Heap Application 1 Application 2 DLL Data 1 Data 2 Application code DLL Code DLL Data Application code DLL Code DLL Data Process 1 Process 2
To create a very simple DLL in VB.NET, follow the steps as described below.
1. Select File – New Project – Class Library 2. Specify the name of class as ‘MyClass’
3. Write the following code, change file name as MyClass.vb and compile the project. The output file will be MyClass.dll.
Code 5-3
Public Class SimpleClass Public MyName As String
Public Function Sum(ByVal a As Integer, ByVal b As Integer) As Integer
Return a + b End Function End Class
4. Open a new Windows Application project say MyClient.
5. Select Project – Add Reference … – browse MyClass.dll file and select it. 6. Add a button to the form and in its click event write following code.
Code 5-4
Dim m As New [MyClass].SimpleClass() MsgBox(m.Sum(5, 6))
7. Now run the MyClient project. Click on command button. You should see the summation result.
8. Congratulation! You’ve made your first DLL.
GOLDEN RULE OF
PROGRAMMING
THINK FIRST,
CODE LATER.
So far we have learnt how to write DLLs in VB. The main advantage of DLL is that, if more than one program requires that DLL, only one copy of the DLL will be loaded into memory. Also, DLL gives you reusable code. You can just plug-in pre-built DLLs into your project without knowing their internal code. So, it’s a good practice to keep your often-used functions/procedures in a DLL file. The concept of DLL is common to nearly all languages. In VB and C++, it is known as DLL. In Java and Oracle it is known as ‘Package’ (these are not exactly DLLs but the concept is similar).
Consider the matrix operations for example. It’s used in most engineering calculations but matrix is absent in most languages. Instead of writing matrix-handling program every time, you can store them in a DLL. I’m going to tell you how to do the same in VB.NET.
Create a class library. Add the following code for calculating product of two matrices. Assume class name is CMath.
Code 5-5
Public Function Product(ByRef FirstMatrix(,) As Double, ByRef SecondMatrix(,) As Double, ByRef ProductMatrix(,) As Double) As Integer
'Product of two matrices 'returns 0 if successful Dim s As Double
Dim i, j, k, L As Integer
Dim RowsInFirstMatrix, ColsInFirstMatrix, ColsInSecondMatrix As Integer RowsInFirstMatrix = UBound(FirstMatrix, 1) - 1 ColsInFirstMatrix = UBound(FirstMatrix, 2) - 1 ColsInSecondMatrix = UBound(SecondMatrix, 2) - 1 ReDim ProductMatrix(RowsInFirstMatrix + 1, ColsInSecondMatrix + 1) Try For i = 0 To RowsInFirstMatrix For j = 0 To ColsInSecondMatrix s = 0 For k = 0 To ColsInFirstMatrix s = s + FirstMatrix(i, k) * SecondMatrix(k, j) Next ProductMatrix(i, j) = s Next Next
Catch x As System.Exception MessageBox.Show(x.ToString) Return 1 End Try Return 0 End Function
Now compile it to a DLL file. I’m showing here only the code for matrix product. You should write all matrix operations such as summation, scalar product, inversion (real tough) etc. I already wrote those functions in a DLL. If you like to have the source code, don’t forget to send me an email! ☺
In a new project, add the reference to this newly created DLL file. Make an instance of your matrix class and then call the matrix methods as necessary. Now to test the product matrix, you may add the following code in form command button.
Dim a(2, 2), b(2, 2), d(2, 2) As Double Dim m, n As Integer a(0, 0) = 1 a(0, 1) = 2 a(1, 0) = 3 a(1, 1) = 4 b(0, 0) = 5 b(0, 1) = 6 b(1, 0) = 7 b(1, 1) = 8
Dim myMath As New CMaths()
Dim x = myMath.Product(a, b, d) MsgBox("product")
If x <> 0 Then MsgBox("Error") : Exit Sub For m = 0 To 1
For n = 0 To 1
MsgBox("(" & m & "," & n & ")= " & d(m, n)) Next
Next
This program also shows how to return an array from a procedure or function. We took advantage of same concept as discussed in chapter 3 for returning an array.
In C, to return an array you need to use pointers. I’m showing here how to write a 2D-matrix multiplication program in C++.
/* matrix multiplication */
/* example of returning array from a function using pointer */
#include <iostream.h> #include <stdlib.h>
int **ProductMatrix(int ** ,int **,int,int,int); void main(void)
{
int **p,**matrix1,**matrix2;
int i,j;
int row1,row2,col1,col2;
cout << "Rows Cols for matrix1 eg. 2 2" << endl; cin >> row1 >> col1 ;
cout << "Rows Cols for matrix2 eg. 2 2" << endl; cin >> row2 >> col2;
if(col1 != row2) {
cout << "Multiplication impossible" << endl; exit(1);
}
// create rows dynamically matrix1 = new int * [row1]; matrix2 = new int * [row2]; // creates columns dynamically for(i=0;i<row1;i++)
matrix1[i] = new int [col1]; for(i=0;i<row2;i++)
matrix2[i] = new int [col2];
cout << "Enter the first matrix" << endl; for(i=0;i<row1;i++) { for(j=0;j<col1;j++) { cin >> matrix1[i][j]; } }
cout << "Enter the 2nd matrix" << endl; for(i=0;i<row2;i++)
{
for(j=0;j<col2;j++)
cin >> matrix2[i][j];
}
}
p = ProductMatrix(matrix1,matrix2,row1,col1,col2); // print product matrix inside main
cout << "inside main\n" << endl; for(i=0;i<row1;i++) { for(j=0;j<col2;j++) { cout << p[i][j] << "\t"; } cout << endl; } // free memory for(i=0;i<row1;i++) delete []matrix1[i]; delete []matrix1; for(i=0;i<row2;i++) delete []matrix2[i]; delete []matrix2; for(i=0;i<row1;i++) delete []p[i]; delete []p; } // end of main
int **ProductMatrix(int **m1 ,int **m2,int r1,int c1,int c2) { int i,j,k; int s; int row1,col1,col2; int **matrixp; row1=r1; col2=c2; col1=c1;
matrixp = new int * [row1]; for(i=0;i<row1;i++)
matrixp[i] = new int [col2]; for(i=0;i<row1;i++)
{
{ s = 0; for(k=0;k<col1;k++) s = s + m1[i][k]*m2[k][j]; matrixp[i][j]=s; } }
cout << endl << "Inside function The product matrix is" << endl; for(i=0;i<row1;i++) { for(j=0;j<col2;j++) cout << matrixp[i][j] << "\t"; cout << endl; } return(matrixp); } /* end of ProductMatrix */
The code indeed seems little bit confusing! Isn’t it? For a ready reference, you may use the following example.
| 1 2 | . | 5 6 | = | 19 22 | | 3 4 | | 7 8 | | 43 50 |
In VB, the entire product matrix was passed by reference. But in C, you’ll have to return the starting address of the first element of the array i.e.
matrixp[0][0]. Since it was two-dimensional array, you had to use **. In case
of one-dimensional array, you’ll use only *.
I understand that the above C++ code may seem little bit terse. So it needs some explanation on pointers. Observe that here we are allocating memory dynamically by new operator. In case of C we had to use malloc function.
Strictly speaking, there is no multi-dimensional array in C! What we see here is an array of 1-dimensional arrays! In the line p = new int * [rows]; (note
that p is defined as int ** p;) we are creating an array of rows rows where
each of p[0] to p[rows-1] holds an address. The next line inside for loop, p[i] = new int [cols]; creates the rooms for the columns.
The thing will be clearer from the figure 14-1. Take an example of a 4x4-matrix p.
The element (3,2) of the matrix may be obtained either by p[3][2] or *(p[3]+2) or *(*(p+3)+2). So when the function returns matrixp, we
In actual practice, memory is a block of continuous addresses like a list row. 0 1 2 3 p[0] p[1] p[2] p[3] Figure 5-2
Now I’m giving another useful program in VB.NET. It is solution of simultaneous linear equations by Gauss elimination technique. As I already stated, it will be better if you save the procedure in a class file and later compile it into a DLL. Here’s the main code for solving equations.
Code 5-7
Public Function EquationSolve(ByVal A(,) As Double, ByRef x() As Double) As Integer
'This procedure solves a set of linear simultaneous 'equations by Gauss Elimination method
'Usual notation is [A]{X}={B}
'here we combine {-B} into matrix [A] as the last column 'solution column matrix is X
'so for N equations, [A] will have N rows & N+1 columns 'first element of matrix is A(0,0)
'[A] is called Augmented Matrix
'Status returns whether solution is successful or not 'returns 0 if successful, non zero if not
Dim n, c, i, j, k As Integer Dim AA As Double
n = UBound(A, 1) 'rows in augmented matrix ReDim x(n) Try c = A(0, 0) For k = 0 To n - 2 For i = k + 1 To n - 1 Indicates address of this place * ( p [3] + 2 ) indicates value stored at this place
For j = k + 1 To n If c = 0 Then
'No unique solution exists Return 2
End If
A(i, j) = A(i, j) - A(i, k) * A(k, j) / c
System.Windows.Forms.Application.DoEvents()
'System.Diagnostics.Debug.WriteLine("i=" & i & " j=" & j & " k=" & k & " A(" & i & "," & j & ")=" & A(i, j))
Next j Next i c = A(k + 1, k + 1) Next k For i = n - 1 To 0 Step -1 AA = A(i, n) If i = n - 1 Then j = n - 1 : GoTo 100 For j = n - 1 To i + 1 Step -1 AA = AA + x(j) * A(i, j) Next j
100: x(i) = -AA / A(i, j) Next i
Catch z As System.Exception Return 1
End Try
Return 0 'solution successful End Function
To call the procedure, you may add the following code against a command button. Here I’ve solved the following set of equations.
x + y + z – 6 = 0, 2x + 3y + z – 11 = 0 and x – 2y – z – 6 = 0 Dim B(3, 4) As Double Dim Z(3) As Double Dim W, I As Integer B(0, 0) = 1 B(0, 1) = 1 B(0, 2) = 1 B(0, 3) = -6 B(1, 0) = 2 B(1, 1) = 3 B(1, 2) = 1 B(1, 3) = -11 B(2, 0) = 1 B(2, 1) = -2
B(2, 3) = -1 B(2, 3) = 6
Dim myMath As New CMaths() w = myMath.EquationSolve(b, z) TextBox1.Clear()
If w = 0 Then
For i = 0 To UBound(z, 1) - 1
TextBox1.Text = TextBox1.Text & "X(" & i & ")=" & z(i) & " ¦ "
Next Else
MsgBox("Error in solution, return value = " & w) End If
I hope you’ll find this example handy! Note that in the EquationSolve module
used 0 or 1 to indicate the solution was successful. In case of any error, this parameter will be returned as 0 to the calling module. This is a good habit because if you have runtime errors in DLL module, you’re gonna to invite trouble. In this particular program module, I used first element of array as (0,0). The VB UBound and LBound are very useful functions, which helps us to write
general-purpose array/matrix handling procedures quite easily! There’s another wonderful function ReDim Preserve, which allows you to write dynamically
growing array without losing its content!
Exercise 5-2
Write a program to copy one matrix into another. The procedure prototype may
look like CopyMatrix(OriginalMatrix( ) As Double, CopiedMatrix( )
As Double). Just have a try. It’s quite easy problem. Remember that the matrix
may be 1, 2 or 3 dimensional.
Using stack, try to convert any expression (for example 2+3/5) into equivalent post-fix notation (i.e. 2 3 5 / +) and then evaluate the expression. This would be a much better calculator compared to that comes with Windows! This is a quite tough problem. (If you get struck, ask me for help!). Please note that stack function is built in VB.NET.
If the things like stack or post-fix notation appear strange to you, I recommend that you go through a text book of ‘Data Structures’ and read the following topics – stack, queue, linked list, searching (binary tree), sorting (merge sort, quick sort etc.), trees, graph theory etc.
In normal case functions return their values on top of stack. All variables are stored on stack by default. However, when you use new or malloc the variables are stored in a special space of memory known as ‘heap’. They remain in that place until you delete them! So it is a good idea to clear heap using when you no longer need a variable. If the heap is full, any new or malloc
will fail. So, whenever you do dynamic memory allocation checks if it has been done properly. If it fails the variable pointer will point to NULL. However, some recent compilers automatically handle this situation.
6. Writing DLL in C/C++
There are mainly three types of DLLs that can be written in Visual C++. 1. Win32 console mode DLL and
2. Microsoft Foundation Class (MFC) DLL. 3. COM DLL using ATL (see chapter 19)
First I’ll discuss about console mode DLL.
1. In Visual C++ (Visual Studio prior to .NET), open a new ‘Win32 Dynamic Link Library’ project.
2. Name it as ExDLL
3. Add a C++ source code file and write the following code in it.
Code 6-1
// an example of win32 console DLL #include <math.h>
// define what to export from DLL
_declspec(dllexport) int SquareIt(int); _declspec(dllexport) double pi = 3.141; _declspec(dllexport)
int solve_quad(int,int,int,float*,float*); // a typical function passed by value
int SquareIt(int x) {
return x * x; }
// a typical function passed by reference
int solve_quad(int p, int q, int r, float *q1, float *q2) { float determ; determ=q*q-4*p*r; if(determ<0) { *q1=0; *q2=0; return 0; } else {
*q1=((-q)+sqrt(determ))/(2*p); *q2=((-q)-sqrt(determ))/(2*p);
return 1;
} }
4. Build and compile ExDLL.dll
5. Now open a new ‘Win32 Console Application’ project and name it ExDLLUse.
6. Add a C++ source code file and write the following code in it.
Code 6-2
// this files calls a DLL in runtime
// the DLL must be in same folder as of EXE
// specify full DLL path in Project -> Setting -> Link #include <iostream.h>
// define what to import from DLL
_declspec (dllimport) int SquareIt(int); _declspec (dllimport) double pi;
_declspec (dllimport) int solve_quad(int,int,int,float*,float*); void main(void) { int i; int a,b,c; float root1,root2; int status;
// a function called from DLL cout << "Enter an integer " ; cin >> i ;
cout << "Square is " << SquareIt(i) << endl; // value of a variable called from DLL
cout << "Value of Pi is " << pi << endl; // two values returned from DLL by reference cout << "Enter a,b,c for ax^2+bx+c=0" << endl; cin >> a >> b >> c ;
status = solve_quad(a,b,c,&root1,&root2); if(status==1)
{
cout << "The solution is" << endl;
} else
cout << "No real root" << endl; }
7. Select Project – Setting – Link tab.
8. In the Object/Library module text box, specify the full path of the dll LIB file as shown for example (the path will be different in your computer): "D:\C-PROGRAMMING\ExDLL\Debug\ExDLL.lib"
9. Now compile ExDLLUse.exe
10. Now copy the ExDLL.dll file in the same folder where ExDLLUse.exe exists.
11. Now execute ExDLLUse.exe. It should run as shown below. D:\TEMP>exdlluse
Enter an integer 5 Square is 25
Value of Pi is 3.141
Enter a,b,c for ax^2+bx+c=0 1 -5 6
The solution is 3 and 2
Hooray, you successfully made a DLL in C++!
Observe several points. First, in a DLL you must specify which function/variable to be exported using _declspec (dllexport) command. In
the same way, you need to tell what to import from DLL to an EXE using
_declspec (dllimport) command. In the above example, I showed how to
export a variable, a function by value and another function with some values passed by reference. Compare the function with shown in chapter 3.
Can I call a DLL written in C from Visual Basic?
Yes, you can! But in order to call it, you need to write something extra in C DLL source code.
If you just try to call the above from VB, you will get either 'Bad DLL calling convention' or 'Entry point not found' error message. In order to call a C DLL successfully from VB, you'll need to 'tell' the C DLL in that way. That’s why we need to use _stdcall command.
Modify the Code 6-1 as shown in Code 6-3 and save it as ExDLL.cpp (C/++ source code file).
#include <math.h>
// to import in VB, you should use _stdcall int _stdcall SquareIt(int);
double _stdcall pi = 3.141;
int _stdcall solve_quad(int,int,int,float*,float*);
// a typical function passed by value int _stdcall SquareIt(int x)
{
return x * x; }
// a typical function passed by reference
int _stdcall solve_quad(int p, int q, int r, float *q1, float *q2) { float determ; determ=q*q-4*p*r; if(determ<0) { *q1=0; *q2=0; return 0; } else { *q1=((-q)+sqrt(determ))/(2*p); *q2=((-q)-sqrt(determ))/(2*p); return 1; } }
Since we shall call this C DLL from VB, we don't need any C EXE to test it. But we must define a .DEF file to tell VB which functions/variables can be found from our DLL.
So, in the ExDLL project, add a text file named ExDLL.def and write following code in it.
Code 6-4
LIBRARY ExDLL DESCRIPTION My Test DLL EXPORTS
solve_quad pi
The EXPORTS command provides ‘entry points of DLLs’. LIBRARY is the name
of DLL without any extension. In DESCRIPTION you can write anything. To add
any extra comment, use semicolon. There are several other commands you can write in a .def file. We shall discuss them later.
Now compile the DLL. You should not encounter any error message. (There may be some warning about double to float conversion etc. for this particular example problem, but you can ignore them)
DLL is now ready. Next we shall see how to call it from VB.
Open a standard EXE VB project. Add a standard module (BAS file). Write the following code in it. In your computer, the path of DLL may be different. If you place the compiled DLL file in \WINDOWS\SYSTEM folder, you can just specify the file name as “exdll” only.
Code 6-5
Declare Function SquareIt Lib "d:\c-programming\exdll\debug\exdll.dll" _ (ByVal x As Integer) As Integer
Declare Function solve_quad Lib "d:\c-programming\exdll\debug\exdll.dll" _
(ByVal a As Integer, ByVal b As Integer, ByVal c As Integer, _ ByRef r1 As Single, ByRef r2 As Single) As Integer
Create a command button in the form. In its 'Click' event, write the following code.
Code 6-6
Private Sub Command1_Click() Dim z As Integer
z = SquareIt(5)
MsgBox "Square is " & z Dim a As Integer
Dim b As Integer Dim c As Integer Dim s1 As Single Dim s2 As Single
Dim status As Integer
status = solve_quad(1, -5, 6, s1, s2)
'try with status = solve_quad(1, 1, 6, s1, s2) If status = 1 Then
MsgBox "Root1 " & s1 MsgBox "Root2 " & s2 Else
MsgBox "No real root" End If
End Sub
Now run the VB project. Click on the command button. Everything should be fine and you will get the expected results! So, you have called a DLL written in C from a VB application.
More general way of writing C/++ DLL
You are probably wondering why you should write the C/++ DLL differently for calling from C/++ and VB EXE. In fact you should not! Now I am going to show you how to write a DLL in C/++ so that both C/++ and VB EXE will be able to call it without any modification!
To achieve this feat, instead of _dllexport or _stdcall, we need to write BOOL APIENTRY. For example re-write code 6-3 (ExDLL.cpp) as follows.
Code 6-7
#include <math.h> #include <windows.h>
BOOL APIENTRY SquareIt(int x,int *y) {
*y = x * x;
return 1;
}
BOOL APIENTRY solve_quad(int p, int q, int r, float *q1, float *q2) { float determ; determ=q*q-4*p*r; if(determ<0) { *q1=0; *q2=0; return 0; } else { *q1=((-q)+sqrt(determ))/(2*p); *q2=((-q)-sqrt(determ))/(2*p);
return 1; }
}
Change code 6-4 (ExDLL.def) slightly as shown below.
Code 6-8 LIBRARY ExDLL DESCRIPTION My Test DLL EXPORTS SquareIt solve_quad
Now compile and build the ExDLL.dll file.
To use the new DLL in a C/++ executable, change code 6-2 as shown below.
Code 6-9
#include <iostream.h> #include <windows.h>
// define what to import from DLL
_declspec (dllimport) BOOL APIENTRY SquareIt(int,int*); _declspec (dllexport) BOOL APIENTRY
solve_quad(int,int,int,float*,float*); void main(void) { int i; int a,b,c,y,z; float root1,root2; int status;
cout << "Enter an integer " ; cin >> i ;
z = SquareIt(i,&y);
cout << "Square is " << y << endl;
cout << "Enter a,b,c for ax^2+bx+c=0" << endl; cin >> a >> b >> c ;
status = solve_quad(a,b,c,&root1,&root2); if(status==1)
{
cout << "The solution is" << endl;
cout << root1 << " and " << root2 << endl; }
else
}
Before running, remember to copy the DLL in C/++ exe’s path! To call the DLL from VB, replace code 6-5 by this new code.
Code 6-10
Declare Function SquareIt Lib "d:\c-programming\exdll\debug\exdll.dll" _
(ByVal x As Integer, ByRef y As Integer) As Integer Declare Function solve_quad Lib
"d:\c-programming\exdll\debug\exdll.dll" _
(ByVal a As Integer, ByVal b As Integer, ByVal c As Integer, _ ByRef r1 As Single, ByRef r2 As Single) As Integer
And finally, change code 6-6 as this.
Code 6-11
Private Sub Command1_Click() Dim z As Integer
Dim y As Integer z = SquareIt(5, y)
MsgBox "Square is " & y Dim a As Integer
Dim b As Integer Dim c As Integer Dim s1 As Single Dim s2 As Single
Dim status As Integer
status = solve_quad(1, -5, 6, s1, s2)
'try with status = solve_quad(1, 1, 6, s1, s2) If status = 1 Then
MsgBox "Root1 " & s1 MsgBox "Root2 " & s2 Else
MsgBox "No real root" End If
End Sub
This version of DLL is noticeably better than that we discussed earlier! The type
BOOL returns an integer – either 1 (true) or 0 (false). The calling convention in
code 6-9 might have been written in another way using GetProcAddress. But I
am leaving that stuff!
Why should I bother writing a DLL in C and call it from VB? I can write the DLL in VB itself.
Of course you can write a DLL in VB as well. But main advantage of writing a DLL in C is performance. A program (either EXE or DLL) written in C generally executes faster than those written in VB. (This sentence needs a little bit explanation. The speed of your applications mainly depends on the algorithm adopted rather than on the language you are using. A good VB program can easily outperform a poorly written C program. Then why it is said the C program executes faster? Well, take an example of string operations. Normally in C you use char* to perform string manipulation. But VB does not have
explicit pointers. However, it internally handles strings in the same way as that of pointers in C. Definitely this internal operation takes some more time than direct handling of pointers. Similarly i++ executes faster than i = i + 1
because in former case the accumulator in memory directly gets incremented while in case of the later, variable’s value is first copied into the registers and then the addition is performed. Moreover VB programs often calls Windows API functions, which are mostly written in C. Using the same logic, programs written in assembly or machine languages run fastest because the processor directly executes them without any translation.) In many applications speed is significant. Apart from that, I already told you that C could interact with hardware in much more versatile manner compared to VB. So often, writing a DLL in C becomes a necessity.
Can I call any C/++ DLL in this way?
Unfortunately, you can’t. Why? Well, to call a DLL you must know how to call its functions. Unless the developer of the DLL provides you with the necessary documentation, you don’t know how to call it. For example, Microsoft tells you how to call its APIs. So, you can use them. Moreover, some DLLs can’t be used without registering/licensing. All these registration stuff can be described in a .def file. These things are quite scary and I shall come to this point later.
Next we’re going to build a MFC DLL!
In Visual C++, open a new MFCAppWizard(dll) project and name it as MyDll. The VC will generate lots of code for you. All you need to add your own codes in between. I am showing your own code in blue color and some VC generated code in pink color as a guide where you need to insert your own code.
Add the following code in Mydll.h file.
Code 6-12
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h" // main symbols // my code starts
_declspec(dllexport) void WINAPI ThickRectangle(CDC* pDC, int x1, int y1,int x2, int y2, int t);
_declspec(dllexport) void WINAPI ThickEllipse(CDC* pDC, int x1, int y1, int x2, int y2, int t);
_declspec(dllexport) void WINAPI ThickPixel(CDC* pDC, int x1, int y1);
// my code ends
////////////////////////////////////////////////////////////// ///////////////
// CMyDllApp
// See MyDll.cpp for the implementation of this class
Now add the following code in MyDll.cpp file.
Code 6-13
////////////////////////////////////////////////////////////// ///////////////
// The one and only CMyDllApp object CMyDllApp theApp;
// my code starts here
_declspec(dllexport) void WINAPI ThickRectangle(CDC* pDC, int x1, int y1,int x2, int y2, int t)
{ AFX_MANAGE_STATE(AfxGetStaticModuleState()); CBrush newbrush; CBrush* oldbrush; pDC->Rectangle(x1,y1,x2,y2); pDC->Rectangle(x1+t,y1+t,x2-t,y2-t); newbrush.CreateSolidBrush(RGB(255,255,0)); oldbrush=pDC->SelectObject(&newbrush); pDC->FloodFill(x1+(t/2),y1+(t/2),RGB(0,0,0)); pDC->SelectObject(oldbrush); newbrush.DeleteObject(); }
_declspec(dllexport) void WINAPI ThickEllipse(CDC* pDC, int x1, int y1, int x2, int y2, int t)
AFX_MANAGE_STATE(AfxGetStaticModuleState()); CBrush newbrush; CBrush* oldbrush; pDC->Ellipse(x1,y1,x2,y2); pDC->Ellipse(x1+t,y1+t,x2-t,y2-t); newbrush.CreateSolidBrush(RGB(255,0,0)); oldbrush=pDC->SelectObject(&newbrush); pDC->FloodFill(x1+(t/2),y1+((y2-y1)/2),RGB(0,0,0)); pDC->SelectObject(oldbrush); newbrush.DeleteObject(); }
_declspec(dllexport) void WINAPI ThickPixel(CDC* pDC, int x1, int y1) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CPen newpen; CPen* oldpen; pDC->SetPixel(x1,y1,0L); newpen.CreatePen(PS_SOLID,2,RGB(255,255,0)); oldpen=pDC->SelectObject(&newpen); pDC->MoveTo(x1-5,y1); pDC->LineTo(x1-1,y1); pDC->MoveTo(x1+1,y1); pDC->LineTo(x1+5,y1); pDC->MoveTo(x1,y1-5); pDC->LineTo(x1,y1-1); pDC->MoveTo(x1,y1+1); pDC->LineTo(x1,y1+5); pDC->SelectObject(oldpen); newpen.DeleteObject(); }
// my code ends here
Now compile the dll. It should compile without any error message.
To test our generated dll, we need to make an MFC EXE to test it. Create a new MFCAppWizard(exe) named MyDllExe.
In MyDllExeView.h, add the following code.
Code 6-14
#pragma once
#endif // _MSC_VER >= 1000 // my code starts here
extern void WINAPI ThickRectangle(CDC* pDC, int x1, int y1, int x2, int y2,int t);
extern void WINAPI ThickEllipse(CDC* pDC, int x1, int y1, int x2, int y2,int t);
extern void WINAPI ThickPixel(CDC* pDC, int x1, int y1); // my code ends here
class CMyDllExeView : public CView {
protected: // create from serialization only
In MyDllExeView.cpp file, add the following code.
Code 6-15
void CMyDllExeView::OnDraw(CDC* pDC) {
CMyDllExeDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);
// TODO: add draw code for native data here // my code starts here
// call ThickPixel several times for(int i=50;i<900;i++) ThickPixel(pDC,i,75); // call ThickRectangle ThickRectangle(pDC,50,300,75,350,20); ThickRectangle(pDC,150,350,250,450,25); ThickRectangle(pDC,400,200,700,600,25); // call ThickEllipse ThickEllipse(pDC,50,100,75,150,10); ThickEllipse(pDC,150,150,250,250,15); ThickEllipse(pDC,450,250,650,550,10);
pDC->TextOut(50,50,"Saikat's first DLL in Visual C++!"); pDC->MoveTo(200,200);
pDC->LineTo(300,300); // my code ends here
}
////////////////////////////////////////////////////////////// ///////////////
As stated before, from Project – Settings – Link tab, specify the full path of Object/Library module (for the DLL’s LIB file). Now compile the EXE file. Be sure to copy the DLL file into same folder as of the EXE. Run the EXE and you should see some colorful boxes and ellipses! In this example, I used some graphics features of VC++ intentionally to demonstrate a true MFC example. You can also incorporate the same functions as discussed in console mode DLL creation. Study the code carefully and you will realize what is the general procedure to write MFC DLLs! Of course, things may appear Hebrew to you if you are new to VC++. That’s why I recommend that you read a standard Visual C++ learning side by side. In fact, there are lots of more things need to be taken into consideration while developing MFC DLLs. A detail discussion of these topics is beyond the scope of this book. Honestly speaking, I myself don’t know all the features of VC++!
What is the meaning of _stdcall?
Functions using the standard calling convention remove the parameters from the stack before they return to the caller. In the normal C/++ calling convention, the caller cleans up the stack instead of the function. Most other language such as VB or Pascal use standard calling convention by default. The standard calling convention so named because all Win32 API functions, except few that take variable arguments, use it. Variable argument functions continue to use C calling convention of _cdecl. Virtually all functions offered by COM interfaces on Microsoft platforms use the standard calling convention.
Gee! So you learnt to create both console mode and MFC DLLs in C++! So far we only exported functions and variables from a DLL. There remains to learn another important task. How to export classes from a DLL?
Can I use DLL in Unix?
Yeah! In Unix, DLLs are known as ‘shared objects’ (*.so) – they work much like in the same way as that of DLLs in Windows.
Final notes on DLL – all’s not well that ends well
Don’t think that DLLs are solutions of all problems! Calling DLLs have traps of their own! As long as you are passing simple data types (e.g. number, string etc.) between C and VB you are fine. Trouble starts when you try to pass complex data types such as array, object etc.
Consider code 5-6 again. It showed how to find out matrix product in C/++. Now, suppose you want to make it a DLL. You can easily do that in C/++ by
following the methods discussed (dllexport, stdcall or BOOL API etc.) in this chapter. Calling the DLL from C/++ won’t have any problem. But how do you call it from VB? Observe that the line in C/++ which calls the function p = ProductMatrix(matrix1,matrix2,row1,col1,col2) does not have any
direct equivalent in VB!
In C/++ when you state matrix1, you are actually passing the address of the
first element of the matrix. Also the variable p after calling the function contains an address which stores address of an integer. How do you make VB understand this glitch? How do you return as user defined structure or object from C/++ to VB, which does not support that structure or object? So inter operability among several languages indeed a problem, a big problem. We’ll see in the following program how to pass a matrix through a C function that returns a structure and then calling it in VB.
Create a DLL named ArrayStructure using the methodology described already. The code is given.
Code 6-16 #include<string.h> #include<math.h> #include<malloc.h> struct quad { float r1; float r2; char* status; };
quad* _stdcall solve_quad(int *a); quad* _stdcall solve_quad(int *a) { quad *x; x=(quad*)malloc(sizeof(quad)); int p,q,r; float determ; p=*(a+0); q=*(a+1); r=*(a+2); determ=q*q-4*p*r;
if(determ<0) /* no real root */ {
x->r1=0; x->r2=0;
x->status=(char*)malloc(13*sizeof(char)); strcpy(x->status,"No real root");
return x;
}
else /* real root exists */ {
x->r1=((-q)+sqrt(determ))/(2*p); x->r2=((-q)-sqrt(determ))/(2*p);
x->status=(char*)malloc(15*sizeof(char)); strcpy(x->status,"Two real roots");
return x;
} }
Create also the .def file as usual.
LIBRARY ArrayStructure EXPORTS solve_quad
Now use the following code to call the function in VB6.
Code 6-17
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(Destination As Any, ByVal Source As Any, ByVal Length As Long)
Private Declare Function solve_quad Lib "arraystructure.dll" _ (ByVal m As Long) As Long
Private Sub Command1_Click() Dim adr As Long
Dim z(3) As Long Dim Root1 As Single Dim Root2 As Single Dim Solution As String z(0) = 1
z(1) = -5 z(2) = 6
adr = solve_quad(VarPtr(z(0))) Call CopyMemory(Root1, adr, 4) Call CopyMemory(Root2, adr + 4, 4) Call CopyMemory(Solution, adr + 8, 4)
MsgBox "Root1 = " & Root1 & vbCrLf & "Root2 = " & Root2 _ & vbCrLf & "Solution = " & Solution
Execute the VB6 program and you’ll see two roots and status of solution.
I understand that code 6-17 requires some clarification. VarPtr is an
undocumented V6B function that returns the address of a variable as long. Two
similar functions are StrPtr and ObjPtr. In code 17-16, we pass the matrix as
the address of its first element i.e. a(0). The quad* indicates that the function solve_quad returns an address which holds a data of type quad. In C/++, an int needs 4 bytes. In VB6, a long needs the same amount of bytes. That’s why
we defined z(3) as Long in VB6 instead of Integer. (You may wonder why
there is such a discrepancy between C and VB6 data types. Well, it is a logical question. However, this has been rectified in VB.NET where Integer takes 4
bytes as that of in C and Short takes 2 bytes as of earlier Integer!)
Anyway, back to our business. The adr stores the memory address of quad
structure after calling the solve_quad function. Next the API function CopyMemory(Root1, adr, 4) assigns the content of address adr into variable Root1. It copies 4 bytes of data (since we defined Root1 as Single, which also
takes 4 bytes) beginning from address adr.
By the way, these direct memory manipulation functions have been removed from VB.NET!
Exercise 6-1
Rewrite the code 6-16 using BOOL APIENTRY instead of _stdcall. (Hints:
Suppose you want to call this function inside a C main function. Then you can
also use prototype as void solve_quad(int *a, quad **m). Inside
function, write *m = x instead of return x in code 6-16. While calling the
function, use solve_quad(z,&w) where z is defined as int z[3] and w as quad *w. Access structure elements as w->r1, w->status etc. Compare two
different types of declarations of same function in conjunction with “difference between function and procedure” as discussed in chapter 3.)
7. File handling
In this chapter you learn basic file handling in C and QBASIC. You may ask that why I am going to discuss this very basic file handling technique while they are normally available in most beginning programming language books. It’s true. In fact nowadays people rarely use C/++ (leave alone Basic) exclusively for file handling operation in an application. Mostly a backend database and a front end user interface are used. However, in very early days of computing, only one language was used to create everything, from writing to file to presenting data to user as well. If you ever need to maintain some legacy code, you might find this kind of file handling in C.
However, I presented the code here in very basic form. For example, the updating/deletion algorithm adopted here is not very efficient. Moreover, in actual practice, the records should be stored in binary tree or b-tree format (normally b-tree format is taken in most large database applications).
Code 6-1 shows the how to create binary file in C and then add, modify, display, delete, search records in the file and exporting to ASCII as well.
Code 7-1
/* A SIMPLE DATABASE OPERATION PROGRAM */ /* (c) Saikat Basak */
/* COMPILED IN TURBO-C/C++, also compiles in Visual C++ */ /* the program should also compile in UNIX */
#include<stdio.h> #include<string.h> #include<stdlib.h> void main(void) { FILE *fp,*ft,*fx; int choice, another,n; struct person { char name[30+1]; char address[50+1]; char phone[10+1]; }; char myname[30+1]; struct person man; long int recsize;
if(fp==NULL) {
fp=fopen("data.dat","wb+"); if(fp==NULL)
{
puts("Can't open file"); exit(0); } } recsize=sizeof(man); while(1) { puts("---"); puts("A simple database application");
puts("---"); printf("1. Add Record\n");
printf("2. Display\n"); printf("3. Modify\n"); printf("4. Delete\n"); printf("5. Search\n");
printf("6. Export to text\n"); printf("7. Exit\n");
printf("Enter your choice\n"); fflush(stdin); scanf("%d",&choice); switch(choice) { case 1: fseek(fp,0,SEEK_END); another=1; while(another==1) { printf("Enter name\n"); scanf(" %[^\n]s",man.name); printf("Enter address\n"); scanf(" %[^\n]s",man.address); printf("Enter phone\n"); scanf(" %[^\n]s",man.phone); fwrite(&man,recsize,1,fp);
printf("Add another? 1 for yes\n"); scanf("%d",&another); } break; case 2: rewind(fp); while(fread(&man,recsize,1,fp)==1)
printf("%s %s %s\n",man.name,man.address,man.phone); break; case 3: another=1; while(another==1) {
printf("Enter name to modify\n"); scanf("%[^\n]s",myname); rewind(fp); while(fread(&man,recsize,1,fp)==1) { if(strcmp(man.name,myname)==0) {
printf("Enter new name\n"); scanf(" %[^\n]s",man.name); printf("Enter new address\n"); scanf(" %[^\n]s",man.address); printf("Enter new phone\n"); scanf(" %[^\n]s",man.phone); fseek(fp,-recsize,SEEK_CUR); fwrite(&man,recsize,1,fp); break; } }
printf("Modify another record? 1 for yes\n"); fflush(stdin); scanf("%d",&another); } break; case 4: another=1; while(another==1) {
printf("Enter name to delete\n"); scanf("%s",myname); ft=fopen("temp.dat","wb+"); rewind(fp); while(fread(&man,recsize,1,fp)==1) { if(strcmp(man.name,myname) != 0) fwrite(&man,recsize,1,ft); }
fclose(fp); fclose(ft);
remove("data.dat");
rename("temp.dat","data.dat"); fp=fopen("data.dat","rb+");
printf("Delete another record 1 for yes\n"); fflush(stdin); scanf("%d",&another); } break; case 5: rewind(fp);
printf("Enter name to search for (you may input first few characters)\n");
scanf("%s",myname); n=strlen(myname); while(fread(&man,recsize,1,fp)==1) { if(strnicmp(man.name,myname,n)==0) { printf("%s %s %s\n",man.name,man.address,man.phone); } } break; case 6: fx=fopen("data.csv","w+"); rewind(fp); while(fread(&man,recsize,1,fp)==1) fprintf(fx,"%s,%s,%s\n",man.name,man.address,man.phone); fclose(fx);
printf("File contents has been exported to data.csv\n"); break; case 7: fclose(fp); exit(1); } } }
Study the above program carefully. There are some new functions. The same
program is again made in QBASIC as given in Code 6-2. Here TYPE is
Code 7-2
REM database handling, structure and file example in QBASIC REM (c) Saikat Basak
REM with add, display, search, modify, delete in binary mode ON ERROR GOTO 100 TYPE person myname AS STRING * 30 myaddress AS STRING * 40 myphone AS STRING * 10 END TYPE
DIM MyBook AS person
DIM whatname AS STRING * 30 DIM deletename AS STRING * 30 5
WHILE (1)
COLOR 10, 3, 2 CLS
LOCATE 5, 25
PRINT "Simple database application" LOCATE 7, 30
PRINT "1. Add record" LOCATE 8, 30 PRINT "2. Display" LOCATE 9, 30 PRINT "3. Modify" LOCATE 10, 30 PRINT "4. Delete" LOCATE 11, 30 PRINT "5. Search" LOCATE 12, 30
PRINT "6. Export to text" LOCATE 13, 30
PRINT "7. Exit" LOCATE 20, 25
INPUT "Enter your choice"; choice SELECT CASE choice
CASE 1 CLS
another$ = "y"
OPEN "database.dat" FOR BINARY AS #1 LEN = LEN(MyBook) OPEN "database.idx" FOR INPUT AS #10
INPUT #10, i CLOSE #10
INPUT "Enter name: "; MyBook.myname
INPUT "Enter address: "; MyBook.myaddress INPUT "Enter phone#: "; MyBook.myphone PUT #1, i, MyBook
i = i + 80
INPUT "Add another ? ", another$ LOOP
CLOSE #1
OPEN "database.idx" FOR OUTPUT AS #10 PRINT #10, i
CLOSE #10
CASE 2
OPEN "database.idx" FOR INPUT AS #10 INPUT #10, i
CLOSE #10 CLS
PRINT "Displaying records...": PRINT j = 1
OPEN "database.dat" FOR BINARY AS #2 LEN = LEN(MyBook) DO WHILE ((NOT EOF(2)) AND (j < i))
GET #2, j, MyBook
PRINT "Name: "; MyBook.myname
PRINT "Address: "; MyBook.myaddress PRINT "Phone: "; MyBook.myphone PRINT
j = j + 80 LOOP
CLOSE #2
INPUT "Press any key to continue", x CASE 3
CLS
OPEN "database.dat" FOR BINARY AS #3 LEN = LEN(MyBook) INPUT "Enter name to modify: "; whatname
flag = 0 m = 1
DO WHILE NOT EOF(3) GET #3, m, MyBook
IF MyBook.myname = whatname THEN flag = 1
INPUT "Enter Name: "; MyBook.myname
INPUT "Enter Address: "; MyBook.myaddress INPUT "Enter Phone#: "; MyBook.myphone PUT #3, m, MyBook
END IF m = m + 80 LOOP
CLOSE #3
INPUT "Press any key to continue", x CASE 4
CLS
OPEN "temp.dat" FOR BINARY AS #11 LEN = LEN(MyBook) OPEN "database.dat" FOR BINARY AS #4 LEN = LEN(MyBook) INPUT "Enter name to delete: "; deletename
flag = 0 p = 1 q = 1
DO WHILE NOT EOF(4) GET #4, p, MyBook
IF MyBook.myname <> deletename THEN PUT #11, q, MyBook
q = q + 80
ELSEIF MyBook.myname = deletename THEN flag = 1
PRINT ""; deletename; " is being deleted" q = q
END IF p = p + 80 LOOP
IF flag = 0 THEN PRINT "Record not found" CLOSE #4
CLOSE #11
SHELL "del database.dat"
SHELL "ren temp.dat database.dat" OPEN "database.idx" FOR OUTPUT AS #10 PRINT #10, q - 80
CLOSE #10
INPUT "Press any key to continue", x CASE 5
CLS
OPEN "database.dat" FOR BINARY AS #5 LEN = LEN(MyBook) INPUT "Enter name to search for: "; whatname
flag = 0 k = 1
DO WHILE NOT EOF(5) GET #5, k, MyBook
IF MyBook.myname = whatname THEN flag = 1
PRINT "Name: "; MyBook.myname
PRINT "Address: "; MyBook.myaddress PRINT "Phone: "; MyBook.myphone END IF
k = k + 80 LOOP