Managed C++ for beginners

Introduction

When you use managed C++, your code is handled by the common language runtime (CLR). That means things like garbage collection and interoperability are done for you by the CLR. I have given below a well-commented simple program to get you guys started. I am not sure how it's gonna work if you use VS.Net. I used notepad to create the cpp file and compiled it using cl.exe with the /clr switch. I thought I'd better get a hang of things before I use the VS.Net App Wizard to generate managed C++ code for me.

Anyhow good luck....

The Program

#using

using namespace System;

//__gc means this is a garbage collected class

//we don't have to delete the instances

public __gc class abc

{

public:

String *s1;

//overloaded member functions

void Greet ( String* s ) ;

void Greet( ) ;

//overloaded constructors

abc( ) ;

abc ( int ) ;

} ;

void main( )

{

Console::WriteLine ( "Hello, what's up? " ) ;

abc *a1= new abc( ) ;

a1->Greet( ) ;

a1->Greet ( "nish" ) ;

abc *a2 = new abc ( 5 ) ;

//Now lets have some fun with pointers

String* s ; //Create a string s on the CLR heap

String* &x=s ; //Create a string x that references s

String* *z=&s ; //Create a string pointer and point it to s

//Well, basically now we have x,z,s all pointing to the same memory :-)

s = "This is fun" ;

Console::WriteLine ( x ) ;

Console::WriteLine ( s ) ;

Console::WriteLine ( *z ) ;

x="More fun";

Console::WriteLine ( x ) ;

Console::WriteLine ( s ) ;

Console::WriteLine ( *z ) ;

*z="Getting really cool now";

Console::WriteLine ( x ) ;

Console::WriteLine ( s ) ;

Console::WriteLine ( *z ) ;

}

abc::abc( )

{

s1 = "Kamran" ;

}

abc::abc ( int i )

{

for ( int j = 0 ; j <>

Console::WriteLine ( "Managed C++ is more fun than C#" ) ;

}

void abc::Greet ( String* s )

{

//I tried %s instead of {0}. Won't work :-)

Console::WriteLine("Hello {0}",s);

}

void abc::Greet( )

{

Console::WriteLine ( "Hello {0}", s1 );

}

Output

D:\test\mc++>hello

Hello, what's up?

Hello Kamran

Hello nish

Managed C++ is more fun than C#

Managed C++ is more fun than C#

Managed C++ is more fun than C#

Managed C++ is more fun than C#

Managed C++ is more fun than C#

This is fun

This is fun

This is fun

More fun

More fun

More fun

Getting really cool now

Getting really cool now

Getting really cool now

Chat Application In Managed C++

The following program is a dialog based application, which works both as a client and a server. We can send messages to both the client and the server. First we will run it as a server connected to a desired port and then as a client. We plan to design our dialog application like this.

One major disadvantage of Managed C++ is the absence of the Resource Editor. Due to this difficulty we always have to wirte the full code to create a dialog and its components. To create our dialog box we have to write the code as shown below. Following is the class which represents our form i.e the dialog. The �gc keyword that preceeds the class name declares a garbage-collected object.

__gc class WinForm: public Form

{

public:

Label *label3 ;

ListBox *rmessage ;

GroupBox *groupBox3 ;

GroupBox *groupBox2 ;

ListBox *smessage ;

Button *sendbut ;

TextBox *message ;

RadioButton *crad ;

RadioButton *srad ;

GroupBox *groupBox1 ;

Button *conbutton ;

TextBox *port ;

Label *label2 ;

TextBox *sname ;

Label *label1 ;

Socket *s ;

Stream *st ;

void conbutton_Click ( Object *sender, System::EventArgs* e ) ;

void sendbut_Click ( Object *sender, System::EventArgs* e ) ;

WinForm( )

{

InitForm( ) ;

}

void Dispose( )

{

// Form is being destroyed. Do any necessary clean-up here.

s -> Close( ) ;

st -> Close( ) ;

Form::Dispose( ) ;

}

void InitForm( )

{

rmessage = new ListBox( ) ;

groupBox1 = new GroupBox( ) ;

port = new TextBox( ) ;

smessage = new ListBox( ) ;

label2 = new Label( ) ;

label3 = new Label( ) ;

label1 = new Label( ) ;

sname = new TextBox( ) ;

conbutton = new Button( ) ;

groupBox2 = new GroupBox( ) ;

groupBox3 = new GroupBox( ) ;

crad = new RadioButton( ) ;

sendbut = new Button( ) ;

message = new TextBox( ) ;

srad = new RadioButton( ) ;

////form

System::Drawing::Size s ;

s.Height = 400 ;

s.Width = 400 ;

ClientSize = s ;

Point p ;

p.X = 264 ;

p.Y = 48 ;

Text = "Chat" ;

//rmessage

p.X = 18 ;

p.Y = 22 ;

s.Width = 312 ;

s.Height = 69 ;

rmessage -> Location = p ;

rmessage -> Size = s ;

rmessage -> TabIndex = 10 ;

//groupbox1

p.X = 16 ;

p.Y = 8 ;

s.Width = 88 ;

s.Height = 72 ;

groupBox1 -> Location = p ;

groupBox1 -> TabIndex = 5 ;

groupBox1 -> TabStop = false ;

groupBox1 -> Text = "Connect As" ;

groupBox1 -> Size = s ;

//port

p.X = 200 ;

p.Y = 48 ;

s.Width = 56 ;

s.Height = 20 ;

port -> Location = p ;

port -> TabIndex = 3 ;

port -> Size = s ;

//smessage

p.X = 32 ;

p.Y = 144 ;

s.Width = 312 ;

s.Height = 69 ;

smessage -> Location = p ;

smessage -> Size = s ;

smessage -> TabIndex = 8 ;

//label2

p.X = 128 ;

p.Y = 48 ;

s.Width = 40 ;

s.Height = 16 ;

label2 -> Location = p ;

label2 -> Text = "Port" ;

label2 -> Size = s ;

label2 -> TabIndex = 2 ;

///label3

p.X = 16 ;

p.Y = 96 ;

s.Width = 48 ;

s.Height = 23 ;

label3 -> Location = p ;

label3 -> Text = "Message" ;

label3 -> Size = s ;

label3 -> TabIndex = 12 ;

//label1

p.X = 128 ;

p.Y = 16 ;

s.Width = 72 ;

s.Height = 16 ;

label1 -> Location = p ;

label1 -> Text = "Server Name" ;

label1 -> Size = s ;

label1 -> TabIndex = 0 ;

// sname

p.X = 200 ;

p.Y = 16 ;

s.Width = 120 ;

s.Height = 20 ;

sname -> Location = p ;

sname -> TabIndex = 1 ;

sname -> Size = s ;

//conbutton

p.X = 264 ;

p.Y = 48 ;

s.Width = 56 ;

s.Height = 23 ;

conbutton -> Location = p ;

conbutton -> Size = s ;

conbutton -> TabIndex = 4 ;

conbutton -> Text = "Connect" ;

conbutton -> add_Click ( new EventHandler ( this,

&WinForm::conbutton_Click ) ) ;

//groupbox2

p.X = 16 ;

p.Y = 128 ;

s.Width = 344 ;

s.Height = 96 ;

groupBox2 -> Location = p ;

groupBox2 -> TabIndex = 9 ;

groupBox2 -> TabStop = false ;

groupBox2 -> Text = "Send" ;

groupBox2 -> Size = s ;

///groupbox3

p.X = 16 ;

p.Y = 232 ;

s.Width = 344 ;

s.Height = 104 ;

groupBox3 -> Location = p ;

groupBox3 -> TabIndex = 9 ;

groupBox3 -> TabStop = false ;

groupBox3 -> Text = "Recieved" ;

groupBox3 -> Size = s ;

//crad

p.X = 16 ;

p.Y = 40 ;

s.Width = 56 ;

s.Height = 16 ;

crad -> Location = p ;

crad -> Text = "Client" ;

crad -> Size = s ;

crad -> TabIndex = 1 ;

//sendbut

p.X = 288 ;

p.Y = 96 ;

s.Width = 75 ;

s.Height = 23 ;

sendbut -> Location = p ;

sendbut -> Size = s ;

sendbut -> TabIndex = 7 ;

sendbut -> Text = "Send" ;

sendbut -> add_Click ( new EventHandler ( this,

&WinForm::sendbut_Click ) ) ;

///message

p.X = 72 ;

p.Y = 96 ;

s.Width = 208 ;

s.Height = 20 ;

message -> Location = p ;

message -> TabIndex = 6 ;

message -> Size = s ;

///srad

p.X = 16 ;

p.Y = 16 ;

s.Width = 56 ;

s.Height = 23 ;

srad -> Location = p ;

srad -> Text = "Server" ;

srad -> Size = s ;

srad -> TabIndex = 0 ;

srad -> Checked = true ;

groupBox3 -> Controls -> Add ( this -> rmessage ) ;

groupBox1 -> Controls -> Add ( this -> crad ) ;

groupBox1 -> Controls -> Add ( this -> srad ) ;

Controls -> Add ( label3 ) ;

Controls -> Add ( smessage ) ;

Controls -> Add ( sendbut ) ;

Controls -> Add ( message ) ;

Controls -> Add ( groupBox1 ) ;

Controls -> Add ( conbutton ) ;

Controls -> Add ( port ) ;

Controls -> Add ( label2 ) ;

Controls -> Add ( sname ) ;

Controls -> Add ( label1 ) ;

Controls -> Add ( groupBox2 ) ;

Controls -> Add ( groupBox3 ) ;

}

} ;

We wrote all the initailizations in a function called InitForm( ) which we have called in the constructor of our form class . This is how the form should now look (this will be visible only when we compile it, now we still have to add handlers so we will not compile it right now ).

Next we added two data members Viz:

Socket s ;

Stream st ;

We will have to add two events viz: conbutton_Click( ) and sendbut_Click( ) for the Connect and Send Buttons respectively. The Connect button will make the application work as a client or server according to the selection made.

void WinForm::conbutton_Click(Object *sender, System::EventArgs *e)

{

String *servername = sname -> Text ;

int *portno ;

int x = port -> Text -> ToInt32( ) ;

portno = &x ;

if ( srad -> Checked )

{

TCPListener *tcpl = new TCPListener ( x ) ;

tcpl -> Start( ) ;

s = tcpl -> Accept( ) ;

recvserverthread *r = new recvserverthread ( s, rmessage);

ThreadStart *ts = new ThreadStart ( r,

&recvserverthread::myfunc ) ;

Thread *t = new Thread ( ts ) ;

t -> Start( ) ;

}

else

{

TCPClient *tcpc = new TCPClient( ) ;

if(DNS::GetHostByName(servername ) -> Hostname == "" )

{

MessageBox::Show ( "can't Connect to Server" ) ;

return ;

}

tcpc -> Connect ( servername, x ) ;

st = tcpc -> GetStream( ) ;

recvclientthread *r = new recvclientthread ( st, rmessage ) ;

Thread *t = new Thread ( new ThreadStart (r,

&recvclientthread::myfunc ) ) ;

t -> Start( ) ;

}

}

If the Checked property of srad is true then the TCPListener class is used for server. The Start( ) method of the TCPListener class starts listening to network requests. The Accept( ) method accepts a pending connection request. Next the receiving of messages is started on another thread.

If the Checked property of srad is not true the TCPClient class is used for client. The GetHostByName( ) method retrieves a string representing the DNS name of the host. If the name is null, the application stops. If not, the client is connected using the Connect( ) method. The Connect( ) method connects the client to a TCP host. The GetStream( ) method returns the stream used to write data to the remote host. The receiving of messages is then started on a different thread.

The sendbut_Click( ) event looks like this:

void WinForm::sendbut_Click(Object *sender, System::EventArgs *e)

{

Byte bstr[ ] = Encoding :: ASCII-> GetBytes ( message -> Text);

if ( srad -> Checked )

s -> Send ( bstr, bstr.Length, 0 ) ;

else

st -> Write ( bstr, 0, bstr.Length ) ;

smessage -> Items -> Add ( message -> Text ) ;

message -> Text = "" ;

}

If the application is running as a server we use the Send( ) method of the Socket class, which sends data connected to a socket. If it is running as a client we use the Write( ) method of the Stream class, which writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. This message is then added to the �Sent� ListBox.

To receive messages we wrote two classes viz: - the recvclientthread class and the recvserverthread class for the client and server respectively. These two classes will take care of starting the threads receiving the messages. The code for both the classes is as follows:

__gc class recvclientthread

{

Stream *s ;

ListBox *l ;

public:

recvclientthread ( Stream *ss, ListBox *ll )

{

s = ss ;

l = ll ;

}

void myfunc( )

{

Byte bstr[] = new Byte[32] ;

while ( true )

{

s -> Read ( bstr, 0, bstr.Length ) ;

String *str = Encoding :: ASCII -> GetString ( bstr ) ;

l -> Items -> Add ( str ) ;

}

}

};

__gc class recvserverthread

{

private:

Socket *s ;

ListBox *l ;

public:

recvserverthread( Socket *ss, ListBox *ll )

{

l = ll ;

s = ss ;

}

void myfunc( )

{

while ( true )

{

Byte bstr[] = new Byte[32] ;

s -> Receive ( bstr, bstr.Length, 0 ) ;

String *str = Encoding :: ASCII -> GetString ( bstr ) ;

l -> Items -> Add ( str ) ;

}

}

};

To receive a message in the client we use the Stream class. The Read( ) method of the Stream class reads a sequence of bytes from the current stream and advances the current position within the stream by the number of bytes read. This stream is converted into a string using the Encoding::ASCII -> GetString( ) method and added to the ListBox using the Items->Add( ) method.

To receive a message in the server we use the Socket class. The Receive( ) method of the Socket class receives data from a connected socket. This data is converted into a string and added to the ListBox.

Transformations

Transforming an object includes Translation, Rotation, Scaling and Shearing. We plan to apply these transformations to our text. We plan to do something like this

In this program we have drawn the shadow first and then the text, so that the text overlaps the shadow making the shadow appear as if it is behind the text. We have drawn the shadow using the same font as the original text and using gray color. The shadow should be half the size of the original text and must be sheared in the x direction.

The code for OnPaint( ) is

virtual void OnPaint(PaintEventArgs* e)

{

Graphics * g = e->Graphics ;

System::Drawing::Font *myfont = new

System::Drawing::Font ( "Times New Roman",100 ) ;

Matrix *mymat = new Matrix ( );

mymat -> Shear ( -1.4, 0 ) ;

mymat -> Scale ( 1, 0.5 ) ;

mymat -> Translate ( 236, 170 );

g -> Transform = mymat ;

SolidBrush *mybrush = new SolidBrush ( Color::Gray ) ;

g -> DrawString ( "K", myfont, mybrush, 50, 50 ) ;

g -> DrawString ( "I", myfont, mybrush, 150, 50 ) ;

g -> DrawString ( "C", myfont, mybrush, 200, 50 ) ;

g -> DrawString ( "I", myfont, mybrush, 300, 50 ) ;

g -> DrawString ( "T", myfont, mybrush, 350, 50 ) ;

g -> ResetTransform( ) ;

mybrush -> Color = Color::DarkMagenta ;

g -> DrawString ( "K", myfont, mybrush, 50, 50 ) ;

mybrush -> Color = Color::FromARGB ( 150, 0, 255, 255 ) ;

g -> DrawString ( "I", myfont, mybrush, 150, 50 ) ;

Point p1 ;

p1.X = 200 ;

p1.Y = 50 ;

Point p2 ;

p2.X = 350 ;

p2.Y = 200 ;

LinearGradientBrush *lgb = new LinearGradientBrush ( p1,

p2, Color::Brown , Color::Yellow ) ;

g -> DrawString ( "C", myfont, lgb, 200, 50 ) ;

HatchBrush *hb = new HatchBrush (

HatchStyle::DiagonalCross, Color::Blue, Color::Red ) ;

g -> DrawString ( "I", myfont, hb, 300, 50 ) ;

Image *myimg = Image::FromFile ( "C:\\test.bmp" );

TextureBrush *tb = new TextureBrush ( myimg ) ;

g -> DrawString ( "T", myfont, tb, 350, 50 ) ;

}

We have created an object of the Matrix class. Transformations are always applied using Matrices. Matrix addition and multiplication result in various transformations.

We have applied shearing by �1.4 in x direction using the Shear( ) method. Next we have scaled the matrix by half, using the Scale( ) method. Passing a 0.5 as the second coordinate results in reduction in the y direction by half. After doing all this, the coordinate column of the matrix also gets multiplied by some factor resulting in some different coordinates. These coordinates have to be brought to their actual positions. We did this using the Translate( ) method. This corresponds to bringing the origin back to normal.

After applying all the transformations to the Matrix, we have set the Transform property of the Graphics class to this matrix. The Transform property of the Graphics class sets or gets the world transform for the Graphics object.

Under such a transformed scenario we have drawn the strings at the specified positions using the specified Brush and Font. Using the DrawString( ) method. This will fully create the shadow part.

The original text drawing is simple; we must get rid of the transformations applied. This is done by resetting the Transformation world by using the ResetTransform( ) method.

To draw a "K" we have used a solid brush with a DarkMegenta Color.

To draw an "I" we have used a Transparent Brush. A Brush can be made Transparent by setting the alpha component of its color to a value less than 255 using the FromArgb( ) method.. The alpha value ranges from 0 (fully transparent) to 255(fully opaque). The default is opaque.

We have drawn "C� with a GradientBrush, while "I" with a HatchBrush. To draw the "T", we have used an image in a TextureBrush.

Code for Mirror Image

In the following program we have drawn a mirror image.

__gc class WinForm: public Form

{

public:

WinForm( )

{

InitForm( ) ;

}

void Dispose( )

{

// Form is being destroyed. Do any necessary clean-up here.

Form::Dispose( ) ;

}

void InitForm( )

{

Text = "Drawing Shapes";

System::Drawing::Size s ;

s.Height = 300 ;

s.Width = 350 ;

ClientSize = s ;

}

protected:

virtual void OnPaint ( PaintEventArgs* e )

{

Graphics *g = e->Graphics ;

Image *myimg = Image::FromFile ( "fcode.gif" ) ;

g -> DrawImage ( myimg, 20, 20 ) ;

Matrix *mymat = new Matrix( ) ;

mymat -> Translate ( myimg->Width + 20, myimg->Height+ 30);

mymat -> Scale ( -1.0, 1 ) ;

g -> Transform = mymat ;

g -> DrawImage( myimg, 0, 0 );

g -> ResetTransform( ) ;

}

} ;

By scaling the image with a factor of -1.0 in the x direction, what we get is a mirror image.

Your Title