Bare Bones IRC Bot In Perl.

0

Bare Bones IRC Bot In Perl.

          

This is a short guide to creating your own perl bot which will work on irc. I will not cover all the different modules and ways to connect to irc and issue commands. This will only cover connecting with IO::Socket and using raw irc commands. I feel you learn the most this way and have alot of control over what is happening.

IRC experience is helpful, but I'll take things slow enough so that an absolute beginner can understand what is taking place. This will also help those with alittle knowledge fully understand the irc protocol. Although I am no irc expert, after creating this bot I did learn a few tricks.

We start off by getting a connection underway:
#!/usr/bin/perl
use IO::Socket;

$sock = IO::Socket::INET->new(
 PeerAddr => 'irc.undernet.org', 
 PeerPort => 6667, 
 Proto => 'tcp' ) or die "could not make the connection";

You can use any irc server and any port (commonly used ports are 6667-7000), so long as they are valid. If you have problems try to find a different server on that network. To make things easier you can make the PeerAddr a variable which is specified by an argument from the command line. Or purhaps map out all the servers on the network and make an arry from them, connecting to random ones and using the best connection. There are many possibilities, each work best for certain situations. We'll stick to the simple hard coded address and port.

Now we have a connection to the server. We still need to get connected/logged in to the ircd. Anything we send to or recieve from the server will go through $sock. So lets see what the server is sending us after we make a connection.
while($line = <$sock>){
 print "$line\n";
}

We will see that the server prints out some lines. Each line will have a number representation to it. This will really help to tell the bot when to start and end routines. The key here is the line with 'NOTICE AUTH' in it. This is when we need to login to the irc server. To do this we send

NICK bots_nick
USER bots_ident 0 0 :bots name

With a line break after the bots_nick and a line break at the end. So in the while loop we will add something like this:
while($line = <$sock>){
 print $line;
 if($line =~ /(NOTICE AUTH).*(checking ident)/i){
  print $sock "NICK b0ilersbot\nUSER bot 0 0 :just a bot\n";
  last;
 }
}

Now we are done with the login process. If you are having any problems try to read up on the irc protocol and how to login to it with telnet. Raven from www.securitywriters.org has wrote a decent tutorial on the subject, look for it.

Some servers will ask for a ping to make sure the client is active. This is only done on some servers and is a common pitfall to many bots which don't support this kind of login proceedure. To handle this we will check if the server wants us to ping it. The server will ask for a ping before it asks about nickserv registration/identification, so we will stop this loop after it mentions nickserv. This is what those numbers in the last if statment are for, the 376|422. The way to identify to nickserv is like this

NICKSERV :identify nick_password

this is just a simple irc command. The command is 'NICKSERV' and the arguments are 'identify nick_password' where nick_password is the actual password for this nick. The line ends in a line break and all irc commands are in upper case. When there is a : before something it means it is a multiple word argument (has spaces in it). This is how we will handle the possible ping and the nickserv identification.
while($line = <$sock>){
 print $line;    
 #use next line if the server asks for a ping
 if($line =~ /^PING/){
  print $sock "PONG :" . (split(/ :/, $line))[1];
 }
 if($line =~ /(376|422)/i){
  print $sock "NICKSERV :identify nick_password\n";
  last;
 }
}

If you want to have a registration code you can find this out on your own.. or do what I do and register the nick with a normal irc client. This way we only need the bot to identify.

When you create your bot you can customize it however you want. Most of my bots have alittle bit more AI then this tutorial shows. This bot will be pretty strait forword and doesn't make many decisions. It just connects and does something.

I like to make the bot sleep for a few seconds just to get the connection cought up. I am on a 56k and things can go slow sometimes. A few times without the sleep the bot has joined channels before the nickserv identification is complete, this can be a pain in the neck if the bot needs a usermode or other circumstances which require the nick to be identified (such as other bots, +R channel mode, or trust issues with users).

After it sleeps it will join the channel. You will see that the server prints out alot of information about the channel when you join. You can save this information in variables to allow the bot to make many decisions. Again, this is a simple bot and won't be aware of it's environment or be dynamic in anyway. But you could for example turn on/off colors by what channel modes are set or who is in the channel (some people really hate colors). This is the last bit of the login proccess, after this the bot can actually do something.
sleep 3;
print $sock "JOIN #channel\n";

Notice there is no : before #channel. This is because it does not have any spaces in it. And the JOIN command is in all caps. For a full list of commands try reading a tutorial on the IRC protocol. I don't even cover the basics here, there are tons of useful to know commands.

Now we are joining the channel. There is nothing else to do besides read the messages users send to the channel and respond to them. But inorder to read the messages we need to parse them so they make sense. The format of a priv_msg is as follows:

:nick!ident@hostname.com PRIVMSG #channel :the line of text

I like to seperate them into the following variables to make things easier to keep track of.

:$nick!$hostname $type $channel :$text

in this example here is the values of the variables:

$nick = nick
$hostname = ident
$type = priv_msg
$channel = #channel
$text = the line of text

So we are going to need to parse what is send from the server into useable data. This is how we'll do it. There is only one twist here, and that is incase the server sends a ping. They do this quite often to check and see if you are still connected. If we don't reply the the pings then we will get disconnected. When the server sends a ping you must reply with a PONG and the same characters the ping had. So this is how we will send it
while ($line = <$sock>) {
 ($command, $text) = split(/ :/, $line);   #$text is the stuff from the ping or the text from the server
 
 if ($command eq 'PING'){
  #while there is a line break - many different ways to do this
  while ( (index($text,"\r") >= 0) || (index($text,"\n") >= 0) ){ chop($text); }
  print $sock "PONG $text\n";
  next;
 }
 #done with ping handling
 
 ($nick,$type,$channel) = split(/ /, $line); #split by spaces
 
 ($nick,$hostname) = split(/!/, $nick); #split by ! to get nick and hostname seperate
 
 $nick =~ s/://; #remove :'s
 $text =~ s/://;
 
 #get rid of all line breaks.  Again, many different way of doing this.
 $/ = "\r\n";
 while($text =~ m#$/$#){ chomp($text); }
        
 #end of parsing, now for actions
}

ok. That was a rather large chunk of code and some parts were rather confusing. Most of it is just getting rid of what we don't want and seperating what we do want into variables. The next bit is just for looks. We print out what is said as if this is a normal irc client.
if($channel eq '#channel'){
 print "<$nick> $text";
}

The $channel check is needed incase people priv_msg or notice you things. This can be a problem when dealing with bots which need to be secure or can cause large headaches when things go wrong. I'll leave dealing with multiple channels to you. But to send Notices you simply do: print $sock "NOTICE nick :the line of text here\n"; and to send a priv_msg you do: print $sock "PRIVMSG nick :the line of text here\n";

Now the bot structure is done. Everything required is done, the only thing left to do is custimize your bot to have it do what you want it to do. This can be almost any sort of task imaginable. Simply parse the $text $nick and other variables we created to have the bot make decisions on what to do.

Here is the final bot in whole. I added one bit just to prove that the bot works:
#!/usr/bin/perl
use IO::Socket;

$sock = IO::Socket::INET->new(
 PeerAddr => 'irc.undernet.org', 
 PeerPort => 6667, 
 Proto => 'tcp' ) or die "could not make the connection";
 
while($line = <$sock>){
 print $line;
 if($line =~ /(NOTICE AUTH).*(checking ident)/i){
  print $sock "NICK b0ilersbot\nUSER bot 0 0 :just a bot\n";
  last;
 }
}

while($line = <$sock>){
 print $line;    
 #use next line if the server asks for a ping
 if($line =~ /^PING/){
  print $sock "PONG :" . (split(/ :/, $line))[1];
 }
 if($line =~ /(376|422)/i){
  print $sock "NICKSERV :identify nick_password\n";
  last;
 }
}

sleep 3;
print $sock "JOIN #channel\n";

while ($line = <$sock>) {
 ($command, $text) = split(/ :/, $line);   #$text is the stuff from the ping or the text from the server
 
 if ($command eq 'PING'){
  #while there is a line break - many different ways to do this
  while ( (index($text,"\r") >= 0) || (index($text,"\n") >= 0) ){ chop($text); }
  print $sock "PONG $text\n";
  next;
 }
 #done with ping handling
 
 ($nick,$type,$channel) = split(/ /, $line); #split by spaces
 
 ($nick,$hostname) = split(/!/, $nick); #split by ! to get nick and hostname seperate
 
 $nick =~ s/://; #remove :'s
 $text =~ s/://;
 
 #get rid of all line breaks.  Again, many different way of doing this.
 $/ = "\r\n";
 while($text =~ m#$/$#){ chomp($text); }
 
 if($channel eq '#channel'){
  print "<$nick> $text";
  
  if($text =~ /hi b0ilerbot/gi){
   print $sock "PRIVMSG #channel :hi $nick\n";
  }
 }
}

Not very complicated once you look at each part of it. But finding out things for yourself is the real fun of creating a bot. Much trial and error is involved in perfecting the bot, adding security and function can be alot of fun. I would like to stress the security of irc bots. They are in the most hostile environment known to the net and one security mistake and your bot could be used to execute commands on your box. I have found 4 irc perl bots vulnerable to remote command execution, don't let me find yours vulnerable aswell! Read all of the perl security related tutorials at Don't let this discurage you from coding your own bot, it's a great learning experience and as long as you are careful you should be fairly safe. I would love to hear what kind of bots you come up with. The bots I have created include:

quote bot  -  a bot which has many features that deal with irc quotes. it reads off funny/witty things people have said while chatting in my channels. It also has some more advanced features such as listing off all the users in the channel who have a quote and an admin feature which allows me to add quotes while the bot is running.

quiz bot  -  A bot which quizes the channel users. I used this while studying for networking. This bot is great when the channel is dead or to start up a conversation with others. I learned alot from this bot.

poker bot  -  A bot which plays poker. I started to make a ucker (sp?) bot, but I lost motivation when the other people who wantted to play quit going on irc.

channel bot  -  A bot which enforces the channel rules. it warns, kicks, and kick bans users for breaking the rules. it voices,half ops, and ops identified users and keeps stats of channel activity. Good for preventing channel takeovers.

The reason for creating this text was because I remember the stress I had finding info on this subject when I first created the bot. I have since read a few crappy papers on irc bots, but nothing which would be very helpful.

Basic Socket programming in C

0
 BASIC C Socket Programming In Unix For Newbies

<=================================================>



Written by: HackerVinoth

E-mail    : jvkumar007@gmail.com















CONTENTS

=======================================



1. Introduction

2. Different types of Internet Sockets

3. Structures

4. Conversions

5. IP Addresses

6. Important Functions



   6.1. socket()

   6.2. bind()

   6.3. connect()

   6.4. listen()

   6.5. accept()

   6.6. send()

   6.7. recv()

   6.8. sendto()

   6.9. recvfrom()

   6.10. close()

   6.11. shutdown()

   6.12. gethostname()



7. Some words about DNS



8. A Stream Server Example



9. A Stream Client Example



10. Last Words



11. Copyright







1. INTRODUCTION

=======================================



Are you trying to learn c socket programming? Or do you think that it's hard stuff?

Well, then you must read this basic tutorial to get basic ideas and concepts and to start

to work with sockets. Don't expect to be a "socket programming master" after reading this

tutorial. You'll only be that if you practice and read a lot.







2. DIFFERENT TYPES OF INTERNET SOCKETS

=======================================



In the first place I must explain what a socket is. In a very simple way, a socket is a way

to talk to other computer. To be more precise, it's a way to talk to other computers using

standard Unix file descriptors. In Unix, every I/O actions are done by writing or reading

to a file descriptor. A file descriptor is just an integer associated with an open file and it

can be a network connection, a terminal, or something else.



About the different types of internet sockets, there are many types but I'll just describe two

of them - Stream Sockets (SOCK_STREAM) and Datagram Sockets (SOCK_DGRAM).



"And what are the differences between this two types?" you may ask. Here's the answer:



        Stream Sockets - they're error free; if you send through the stream socket three

             items "A,B,C", they will arrive in the same order - "A,B,C" ;

             they use TCP ("Transmission Control Protocol") - this protocol

             assures the items' order.

     

      Datagram Sockets - they use UDP ("User Datagram Protocol"); they're connectionless

             because you don't need to have an open connection as in Stream

             Sockets - you build a packet with the destination information and

             send it out.



A lot more could be explained here about this two kind of sockets, but I think this is enough

to get the basic concept of socket. Understanding what a socket is and this two types of

internet sockets is a good start, but you need to learn how to "work" with them. You'll learn

it in the next sections.





3. STRUCTURES

=======================================



The purpose of this section is not to teach you structures but to tell you how are they

used in C socket programming. If you don't know what a structure is, my advice is to read

a C Tutorial and learn it. For the moment, let's just say that a structure is a data type

that is an aggregate, that is, it contains other data types, which are grouped together into

a single user-defined type.



Structures are used in socket programming to hold information about the address.





The first structure is struct sockaddr that holds socket information.







    struct sockaddr{

        unsigned short  sa_family;    /* address family */

        char            sa_data[14];  /* 14 bytes of protocol address */

    };







But, there's another structure (struct sockaddr_in) that help you to reference to the socket's

elements.







    struct sockaddr_in {

        short int         sin_family;  /* Address family */

        unsigned short int   sin_port;      /* Port */

        struct in_addr         sin_addr;      /* Internet Address */

        unsigned char         sin_zero[8]; /* Same size as struct sockaddr */

    };







Note: sin_zero is set to all zeros with memset() or bzero() (See examples bellow).







The next structure is not very used but it is defined as an union.



As you can see in both examples bellow (Stream Client and Server Client) , when I declare for

example "client" to be of type sockaddr_in then I do client.sin_addr = (...)



Here's the structure anyway:





    struct in_addr {

        unsigned long s_addr;

    };







Finally, I think it's better talk about struct hostent. In the Stream Client Example, you can

see that I use this structure. This structure is used to get remote host information.



Here it is:





    struct hostent

    {

      char *h_name;                 /* Official name of host.  */

      char **h_aliases;             /* Alias list.  */

      int h_addrtype;               /* Host address type.  */

      int h_length;                 /* Length of address.  */

      char **h_addr_list;           /* List of addresses from name server.  */

    #define h_addr  h_addr_list[0]  /* Address, for backward compatibility.  */

    };



This structure is defined in header file netdb.h.





In the beginning, this structures will confuse you a lot, but after you start to write some

lines, and after seeing the examples, it will be easier for you understanding them. To see

how you can use them check the examples (section 8 and 9).







4. CONVERSIONS

=======================================



There are two types of byte ordering: most significant byte and least significant byte.

This former is called "Network Byte Order" and some machines store their numbers internally

in Network Byte Order.



There are two types you can convert: short and long.

Imagine you want to convert a long from Host Byte Order to Network Byte Order. What would you

do? There's a function called htonl() that would convert it =) The following functions are

used to convert :



    htons() -> "Host to Network Short"

    htonl()    -> "Host to Network Long"

    ntohs()    -> "Network to Host Short"

    ntohl() -> "Network to Host Long"



You must be thinking why do you need this. Well, when you finish reading this document, it will

all seems easier =) All you need is to read and a lot of practice =)



An important thing, is that sin_addr and sin_port (from struct sockaddr_in) must be in Network

Byte Order (you'll see in the examples the functions described here to convert and you'll start

to understand it).



5. IP ADRESSES

=======================================



In C, there are some functions that will help you manipulating IP addresses. We'll talk about

inet_addr() and inet_ntoa() functions.





    inet_addr() converts an IP address into an unsigned long. An example:

   

    (...)



    dest.sin_addr.s_addr = inet_addr("195.65.36.12");



    (...)



    /*Remember that this is if you've a struct dest of type sockaddr_in*/







    inet_ntoa() converts string IP addresses to long. An example:



    (...)



    char *IP;



    ip=inet_ntoa(dest.sin_addr);



    printf("Address is: %s\n",ip);



    (...)



Remember that inet_addr() returns the address in Network Byte Order - so you don't need to

call htonl().





6. IMPORTANT FUNCTIONS

=======================================



In this section I'll put the function' syntax, the header files you must include to call it,

and little comments. Besides the functions mentioned in this document, there are more, but

I decided to put only these ones here. Maybe I'll put them in a future version of this

document =) To see examples of these functions, you can check the stream client and stream

server source code (Sections 8 and 9)





  6.1. socket()

  =============

   

    #include

    #include



    int socket(int domain,int type,int protocol);





    Let's see the arguments:



        domain   -> you can set "AF_INET" (set AF_INET to use ARPA internet protocols)

                or "AF_UNIX" if you want to create sockets for inside comunication.

                Those two are the most used, but don't think that there are just

                  those. There are more I just don't mention them.

        type     -> here you put the kind of socket you want (Stream or Datagram)

                  If you want Stream Socket the type must be SOCK_STREAM

                  If you want Datagram Socket the type must be SOCK_DGRAM

        protocol -> you can just set protocol to 0





    socket() gives you a socket descriptor that you can use in later system calls or

    it gives you -1 on error (this is usefull for error checking routines).





  6.2. bind()

  ===========



    #include

    #include



    int bind(int fd, struct sockaddr *my_addr,int addrlen);



   

    Let's see the arguments:



        fd  -> is the socket file descriptor returned by socket() call

        my_addr -> is a pointer to struct sockaddr

        addrlen -> set it to sizeof(struct sockaddr)



    bind() is used when you care about your local port (usually when you use listen() )

    and its function is to associate a socket with a port (on your machine). It returns

    -1 on error.



    You can put your IP address and your port automatically:



        server.sin_port = 0;                /* bind() will choose a random port*/

        server.sin_addr.s_addr = INADDR_ANY;  /* puts server's IP automatically */





    An important aspect about ports and bind() is that all ports bellow 1024 are reserved.

    You can set a  port above 1024 and bellow 65535 (unless the ones being used by other

    programs).





  6.3. connect()

  ==============



    #include

    #include



    int connect(int fd, struct sockaddr *serv_addr, int addrlen);



    Let's see the arguments:



        fd    -> is the socket file descriptor returned by socket() call

        serv_addr -> is a pointer to struct sockaddr that contains destination IP

                 address and port

        addrlen   -> set it to sizeof(struct sockaddr)



    connect() is used to connect to an IP address on a defined port. It returns -1 on

    error.





  6.4. listen()

  =============



    #include

    #include



    int listen(int fd,int backlog);



    Let's see the arguments:



        fd  -> is the socket file descriptor returned by socket() call

        backlog -> is the number of allowed connections



    listen() is used if you're waiting for incoming connections, this is, if you want

    someone to connect to your machine. Before calling listen(), you must call bind()

    or you'll be listening on a random port =) After calling listen() you must call

    accept() in order to accept incoming connection. Resuming, the sequence of system calls

    is:



        1. socket()

        2. bind()

        3. listen()

        4. accept() /* In the next section I'll explain how to use accept() */



    As all functions above described, listen() returns -1 on error.





  6.5. accept()

  =============



    #include



    int accept(int fd, void *addr, int *addrlen);



    Let's see the arguments:



        fd  -> is the socket file descriptor returned by listen() call

        addr    -> is a pointer to struct sockaddr_in where you can determine which host

               is calling you from which port

        addrlen -> set it to sizeof(struct sockaddr_in) before its address is passed

               to accept()



   

    When someone is trying to connect to your computer, you must use accept() to get the

    connection. It's very simple to understand: you just get a connection if you accept =)

   



    Next, I'll give you a little example of accept() use because it's a little different

    from other functions.



    (...)



      sin_size=sizeof(struct sockaddr_in);

      if ((fd2 = accept(fd,(struct sockaddr *)&client,&sin_size))==-1){ /* calls accept() */

        printf("accept() error\n");

        exit(-1);

      }



    (...)



    From now on, fd2 will be used for add send() and recv() calls.





  6.6. send()

  ===========



   

    int send(int fd,const void *msg,int len,int flags);



    Let's see the arguments:



        fd -> is the socket descriptor where you want to send data to

        msg    -> is a pointer to the data you want to send

        len    -> is the length of the data you want to send (in bytes)

        flags  -> set it to 0





    This function is used to send data over stream sockets or CONNECTED datagram sockets.

    If you want to send data over UNCONNECTED datagram sockets you must use sendto().

   

    send() returns the number of bytes sent out and it will return -1 on error.





  6.7. recv()

  ===========





    int recv(int fd, void *buf, int len, unsigned int flags);



    Let's see the arguments:



        fd  -> is the socket descriptor to read from

        buf    -> is the buffer to read the information into

        len     -> is the maximum length of the buffer

        flags     -> set it to 0





    As I said above about send(), this function is used to send data over stream sockets or

    CONNECTED datagram sockets. If you want to send data over UNCONNECTED datagram sockets

    you must use recvfrom().



    recv() returns the number of bytes read into the buffer and it'll return -1 on error.





  6.8. sendto()

  =============



    int sendto(int fd,const void *msg, int len, unsigned int flags,

           const struct sockaddr *to, int tolen);



    Let's see the arguments:



        fd  -> the same as send()

        msg     -> the same as send()

        len    -> the same as send()

        flags    -> the same as send()

        to    -> is a pointer to struct sockaddr

        tolen    -> set it to sizeof(struct sockaddr)



    As you can see, sendto() is just like send(). It has only two more arguments : "to"

    and "tolen" =)



    sendto() is used for UNCONNECTED datagram sockets and it returns the number of bytes

    sent out and it will return -1 on error.





  6.9. recvfrom()

  ===============



    int recvfrom(int fd,void *buf, int len, unsigned int flags

             struct sockaddr *from, int *fromlen);



    Let's see the arguments:



        fd    -> the same as recv()

        buf    -> the same as recv()

        len    -> the same as recv()

        flags    -> the same as recv()

        from    -> is a pointer to struct sockaddr

        fromlen    -> is a pointer to a local int that should be initialised

               to sizeof(struct sockaddr)



    recvfrom() returns the number of bytes received and it'll return -1 on error.





  6.10. close()

  =============



    close(fd);



    close() is used to close the connection on your socket descriptor. If you call close(),

    it won't be no more writes or reads and if someone tries to read/write will receive an

    error.





  6.11. shutdown()

  ================



    int shutdown(int fd, int how);



    Let's see the arguments:



        fd    -> is the socket file descriptor you want to shutdown

        how    -> you put one of those numbers:

               

                    0 -> receives disallowed

                    1 -> sends disallowed

                    2 -> sends and receives disallowed



    When how is set to 2, it's the same thing as close().



    shutdown() returns 0 on success and -1 on error.





  6.12. gethostname()

  ===================



    #include



    int gethostname(char *hostname, size_t size);



    Let's see the arguments:



        hostname  -> is a pointer to an array that contains hostname

        size       -> length of the hostname array (in bytes)



   

    gethostname() is used to get the name of the local machine.







7. SOME WORDS ABOUT DNS

=======================================



I created this section, because I think you should know what DNS is. DNS stands for "Domain

Name Service" and basically, it's used to get IP addresses. For example, I need to know the

IP address of queima.ptlink.net and through DNS I'll get 212.13.37.13 .



This is important, because functions we saw above (like bind() and connect()) work with IP

addresses.



To see you how you can get queima.ptlink.net IP address on c, I made a little example:





/* <---- SOURCE CODE STARTS HERE ----> */



#include

#include    /* This is the header file needed for gethostbyname() */

#include

#include

#include





int main(int argc, char *argv[])

{

  struct hostent *he;



  if (argc!=2){

     printf("Usage: %s \n",argv[0]);

     exit(-1);

  }



  if ((he=gethostbyname(argv[1]))==NULL){

     printf("gethostbyname() error\n");

     exit(-1);

  }



  printf("Hostname : %s\n",he->h_name);  /* prints the hostname */

  printf("IP Address: %s\n",inet_ntoa(*((struct in_addr *)he->h_addr))); /* prints IP address */

}

/* <---- SOURCE CODE ENDS HERE ----> */







8. A STREAM SERVER EXAMPLE

=======================================



In this section, I'll show you a nice example of a stream server. The source code is all

commented so that you ain't no possible doubts =)



Let's start:



/* <---- SOURCE CODE STARTS HERE ----> */



#include           /* These are the usual header files */

#include

#include

#include





#define PORT 3550   /* Port that will be opened */

#define BACKLOG 2   /* Number of allowed connections */



main()

{



  int fd, fd2; /* file descriptors */



  struct sockaddr_in server; /* server's address information */

  struct sockaddr_in client; /* client's address information */



  int sin_size;





  if ((fd=socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  /* calls socket() */

    printf("socket() error\n");

    exit(-1);

  }



  server.sin_family = AF_INET;        

  server.sin_port = htons(PORT);   /* Remember htons() from "Conversions" section? =) */

  server.sin_addr.s_addr = INADDR_ANY;  /* INADDR_ANY puts your IP address automatically */  

  bzero(&(server.sin_zero),8); /* zero the rest of the structure */



 

  if(bind(fd,(struct sockaddr*)&server,sizeof(struct sockaddr))==-1){ /* calls bind() */

      printf("bind() error\n");

      exit(-1);

  }    



  if(listen(fd,BACKLOG) == -1){  /* calls listen() */

      printf("listen() error\n");

      exit(-1);

  }



while(1){

  sin_size=sizeof(struct sockaddr_in);

  if ((fd2 = accept(fd,(struct sockaddr *)&client,&sin_size))==-1){ /* calls accept() */

    printf("accept() error\n");

    exit(-1);

  }

 

  printf("You got a connection from %s\n",inet_ntoa(client.sin_addr) ); /* prints client's IP */

 

  send(fd2,"Welcome to my server.\n",22,0); /* send to the client welcome message */

 

  close(fd2); /*  close fd2 */

}

}



/* <---- SOURCE CODE ENDS HERE ----> */







9. A STREAM CLIENT EXAMPLE

=======================================



/* <---- SOURCE CODE STARTS HERE ----> */



#include

#include

#include

#include

#include         /* netbd.h is needed for struct hostent =) */



#define PORT 3550      /* Open Port on Remote Host */

#define MAXDATASIZE 100   /* Max number of bytes of data */



int main(int argc, char *argv[])

{

  int fd, numbytes;      /* files descriptors */

  char buf[MAXDATASIZE];  /* buf will store received text */

 

  struct hostent *he;         /* structure that will get information about remote host */

  struct sockaddr_in server;  /* server's address information */



  if (argc !=2) {          /* this is used because our program will need one argument (IP) */

    printf("Usage: %s \n",argv[0]);

    exit(-1);

  }



  if ((he=gethostbyname(argv[1]))==NULL){    /* calls gethostbyname() */

    printf("gethostbyname() error\n");

    exit(-1);

  }



  if ((fd=socket(AF_INET, SOCK_STREAM, 0))==-1){  /* calls socket() */

    printf("socket() error\n");

    exit(-1);

  }



  server.sin_family = AF_INET;

  server.sin_port = htons(PORT); /* htons() is needed again */

  server.sin_addr = *((struct in_addr *)he->h_addr);  /*he->h_addr passes "*he"'s info to "h_addr" */

  bzero(&(server.sin_zero),8);



  if(connect(fd, (struct sockaddr *)&server,sizeof(struct sockaddr))==-1){ /* calls connect() */

    printf("connect() error\n");

    exit(-1);

  }



  if ((numbytes=recv(fd,buf,MAXDATASIZE,0)) == -1){  /* calls recv() */

    printf("recv() error\n");

    exit(-1);

  }



      buf[numbytes]='\0';



      printf("Server Message: %s\n",buf); /* it prints server's welcome message =) */



      close(fd);   /* close fd =) */

}



/* <---- SOURCE CODE ENDS HERE ----> */







10. LAST WORDS

=======================================





As I'm just a simple human, it's almost certain that there are some errors on this document.

When I say errors I mean English errors (because my language is not the English) but also

technical errors. Please email me if you detect any error =)



But you must understand that this is the first version of this document, so , it's natural not

to be very complete (as matter of fact I think it is ) and it's also very natural to have

stupid errors. However, I can be sure that source code presented in this document works fine.





If you need help concerning this subject you can email me at





SPECIAL THANKS TO: Ghost_Rider (my good old mate), Raven (for letting me write this tutorial)

           and all my friends =)







11. COPYRIGHT

=======================================



All copyrights are reserved. You can distribute this tutorial freely, as long you don't change

any name or URL. You can't change a line or two, or add another lines, and then claim that this

tutorial is yours. If you want to change something, please email me at :jvkumar007@gmail.com

Database in PHP

0

Using Databases In PHP (ver. 1.0.0)
by: HackerVinoth

Introduction:

This isn't quite a full blown tutorial, I like to think of it as a mini-tutorial. This “mini-tutorial” will cover some of the most commonly used functions. There won't be a background section; we're going to go straight to work. Today, I'm going to discuss (if the title didn't give it away) connecting to and using a database with php. The database will be MySQL because it's just sooo darn popular.

Down To Work:

The first thing we need to do is connect to the database.
mysql_connect("somehost", "username", "password") or die ("Can't connect!");
This will try to connect to the database on somehost and login with “username” as the username and “password” as the password. If it can't, it will output an error message saying that it can't connect. For your own code be sure to change somehost to your host (most of the times it's localhost, ask your admin), username to your username (duh), and password to your password. Another way to connect to a database is to open a persistent connection. To do this, use the mysql_pconnect function and pass it the same arguments as mysql_connect. Why open a persistent connection? When you call mysql_pconnect, instead of going out and opening a connection to the database, it sees if one is already open, if it is, the script will use it. Also, when the script has finished executing, the connection to the database will not automatically be closed like it is when using mysql_connect. This way the connection can be used later on. Using a persistent connection is a good idea if your scripts constantly need to connect to the database.
After we have opened a connection to the database, we then select a database.
mysql_select_db("database_name") or die("Can't select database!");
This will try to select the database named “database_name” (for your own code change it to the name of your database). If it can't select the database, it will output and error. Once you're actually connected to a database, you will want to query a table in the database to get whatever you want done. A query looks like this:
mysql_query("Some query");
Common queries are SELECT and INSERT For full documentation go to the mysql web site (http://www.mysql.com). Another common php function is mysql_num_rows; if it isn't obvious this gets the number of rows from a query. Here is an example of how it can be used with mysql_query:
$result= mysql_query("SELECT * FROM some_table");

  $number_of_rows= @mysql_num_rows($result);

      

  if ($number_of_rows == 0)

  {

    echo "Sorry there are no rows";

  }

  else {

    echo "Yes! we found some rows!";

  } 

?>
Now you may be wondering why I put the @ sign before mysql_num_rows. In php, the @ sign suppress errors; I put it in front of mysql_num_rows so that if there are no rows, MySQL will not output a bunch of errors. So when would mysql_num_rows be useful? Well, you could use it for an authentication script which searchs the database for a username and password and if it doesn't find any (i.e. if no rows are returned), it tells the user that the username, or password, are not correct.
Another really useful function is mysql_fetch_array, because it gets the rows and puts them in an array that contains the name of the rows. That way instead of having to access each row by number you can do it by name! For example, let's say that our database looked like this:
User Password
John afasdfadsfdsf
Billy tla;jrjealjwqsldajf
Mitch pqrtupipripewir
We would use the following code to get the users' names and output them:
echo "The users in this database are: 
";  

  $result= mysql_query("SELECT * FROM some_table");

      

  while ($row= mysql_fetch_array($result))

  {

    $username= $row["User"]; 

    echo "$username
";

  } 

?>
This will output all the usernames in a database; you can add error checking if you like. The while statement is read “while there are rows that satisfy the query, put the contents of the row from the column ‘User’ into the variable ‘username,’ and print the usernames (each on a new line) to an HTML page.”
Now let's cover a couple of functions that actually work with the database. The first is mysql_create_db, don't you just love how the functions are named you can figure out what they do just by looking at the function name, this one obviously creates a database. Here's how to use it:
echo "I am going to try to create a database...
";

      

  if (mysql_create_db("test_database"))

  {

    echo "Hooray, I've created the database!
";

  }

  else { 

    echo "Darn couldn't create the database! because: ";

    echo "mysql_error() 
";

  }

?>
You can see I used a new function, mysql_error, you don't really need to know too much about it, all it does is return the error string sent by MySQL. Now since we learned how to create a database, how's about we learn to delete one. To do that use the mysql_drop_db, here is how to use it:
echo "I am going to try to delete a database...
";

  $result= mysql_drop_db("test_database");

      

  if (!$result)

  {

    echo "Darn couldn't I couldn't delete the database!
";                   

  }

  else { 

    echo "Hooray, I've deleted the database
";   

  }

?>
You can see that the syntax is very similar to that of mysql_create_db, just pop the name of the database you want to delete into the function.
The next two items aren't functions, rather they are queries that you can use to manage an existing table. The following query will insert data into a database:
echo "I am going to try to insert data into a table...
";

  $result= mysql_query("INSERT INTO test_database (username, password) VALUES 

                (Rahim, adfjaldadfsdaf)");

      

  if (!$result)

  {

    echo "Darn couldn't I couldn't delete the database!
"; 

  }

  else { 

    echo "Hooray, I've deleted the database
";     

  }

?>
This query should be pretty obvious, it inserts the data defined in between the parentheses into the rows. Just a little note to remember, the order in which you write out the column names is the order your data will be entered (i.e. a row with the contents Rahim will be entered under username, not password since we wrote username then pasword, if it was reveresd Rahim would be put under password).
The next query we've already gone over, I'm just going to add to it; after I'm done you should be able to use it to help create a simple search engine (upcomming tutorial)! For the sake of brevity I'll remove all the extra php stuff and just show you the “meat” of the code.
$result= mysql_query("SELECT name FROM some_table WHERE name=Joe AND 

         lastname=Sixpack OR lastname=Becker ORDER BY lastname LIMIT 20");
Now I know that looks like a long query, but it's not really all that bad. What it's pretty much saying is: “Get me the name from some_table where the name is Joe and the lastname is Sixpack or Becker, oh and by the way while your at it, put it in alphabetical order by the lastname; oh and one last thing, just get the first 20 results please.” MySQL has lots of other filters that you can add on to the SELECT statement, I highly suggest you download the MySQL documentation and give it a perusing.

Conclusion:

Well that wraps up this “mini-tutorial”, you should use this as a quick reference for my other (upcomming) tutorials. If you found any errors or have any comments please e-mail me (jvkumar007@gmail.com), kindly direct questions to the message board.

Windows programming

0
Windows Programming Tutorial
By: HackerVinoth


Contents


  • 1. Basics
    • 1.1 Introduction
    • 1.2 Simple Message
    • 1.3 Simple Window
    • 1.4 Text
    • 1.5 Menus
      • 1.5.1 Resources
      • 1.5.2 On The Spot
    • 1.6 Dialogs
    • 1.7 Controls
  • 2. Intermediate
    • 2.1 Bitmaps
      • 2.1.1 Resource
      • 2.1.2 From a File
    • 2.2 System Tray
    • 2.3 Toolbars
      • 2.3.1 Custom Buttons
      • 2.3.2 Common Buttons
    • 2.4 Statusbars
  • Appendix
    • A The Compiler
    • B Tools
    • C Links
    • D Shoutouts

1. Basics

1.1 Introduction

First off if you are reading this tutorial, I am going to assume a few things. Because windows programming is "more advanced" than console programming I am going to have to assume you have a good grasp on C/C++. You need to know what #include's are and how to use them, you need to know how to use arrays and pointers, you need to know how to use switch() and case's, and you need to know what a typedef is. These are things this tutorial will not cover. I will format my code to my style, this to make it easier for me and you to read.
You are also going to need a C/C++ Compiler that supports Windows API. I used Borlands Free C/C++ 5.5 Compiler to compile all of these examples, basically because I am to cheap to get Microsoft Visual C++ and Borland is free alternative. For information on getting a free compiler see the Tools Appendix. For information on how to compile windows programs with the compiler see the Compiler Section.
I like to look at the code and compiled examples before I go over what it all means so after each example I will go over the code to clarify what it means.
1.2 Simple Message
Source - Screen Shot
This first example is here just to make sure that your compiler does support windows.

#include

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        MessageBox (NULL, "Hello World" , "Hello", 0);
        return 0;
}


Put that code in a test file and compile it, any errors you get consult your compilers help files. Now lets go through the code.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

WinMain() is the windows equivalent of main() in DOS and UNIX. This is where the program starts. WinMain() takes the following parameters:


hInstance
Identifies the programs current instance (the programs place in the memory)
hPrevInstance
Identifies the previous instance of the application. For a Win32-based application, this parameter is always NULL.
lpCmdLine
All of the command-line arguments in a string (does not include the program name)
nCmdShow
A value that can be passed to ShowWindow()


Many of the keywords and types have windows specific definitions, like UINT is unsigned int and LPSTR is char *, this is intended to increase portability. If you are more comfortable using char * instead of LPSTR, feel free to do so.

MessageBox() is the function that pops up a message box, hence the name. MessageBox() takes the following parameters:


hWnd
Identifies the owner window of the message box to be created. If this parameter is NULL, the message box has no owner window.

lpText


This is the text contained in the window.

lpCaption


This is the text contained in the titlebar of the window.

uType


This is the style of the message box (like if its an error, warning, etc. and what buttons should appear. Setting this to 0 will add no icon and just a Ok button)
1.3 Simple Window
Source - Screen Shot
On of the first questions that plagues a new windows programmer is &quotHow Do I make A Window?" Well I am afraid that the answer is not entirely simple. Here is the source for a simple window.

#include

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        switch(Message) {
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


As you can see its a good 70 or so lines to make a simple window.

Windows Programs unlike DOS or UNIX programs are event driven. Windows programs generaly stay idle untill they recive a message, they act on that message, and then wait for the next one. When a windows program does respond to a message its called handling the message.

As you see I decalred my WiinProc function so that it can be used later on. I decalerd my variables (I prfer to use Hungarian Notation, but you can use what you want).

Inside the function WiNmain() we see some new variables. These are WndClass, hwnd, and msg. The WndClass is the structure that holds all of the widnows class information which is later passed to RegisterClassEx(). The hwnd structure identifies the window. The msg structure contains message information from a thread's message queue. The next thing we come to is when the hInstance is made global in the variable ghInstance. After that we come to the defining of each of the WndClass settings:


cbSize
Is the size (in bytes) of this structure. Always set this to sizeof(WNDCLASSEX).
style
The Class style sof the window. Usually net to NULL.
lpfnWndProc
This points to the windows callback procedure.
cbClsExtra
Amount of extra data allocated for this class in memory. Usually 0.
cbWndExtra
Amount of extra data allocated in memory per window. Usually 0.
hInstance
This is the handle for the window instance.
hIcon
The icon shown when the user presses Alt-Tab. We will worry about this more when we get into resources.
hCursor
The cursor that will be displayed when the mouse is over our window. We will worry about this more when we get into resources.
hbrBackground
The brush to set the color of our window.
lpszMenuName
Name of the menu resource to use. We will worry about this more when we get into resources.
lpszClassName
Name to identify the class with.
hIconSm
The small icon shown in the taskbar and in the top-left corner. We will worry about this more when we get into resources.
After that the next thing we do is register our window class. The program registers it and if it tell the user in an message box. Then we have the defineation of hwnd. This is what we will later pass on to ShowWindow(). Here are what parameters CreateWindowEx() takes:


dwExStyle
This tells it what style of window we want (In the example I used a plain static window)
lpClassName
This points to our class name we came up with earlier.
lpWindowName
This is the text that will apear in the title bar of out window.
dwStyle
This tells tells what style of window we are creating.
x
The inital horizontal starting position of the window (Set this to CW_USEDEFAULT if you want windows to pick a place)
y
The inital verticle starting position of the window (Set this to CW_USEDEFAULT if you want windows to pick a place)
nWidth
The width of the window (In pixels)
nHeight
The heighth of the window (In pixels)
hWndParent
The handle of the parent window (If one does not exist this is NULL)
hMenu
I dentifies a menu for the window (Only appleys if its a child window)
hInstance
Points to the hInstance of the window.
lpParam
Points to a value passed to the window through the CREATESTRUCT structure (I have never found a use for this, but I am sure one exists)
ShowWindow() sets the specified window's show state. UpdateWindow() updates the client area of the specified window by sending a WM_PAINT message to the window. The while loop will set our program to loop untill the WM_QUIT Message is recived. TranslateMessage() translates virtual-key messages into character messages. DispatchMessage() dispatches a message to a window procedure.

Now we get to probably the most important part of the program. The CallBack Procedure. What this is is a function with a giant switch() statment to switch off what each message should do (Sometimes this is a bunch of if's). Our callback takes the following parameters:


hwnd
Identifies the window.
uMsg
Specifies the message.
wParam
Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.
lParam
Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.
In our switch statment we see the first case is WM_CLOSE. This message tells us our use tried clicking the X in the corner or they press Alt-F4. Here we can prompt them to save or any other action we need to do. Then we call DestroyWindow(hwnd). Destroy Window takes one parameter:


hWnd
Identifies the window to be destroyed.
DestroyWindow() will sent the WM_DESTROY Message to our program, here we call PostQuitMessage(0). PostQuitMessage() takes one parameter:


nExitCode
Specifies an application exit code. This value is used as the wParam parameter of the WM_QUIT message.
PostQuitMessage() Will then send out the message WM_QUIT, but we will never get this message. WM_QUIT will cause the loop to return 0 and to exit the loop and also close our application. 1.4 Text
Source - Screen Shot
Now most windows programmers (including myself) never memorize much of that. We usually take a simple window application like that and use it as a skelaton for all of our new applications, adding what we need and changning things. This is what I am going to do throughout the entire tutorial. I will take this code form the last section and I will reuse it in all outher applications that have a window. Now next up how do we write text to our window? This to is not as simple as it may seem.

#include

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        HDC hdc;
        PAINTSTRUCT ps;
        LPSTR szMessage = "Hello World";

        switch(Message) {
                case WM_PAINT:
                        hdc = BeginPaint(hwnd, &ps);
                        TextOut(hdc, 70, 50, szMessage, strlen(szMessage));
                        EndPaint(hwnd, &ps);
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


This prints Hello World out on the screen, not very interesting I know, but we are learning. First to exapling what I added. At the top of the WinProc I added a few variables. hdc Identifies the display DC to be used for painting. ps is our paint structure the PAINTSTRUCT structure contains information for an application. This information can be used to paint the client area of a window owned by that application. message is just a simple character array that hold our message.

After that we can see i also added another case. Remember when I said above in Section 1.3 how the UpdateWidnow() send the message WM_PAINT, well here we can use it. First we tell hdc to recive painting information from BeginPaint(). BeginPaint() takes the following parameters:


hwnd
Identifies the window to be painted.
pPaint
Pointer to the PAINTSTRUCT structure that will receive painting information.
Then we use TextOut() to print text to the screen. TextOut() takes the following parameters:


hdc
Identifies the device context.
nXStart
Specifies the logical x-coordinate of the reference point that Windows uses to align the string.
nYStart
Specifies the logical y-coordinate of the reference point that Windows uses to align the string.
lpString
Points to the string to be printe dout (The string does not have to be \0 terminated, because cbString is the length)
cbString
Specifies the number of characters in the string.
Then EndPaint() tells the program to stop painting the screen. EndPaint() takes the following parameters:


hwnd
Identifies the window that was painted.
lpPaint
Points to a PAINTSTRUCT structure that contains the painting information retrieved by BeginPaint.
1.5 Menus

When programming there are usually a few ways to do things, making a windows menu is no different. I am going to go over the two main ways you can make a menu. I will have each one make the same menu but it will show you different ways of doing things.
1.5.1 Resources
Source - Screen Shot
Resources is probably the simplest way of making a menu. The menu is predefined in a resource file (always something.rc). The resource files are compiled by the resource compiler and linked to the program when the linker runs. This is the first program where I am also going to throw other files besides the source files in. There will be a resource file (*.rc) and a header file (*.h) in the zip along with a make file. Type make when you are in the directory of the source and Borlands should compile and link it all in the right order.

#define ID_FILE_NEW           1000
#define ID_FILE_OPEN          1001
#define ID_FILE_SAVE          1002
#define ID_FILE_EXIT          1003
#define ID_DO_SOMETHING       1004
#define ID_DO_SOMETHING_ELSE  1005
#define ID_HELP_ABOUT         1006


That is our header file. Here we just define all of the id's we are going to use so that we can just include this in both the resource file and the source file.

#include "section_1_5_1.h"

ID_MENU MENU DISCARDABLE
BEGIN
        POPUP "&File"
        BEGIN
                MENUITEM "&New",             ID_FILE_NEW
                MENUITEM "&Open",            ID_FILE_OPEN
                MENUITEM "&Save",            ID_FILE_SAVE
                MENUITEM SEPARATOR
                MENUITEM "E&xit",            ID_FILE_EXIT
        END
        POPUP "&Do"
        BEGIN
                MENUITEM "&Something",       ID_DO_SOMETHING
                MENUITEM "Something &Else",  ID_DO_SOMETHING_ELSE
        END
        POPUP "&Help"
        BEGIN
                MENUITEM "&About",           ID_HELP_ABOUT
        END
END


This is our resource file. Here we are derfining the layout of the menus and giving them message id's. These message id's have to be defined in both the resource file and the source file. This is why we use a header file to define them and then we just include the header file.

#include
#include "section_1_5_1.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = "ID_MENU";
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        switch(Message) {
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                case WM_COMMAND:
                        switch(LOWORD(wParam)) {
                                case ID_FILE_NEW:
                                        MessageBox(hwnd, "New File", "Menu", 0);
                                        break;
                                case ID_FILE_OPEN:
                                        MessageBox(hwnd, "Open File", "Menu", 0);
                                        break;
                                case ID_FILE_SAVE:
                                        MessageBox(hwnd, "Save File", "Menu", 0);
                                        break;
                                case ID_FILE_EXIT:
                                        PostQuitMessage(0);
                                case ID_DO_SOMETHING:
                                        MessageBox(hwnd, "Do Something", "Menu", 0);
                                        break;
                                case ID_DO_SOMETHING_ELSE:
                                        MessageBox(hwnd, "Do Something Else", "Menu", 0);
                                        break;
                                case ID_HELP_ABOUT:
                                        MessageBox(hwnd, "Written By AZTEK", "About", 0);
                                        break;
                        }
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


You see we changed the WndClass.lpszMenuName to what the name for our menu is. Then we added WM_COMMAND. This is the message that is posted when the user clicks a menu item. The ID of the item is sent with the message. We use LOWORD() to get the message from the lower owrd of the 32-bit. LOWORD() takes the following parameter:


dwValue
The value to get the low word from.
The next part will look to see what to do from the cases. If the user selects Exit it tells the program to do a PostQuitMessage(). None of this should be new. We already went over message boxes and the PostQuitMessage function. 1.5.2 On The Spot
Source - Screen Shot
Using resource menus are great if you have a menu that won't change. Some applications although require menus that can be made on the spot. This might be used to add or delete items from the menu or to make some items gray.

#define ID_FILE_NEW           1000
#define ID_FILE_OPEN          1001
#define ID_FILE_SAVE          1002
#define ID_FILE_EXIT          1003
#define ID_DO_SOMETHING       1004
#define ID_DO_SOMETHING_ELSE  1005
#define ID_HELP_ABOUT         1006


Since we are making an identicle program we will use the same header file as above.

#include
#include "section_1_5_2.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        HMENU hMenu, hSubMenu;

        switch(Message) {
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                case WM_CREATE:
                        hMenu = CreateMenu();

                        hSubMenu = CreatePopupMenu();
                        AppendMenu(hSubMenu, MF_STRING, ID_FILE_NEW, "&New");
                        AppendMenu(hSubMenu, MF_STRING, ID_FILE_OPEN, "&Open");
                        AppendMenu(hSubMenu, MF_STRING, ID_FILE_SAVE, "&Save");
                        AppendMenu(hSubMenu, MF_SEPARATOR, 0, 0);
                        AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit");
                        AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File");

                        hSubMenu = CreatePopupMenu();
                        AppendMenu(hSubMenu, MF_STRING, ID_DO_SOMETHING, "&Something");
                        AppendMenu(hSubMenu, MF_STRING, ID_DO_SOMETHING_ELSE, "Something &Else");
                        AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Do");

                        hSubMenu = CreatePopupMenu();
                        AppendMenu(hSubMenu, MF_STRING, ID_HELP_ABOUT, "&About");
                        AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Help");

                        SetMenu(hwnd, hMenu);
                        break;
                case WM_COMMAND:
                        switch(LOWORD(wParam)) {
                                case ID_FILE_NEW:
                                        MessageBox(hwnd, "New File", "Menu", 0);
                                        break;
                                case ID_FILE_OPEN:
                                        MessageBox(hwnd, "Open File", "Menu", 0);
                                        break;
                                case ID_FILE_SAVE:
                                        MessageBox(hwnd, "Save File", "Menu", 0);
                                        break;
                                case ID_FILE_EXIT:
                                        PostQuitMessage(0);
                                case ID_DO_SOMETHING:
                                        MessageBox(hwnd, "Do Something", "Menu", 0);
                                        break;
                                case ID_DO_SOMETHING_ELSE:
                                        MessageBox(hwnd, "Do Something Else", "Menu", 0);
                                        break;
                                case ID_HELP_ABOUT:
                                        MessageBox(hwnd, "Written By AZTEK", "About", 0);
                                        break;
                        }
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


At the top you may notice I set WndClass.lpszMenuName back to NULL. Thats because we are making the menu on the spot and don't need the resource file. First you might notice we added WM_CREATE. This is the message sent when the window is first created. We also declared two varibales of the type HMENU. You see that we set hMenu to CreateMenu(). This will start the whole menu. Then you see we set hSubMenu to CreatePopupMenu(). This will make a blank individual sub menu. We need to fill it. You see we call AppendMenu() to add items to our menus. AppendMenu() takes the following parameters:


hMenu
Identifies the menu bar.
uFlags
Specifies flags to control the appearance and behavior of the new menu item. This parameter can be a combination of values.
uIDNewItem
Specifies either the identifier of the new menu item or, if the uFlags parameter is set to MF_POPUP, the handle to the drop-down menu or submenu.
lpNewItem
Specifies the content of the new menu item.
After that we see SetMenu(), this is what actually displays the menu. SetMenu() takes the following parameters:


hWnd
Identifies the window to which the menu is to be assigned.
hMenu
Identifies the new menu. If this parameter is NULL, the window's current menu is removed.
1.6 Dialogs
Source - Screen Shot
Dialogs are a special kind of windows message box where you have a lot more control than just a title and some text. In dialogs you can have edit boxes, check boxes, icons, bitmaps, change colors, etc. It is easiest to design dialog boxes using a resource editor, you can find some in the Tools Section.

#define ID_FILE_EXIT   1000
#define ID_HELP_ABOUT  1001
#define IDOK           2000
#define IDEMAIL        2001
#define IDAZTEK        2003
#define IDBSRF         2004


This is our header file.

#include "section_1_6.h"

ABOUTDLG DIALOG 19, 17, 182, 71
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "About"
FONT 8, "MS Sans Serif"
BEGIN
        CTEXT         "Written by AZTEK", 101, 17, 30, 81, 11
        GROUPBOX      "About", 102, 11, 11, 95, 48, WS_TABSTOP
        DEFPUSHBUTTON "&Ok", IDOK, 112, 6, 64, 14
        PUSHBUTTON    "&E-mail AZTEK", IDEMAIL, 112, 21, 64, 14
        PUSHBUTTON    "Visit &AZTEK", IDAZTEK, 112, 36, 64, 14
        PUSHBUTTON    "Visit &Blacksun", IDBSRF, 112, 51, 64, 14
END

ID_MENU MENU
BEGIN
        POPUP "&File"
        BEGIN
                MENUITEM "E&xit",  ID_FILE_EXIT
        END
        POPUP "&Help"
        BEGIN
                MENUITEM "&About", ID_HELP_ABOUT
        END
END


This is our resource file. Next the ABOUTDLG DIALOG there are the diameters of our popup dialog box. The next line decalres some styles for the dialog. The third line is the caption or what will appear in the title bar of our window. The FONT 8, "MS Sans Serif" just states 8 point type in the font MS Sans Serif. CTEXT starts some standard text for our dialog. Group box just adds the frame aorund the text. Then DEFPUSHBUTTON decalres the default push button. The rest of the PUSHBUTTON's decalre normal pushable buttons. Now for our source file.

#include
#include "section_1_6.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        switch(Message) {
                case WM_COMMAND:
                        switch(LOWORD(wParam)) {
                                case ID_FILE_EXIT:
                                        PostMessage(hwnd, WM_CLOSE, 0, 0);
                                        break;
                                case ID_HELP_ABOUT:
                                        DialogBox(ghInstance, "ABOUTDLG", hwnd, DlgProc);
                                        break;
                        }
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}

BOOL CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        switch(Message) {
                case WM_INITDIALOG:
                        return TRUE;
                case WM_COMMAND:
                        switch(LOWORD(wParam)) {
                                case IDOK:
                                        EndDialog(hwnd, IDOK);
                                        return TRUE;
                                case IDEMAIL:
                                        ShellExecute(hwnd, "open", "mailto:aztek@faction7.com", 0, 0, 0);
                                        EndDialog(hwnd, IDEMAIL);
                                        return TRUE;
                                case IDAZTEK:
                                        ShellExecute(hwnd, "open", "http://aztek.faction7.com", 0, 0, 0);
                                        EndDialog(hwnd, IDAZTEK);
                                        return TRUE;
                                case IDBSRF:
                                        ShellExecute(hwnd, "open", "http://blacksun.box.sk", 0, 0, 0);
                                        EndDialog(hwnd, IDBSRF);
                                        return TRUE;
                        }
                        break;
        }
        return FALSE;
}


You will notice I added a new function, DlgProc(). This is known as the Dailog Procedure. This function although it returns the Boolen type takes the same parameters as WndProc(). This function is alot like the WndProc() function, it recives and acts on the messages for the dialog. We call DialogBox() to create the dialog. DialogBox() takes the following parameters:


hInstance
Handle to the windows instance.
lpTemplate
Identifies the dialog box template. Usually a null-terminated string.
hWndParent
Identifies the parent window of the dialog box.
pDialogFunc
Points to the window procedure for the Dialog Box
To end the dialog we use the function EndDialog(). EndDialog() takes the following parameters:


hDlg
Identifies the dialog box to be destroyed.
nResult
Specifies the value to be returned to the application.
What value you choose to give to nResult will be returned by the function DialogBox(). We use the function ShellExecute() to open the defualt e-mail and browser programs. ShellExecute() takes the following parameters:


hwnd
Specifies a parent window. This window receives any message boxes that an application produces.
lpOperation
Pointer to a null-terminated string that specifies the operation to perform.
lpFile
Pointer to a null-terminated string that specifies the file to open or print or the folder to open or explore.
lpParameters
If lpFile specifies an executable file, lpParameters is a pointer to a null-terminated string that specifies parameters to be passed to the application.
lpDirectory
Pointer to a null-terminated string that specifies the default directory.
nShowCmd
If lpFile specifies an executable file, nShowCmd specifies how the application is to be shown when it is opened.
1.7 Controls
Source - Screen Shot
Controls are just child windows like buttons, checkboxess, edit boxes, etc. For each control we make a new window using CreateWindowEx() but these are childs of the first main window and not new windows.

#include

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        HWND hButton, hCombo, hEdit, hList, hScroll, hStatic;

        switch(Message) {
                case WM_CREATE:
                        hButton = CreateWindowEx(
                                NULL,
                                "Button",
                                "Button",
                                WS_BORDER | WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                0, 0,
                                100, 30,
                                hwnd, NULL,
                                ghInstance,
                                NULL);
                        hCombo  = CreateWindowEx(
                                NULL,
                                "ComboBox",
                                "",
                                WS_BORDER | WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
                                0, 30,
                                100, 100,
                                hwnd, NULL,
                                ghInstance,
                                NULL);
                        hEdit   = CreateWindowEx(
                                NULL,
                                "Edit",
                                "Edit",
                                WS_BORDER | WS_CHILD | WS_VISIBLE,
                                0, 60,
                                100, 30,
                                hwnd, NULL,
                                ghInstance,
                                NULL);
                        hList   = CreateWindowEx(
                                NULL,
                                "ListBox",
                                "",
                                WS_BORDER | WS_CHILD | WS_VISIBLE,
                                100, 0,
                                100, 200,
                                hwnd, NULL,
                                ghInstance,
                                NULL);
                        hScroll = CreateWindowEx(
                                NULL,
                                "ScrollBar",
                                "",
                                WS_BORDER | WS_CHILD | WS_VISIBLE | SBS_VERT,
                                210, 0,
                                100, 200,
                                hwnd, NULL,
                                ghInstance,
                                NULL);
                        hStatic = CreateWindowEx(
                                NULL,
                                "Static",
                                "",
                                WS_BORDER | WS_CHILD | WS_VISIBLE | SS_BLACKRECT,
                                0, 90,
                                100, 30,
                                hwnd, NULL,
                                ghInstance,
                                NULL);
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


In this example add some very simple controls to this window. There are many other controls you can use but these are the most simple and easy ones to add. Lokk in your windows programming help files for how to recive and send messages to these controls we added.

2. Intermediate

2.1 Bitmaps

In this section I will discus how to display a bitmap file in your program. The first way I will discus is the static way using a resource. The second way is to do it dynamicly using a actual bitmap file. Throughout our examples here I will use this bitmap file.
2.1.1 Resource
Source - Screen Shot
Using a bitmap from a resource file is probbaly the best way to do it unless you need the expandibility of a dynamic bitmap file.

#define IDB_BITMAP1   1000


This is are header file.

#include "section_2_1_1.h"

IDB_BITMAP1 BITMAP "bitmap.bmp"


Now for our source code.

#include
#include "section_2_1_1.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        HBITMAP hBitmap;
        HWND hBmpStat;

        switch(Message) {
                case WM_CREATE:
                        hBitmap  = LoadBitmap(ghInstance, MAKEINTRESOURCE(IDB_BITMAP1));
                        hBmpStat = CreateWindowEx(
                                NULL,
                                "Static",
                                "",
                                WS_VISIBLE | WS_CHILD | SS_BITMAP,
                                0, 0,
                                0, 0,
                                hwnd, NULL,
                                ghInstance,
                                NULL);
                        SendMessage(hBmpStat, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hBitmap);
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


We use a new resource type BITMAP in our resource file. We use the type HBITMAP to define a variable to hold all of the information for loading the bitmap. We give hBitmap the value returned by LoadBitmap(). The function LoadBitmap() takes the following parameters:


hInstance
The instance of the current window.
lpszBitmapName
Points to a null-terminated string that contains the name of the bitmap resource to be loaded. We will use the macro MAKEINTRESOURCE()
The macro MAKEINTRESOURCE() takes the following parameters:


wInteger
Specifies the integer value to be converted.
You will notice that we also have to define a new static child window. This static window has the style SS_BITMAP. Then we use the function SendMessage() to tell the window to display the bitmap. The function SendMessage() takes the following parameters:


hWnd
Identifies the window whose window procedure will receive the message.
Msg
Specifies the message to be sent.
wParam
Specifies additional message-specific information.
lParam
Specifies additional message-specific information.
2.1.2 From a File
Source - Screen Shot
Loading a bitmap from a file is alot like loading one from a resource, in fact there is actually only 1 line change.

#include

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        HBITMAP hBitmap;
        HWND hBmpStat;

        switch(Message) {
                case WM_CREATE:
                        hBitmap  = (HBITMAP) LoadImage(NULL, "bitmap.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
                        hBmpStat = CreateWindowEx(
                                NULL,
                                "Static",
                                "",
                                WS_VISIBLE | WS_CHILD | SS_BITMAP,
                                0, 0,
                                0, 0,
                                hwnd, NULL,
                                ghInstance,
                                NULL);
                        SendMessage(hBmpStat, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hBitmap);
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


You can see we only changed 1 line from LoadBitmap() to LoadImage(). The function LoadImage() takes the following parameters:


hInstance
The instance of the current procedure.
lpszName
Identifies the image to load.
uType
Specifies the type of image to be loaded.
cxDesired
Specifies the width, if this parameter is zero and LR_DEFAULTSIZE is not used, the function uses the actual image width.
cyDesired
Specifies the height, if this parameter is zero and LR_DEFAULTSIZE is not used, the function uses the actual image width.
fuLoad
Specifies additional flags for the image.
2.2 System Tray
Source - Screen Shot
The "Big Thing" for windows programs right now seems to be to put a icon in the system tray. In this next example we will go over putting a icon in in the system tray and having a menu pop up when the user right clicks on the icon.

#include "section_2_2.h"

IDI_ICON1 ICON "icon.ico"


Here is our resource file.

#define WM_TRAYNOTIFY (WM_USER + 1000)
#define IDI_ICON1     1000
#define IDC_TRAYICON  1001
#define IDM_EXIT      1002
#define IDM_ABOUT     1003


Our header file.

#include
#include "section_2_2.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;
HMENU hMenu;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        NOTIFYICONDATA notifyIconData;

        switch(Message) {
                case WM_CREATE:
                        notifyIconData.cbSize           = sizeof(NOTIFYICONDATA);
                        notifyIconData.hWnd             = hwnd;
                        notifyIconData.uID              = IDC_TRAYICON;
                        notifyIconData.uFlags           = NIF_MESSAGE | NIF_ICON | NIF_TIP;
                        notifyIconData.uCallbackMessage = WM_TRAYNOTIFY;
                        notifyIconData.hIcon            = (HICON) LoadImage(
                                                                        ghInstance,
                                                                        MAKEINTRESOURCE(IDI_ICON1),
                                                                        IMAGE_ICON,
                                                                        16, 16,
                                                                        NULL);
                        lstrcpyn(notifyIconData.szTip, "Tray Icon", sizeof(notifyIconData.szTip));
                        Shell_NotifyIcon(NIM_ADD, &notifyIconData);

                        hMenu = CreatePopupMenu();
                        AppendMenu(hMenu, MF_STRING, IDM_EXIT, "E&xit");
                        AppendMenu(hMenu, MF_SEPARATOR, NULL, NULL);
                        AppendMenu(hMenu, MF_STRING, IDM_ABOUT, "&About");
                        break;
                case WM_TRAYNOTIFY:
                        switch(wParam) {
                                case IDC_TRAYICON:
                                        switch(lParam) {
                                                case WM_LBUTTONDOWN:
                                                case WM_RBUTTONDOWN:
                                                        POINT point;

                                                        GetCursorPos(&point);
                                                        SetForegroundWindow(hwnd);
                                                        TrackPopupMenu(hMenu, TPM_RIGHTALIGN, point.x, point.y, NULL, hwnd, NULL);
                                                        SendMessage(hwnd, WM_NULL, NULL, NULL);
                                                        break;
                                        }
                                        break;
                        }
                        break;
                case WM_COMMAND:
                        switch(wParam) {
                                case IDM_EXIT:
                                        SendMessage(hwnd, WM_CLOSE, NULL, NULL);
                                        break;
                                case IDM_ABOUT:
                                        MessageBox(
                                                hwnd,
                                                "Example of a windows system tray program\r\nWritten by AZTEK",
                                                "About",
                                                NULL);
                                        break;
                                        break;
                        }
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        notifyIconData.cbSize = sizeof(NOTIFYICONDATA);
                        notifyIconData.hWnd   = hwnd;
                        notifyIconData.uID    = IDC_TRAYICON;
                        Shell_NotifyIcon(NIM_DELETE, &notifyIconData);

                        DestroyMenu(hMenu);

                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


You see we use a new resource type ICON. In our header file we define our messages. We use the NOTIFYICONDATA structure to hold the data that initilize the tray icon. The structure NOTIFYICONDATA has the following settings:


cbSize
Size of the NOTIFYICONDATA structure.
hWnd
Handle of the window that receives notification messages associated with an icon in the taskbar status area.
uID
Application-defined identifier of the taskbar icon.
uFlags
Array of flags that indicate which of the other members contain valid data.
uCallbackMessage
Application-defined message identifier. The system uses the specified identifier for notification messages that it sends to the window identified by hWnd whenever a mouse event occurs in the bounding rectangle of the icon.
hIcon
Handle of the icon to add, modify, or delete.
szTip
Tooltip text to display for the icon.
I will not go into the function lstrcpyn() since its a common C function and should be known aleady. We use the function Shell_NotifyIcon() to add/delete the icon from the tray. The function Shell_NotifyIcon() takes the following parameters:


dwMessage
Identifier of the message to send.
pnid
Pointer to a NOTIFYICONDATA structure. The content of the structure depends on the value of dwMessage.
We will use the POINT structure to hold data recived from GetCursorPos(). The structure POINT takes the following settings:


x
Specifies the x-coordinate of the point.

y


Specifies the y-coordinate of the point.
The function GetCursorPos() finds the cordinates of the mouse pointer and puts the data in a givin POINT structure. The function GetCurorPos() takes the following parameter:


lpPoint
Points to a POINT structure that receives the screen coordinates of the cursor.
The function SetForgroundWindow() sets what ever hwnd givin to the front and makes it the active window. The function SetForgroundWindow() takes the following parameter:


hWnd
Identifies the window that should be activated and brought to the foreground.
We use the function TrackPopupMenu() to popup and show the shortcut menu hMenu. The function TrackPopupMenu() takes the following parameters:


hMenu
Identifies the shortcut menu to be displayed.
uFlags
A set of bit flags that specify function options.
x
Specifies the horizontal location of the shortcut menu, in screen coordinates.
y
Specifies the vertical location of the shortcut menu, in screen coordinates.
nReserved
Reserved; must be zero.
hWnd
Identifies the window that owns the shortcut menu. This window receives all messages from the menu. The window does not receive a WM_COMMAND message from the menu until the function returns.
prcRect
Points to a RECT structure that specifies the portion of the screen in which the user can select without dismissing the shortcut menu. If this parameter is NULL, the shortcut menu is dismissed if the user clicks outside the shortcut menu.
Then we use SendMessage() to send the window a NULL message. When we recive the WM_DESTROY message we use the Shell_NotifyIcon() function to destroy and remove the icon from the system tray. 2.3 Toolbars
In this section I will cover how to add a toolbar to you window. I will first go over adding custom buttons from a bitmap file. The I will go over common buttons that are predefined.
2.3.1 Custom Buttons
Source - Screen Shot
Custom buttons are just a bitmap image that is devided up in 16x16 squares. In this next example, I use this bitmap file.

#include "section_2_3.h"

IDB_TOOLBAR BITMAP "toolbar.bmp"


Here is our resource file.

#define IDC_TOOLBAR  1000
#define IDB_TOOLBAR  1001
#define IDM_BUTTON0  1002
#define IDM_BUTTON1  1003
#define IDM_BUTTON2  1004
#define IDM_BUTTON3  1005
#define IDM_BUTTON4  1006
#define IDM_BUTTON5  1007
#define IDM_BUTTON6  1008
#define IDM_BUTTON7  1009


This is our header file.

#include
#include
#include "section_2_3_1.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;
HWND   ghToolBar;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        switch(Message) {
                case WM_CREATE:
                        TBADDBITMAP tbAddBitmap;
                        TBBUTTON tbButton[8];

                        InitCommonControls();

                        ghToolBar = CreateWindowEx(
                                NULL,
                                TOOLBARCLASSNAME,
                                NULL,
                                WS_CHILD | WS_VISIBLE,
                                0, 0,
                                0, 0,
                                hwnd, (HMENU)IDC_TOOLBAR,
                                ghInstance,
                                NULL);
                        SendMessage(ghToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), NULL);

                        tbAddBitmap.hInst = ghInstance;
                        tbAddBitmap.nID   = IDB_TOOLBAR;
                        SendMessage(ghToolBar, TB_ADDBITMAP, 0, (LPARAM) &tbAddBitmap);

                        ZeroMemory(tbButton, sizeof(tbButton));

                        tbButton[0].iBitmap   = 0;
                        tbButton[0].fsState   = TBSTATE_ENABLED;
                        tbButton[0].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[0].idCommand = IDM_BUTTON0;

                        tbButton[1].iBitmap   = 1;
                        tbButton[1].fsState   = TBSTATE_ENABLED;
                        tbButton[1].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[1].idCommand = IDM_BUTTON1;

                        tbButton[2].fsStyle   = TBSTYLE_SEP;

                        tbButton[3].iBitmap   = 2;
                        tbButton[3].fsState   = TBSTATE_ENABLED;
                        tbButton[3].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[3].idCommand = IDM_BUTTON2;

                        tbButton[4].iBitmap   = 3;
                        tbButton[4].fsState   = TBSTATE_ENABLED;
                        tbButton[4].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[4].idCommand = IDM_BUTTON3;

                        tbButton[5].fsStyle   = TBSTYLE_SEP;

                        tbButton[6].iBitmap   = 4;
                        tbButton[6].fsState   = TBSTATE_ENABLED;
                        tbButton[6].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[6].idCommand = IDM_BUTTON4;

                        tbButton[7].iBitmap   = 5;
                        tbButton[7].fsState   = TBSTATE_ENABLED;
                        tbButton[7].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[7].idCommand = IDM_BUTTON5;

                        SendMessage(ghToolBar, TB_ADDBUTTONS, 8, (LPARAM) &tbButton);
                        break;
                case WM_COMMAND:
                        switch(LOWORD(wParam)) {
                                case IDM_BUTTON0:
                                case IDM_BUTTON1:
                                case IDM_BUTTON2:
                                case IDM_BUTTON3:
                                case IDM_BUTTON4:
                                case IDM_BUTTON5:
                                        MessageBox(hwnd, "A Button was clicked.", "Toolbar Message", MB_ICONINFORMATION | MB_OK);
                                        break;
                        }
                        break;
                case WM_SIZE:
                        SendMessage(ghToolBar, TB_AUTOSIZE, 0, 0);
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


You will notice that we added a new header file, commctrl.h, this is the header file use for common controls. Common controls are dialogs like Open, Save, Print, etc that many program use it is also a set of controls to make toolbars and statusbars we will get into common controls more in the next tutorial. We declare a TBADDBITMAP structure. The TBADDBITMAP structure adds a bitmap that contains button images to a toolbar. It has the following parameters:


hInst
Handle to the module instance with the executable file that contains a bitmap resource.
nID
Resource identifier of the bitmap resource that contains the button images.
We also added a TBBUTTON structure. This is an array of the settings for each button of our toolbar. The array size has to be the number of buttons in our toolbar including separators since they are considered buttons also. The TBBUTTON structure has the following parameters:

iBitmap

Zero-based index of button image.

idCommand

Command identifier associated with the button. This identifier is used in a WM_COMMAND message when the button is chosen.

fsState

This is the state of the button.

fsStyle

This is the style of the button.

dwData

Application-defined value.

iString

Zero-based index of button string.
Then you see we run the function InitCommonControls(), the InitCommonControls() function ensures that the common control dynamic-link library (DLL) is loaded and that we can use the common controls such as toolbars. The we create our toolbar with CreateWindowEx(). The we send our toolbar the message TB_BUTTONSTRUCTSIZE to tell the system how much space to allocate for our toolbar. The we set our TBADDBITMAP parameters and then send then to the toolbar with the message TB_ADDBITMAP. We use ZeroMemory() to make sure that there is no data in the tbButton structure. We then create 2 buttons then a separator and so on. Then at the end we send our tbButtons structure to the toolbar with the message TB_ADDBUTTONS. We also added a new message trap the WM_SIZE, this message is sent to the program everytime the user resizes the window. When the window is resized we need to tell the toolbar to move and adjust so we send it the TB_AUTOSIZE message.
2.3.2 Common Buttons
Source - Screen Shot
It would be a real pain if you had to make new New, Open, Save, etc. buttons for every application you write, so they came up with common buttons. Common buttons are buttons used by alot of apps. Below I have a common button program example, I will explain the new parts but its pretty much the same as the custom button example above.

#define IDC_TOOLBAR  1000
#define IDM_BUTTON0  1001
#define IDM_BUTTON1  1002
#define IDM_BUTTON2  1003
#define IDM_BUTTON3  1004
#define IDM_BUTTON4  1005
#define IDM_BUTTON5  1006
#define IDM_BUTTON6  1007
#define IDM_BUTTON7  1008


Our header file.

#include
#include
#include "section_2_3_2.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;
HWND   ghToolBar;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        switch(Message) {
                case WM_CREATE:
                        TBADDBITMAP tbAddBitmap;
                        TBBUTTON tbButton[7];

                        InitCommonControls();

                        ghToolBar = CreateWindowEx(
                                NULL,
                                TOOLBARCLASSNAME,
                                NULL,
                                WS_CHILD | WS_VISIBLE,
                                0, 0,
                                0, 0,
                                hwnd, (HMENU)IDC_TOOLBAR,
                                ghInstance,
                                NULL);
                        SendMessage(ghToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), NULL);

                        tbAddBitmap.hInst = HINST_COMMCTRL;
                        tbAddBitmap.nID   = IDB_STD_SMALL_COLOR;
                        SendMessage(ghToolBar, TB_ADDBITMAP, 0, (LPARAM) &tbAddBitmap);

                        ZeroMemory(tbButton, sizeof(tbButton));

                        tbButton[0].iBitmap   = STD_FILENEW;
                        tbButton[0].fsState   = TBSTATE_ENABLED;
                        tbButton[0].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[0].idCommand = IDM_BUTTON0;

                        tbButton[1].iBitmap   = STD_FILEOPEN;
                        tbButton[1].fsState   = TBSTATE_ENABLED;
                        tbButton[1].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[1].idCommand = IDM_BUTTON1;

                        tbButton[3].iBitmap   = STD_FILESAVE;
                        tbButton[3].fsState   = TBSTATE_ENABLED;
                        tbButton[3].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[3].idCommand = IDM_BUTTON2;

                        tbButton[5].fsStyle   = TBSTYLE_SEP;

                        tbButton[4].iBitmap   = STD_CUT;
                        tbButton[4].fsState   = TBSTATE_ENABLED;
                        tbButton[4].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[4].idCommand = IDM_BUTTON3;

                        tbButton[6].iBitmap   = STD_COPY;
                        tbButton[6].fsState   = TBSTATE_ENABLED;
                        tbButton[6].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[6].idCommand = IDM_BUTTON4;

                        tbButton[7].iBitmap   = STD_PASTE;
                        tbButton[7].fsState   = TBSTATE_ENABLED;
                        tbButton[7].fsStyle   = TBSTYLE_BUTTON;
                        tbButton[7].idCommand = IDM_BUTTON5;

                        SendMessage(ghToolBar, TB_ADDBUTTONS, 7, (LPARAM) &tbButton);
                        break;
                case WM_COMMAND:
                        switch(LOWORD(wParam)) {
                                case IDM_BUTTON0:
                                case IDM_BUTTON1:
                                case IDM_BUTTON2:
                                case IDM_BUTTON3:
                                case IDM_BUTTON4:
                                case IDM_BUTTON5:
                                        MessageBox(hwnd, "A Button was clicked.", "Toolbar Message", MB_ICONINFORMATION | MB_OK);
                                        break;
                        }
                        break;
                case WM_SIZE:
                        SendMessage(ghToolBar, TB_AUTOSIZE, 0, 0);
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


As you can see not much overall has changed but the first thing you might notice is that tbAddBitmap.hInst is now set to HINST_COMMCTRL and that tbAddBitmap.nID is set to IDB_STD_SMALL_COLOR this just tell it to use the standard buttons and not a bitmap. And now our iBitmap is a STD_ and not a number. The STD_ is what our button will be like STD_COPY is the copy button. There is not much difference besides that.

2.4 Status Bars
Source - Screen Shot
We have all seen status bars. Most text editing programs have a statusbar. They can be very useful, but they can also be one of the most abused thing in windows programming.

#define IDC_STATBAR 1000


Our header file.

#include
#include
#include "section_2_4.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char gszClassName[]  = "MyWindowClass";
static HINSTANCE ghInstance = NULL;
HWND   ghStatBar;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX WndClass;
        HWND hwnd;
        MSG Msg;

        ghInstance = hInstance;

        WndClass.cbSize        = sizeof(WNDCLASSEX);
        WndClass.style         = NULL;
        WndClass.lpfnWndProc   = WndProc;
        WndClass.cbClsExtra    = 0;
        WndClass.cbWndExtra    = 0;
        WndClass.hInstance     = ghInstance;
        WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WndClass.lpszMenuName  = NULL;
        WndClass.lpszClassName = gszClassName;
        WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(!RegisterClassEx(&WndClass)) {
                MessageBox(0, "Window Registration Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

        hwnd = CreateWindowEx(
                WS_EX_STATICEDGE,
                gszClassName,
                "Windows Title",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                320, 240,
                NULL, NULL,
                ghInstance,
                NULL);

        if(hwnd == NULL) {
                MessageBox(0, "Window Creation Failed!", "Error!", MB_ICONSTOP | MB_OK);
                return 0;
        }

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

        while(GetMessage(&Msg, NULL, 0, 0)) {
                TranslateMessage(&Msg);
                DispatchMessage(&Msg);
        }
        return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
        switch(Message) {
                case WM_CREATE:
                        int iBarWidths[] = {100, 200, 300, -1};
                        InitCommonControls();

                        ghStatBar = CreateWindowEx(
                                NULL,
                                TOOLBARCLASSNAME,
                                NULL,
                                WS_CHILD | WS_VISIBLE,
                                0, 0,
                                0, 0,
                                hwnd, (HMENU)IDC_TOOLBAR,
                                ghInstance,
                                NULL);
                        SendMessage(ghStatBar, SB_SETPARTS, 4, (LPARAM) iBarWidths);

                        SendMessage(ghStatBar, SB_SETTEXT, 0, (LPARAM) "Part 1");
                        SendMessage(ghStatBar, SB_SETTEXT, 1, (LPARAM) "Part 2");
                        SendMessage(ghStatBar, SB_SETTEXT, 2, (LPARAM) "Part 3");
                        SendMessage(ghStatBar, SB_SETTEXT, 3, (LPARAM) "Part 4");
                        break;
                case WM_SIZE:
                        SendMessage(ghStatBar, WM_SIZE, 0, 0);
                        break;
                case WM_CLOSE:
                        DestroyWindow(hwnd);
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                default:
                        return DefWindowProc(hwnd, Message, wParam, lParam);
        }
        return 0;
}


You will see I defined a array of integers at the top of WM_CREATE these are the widths of the sections we will break the statusbar into. A value of -1 will take up the remaining space. Then we create our statusbar with CreateWindowEx(). We then send our statusbar the widths with the message SB_SETPARTS the WPARAM is how many parts we will brak it into. The we set some text fo the parts with the message SB_SETTEXT. The WPARAM of SB_SETTEXT is the zero-based index of the part. When we received the windows you will see in the WM_SIZE trap that we send the statusbar the WM_SIZE message that we just received so it can adjust properly.

Appendix

A. The Compiler

I like most of you are pretty low on cash most of the time. So I use a free compiler from Borland. I also use the old Borland 5.02 the last one with an IDE. I also got my hands on a copy of Visual C++ 6 ( not going to say how) which I didn't find all that great and pretty intrusive. All of the examples in this tutorial were compiled with the Borland Free Compiler v5.5 check the tools section for a link to where you can download it. After you get the compiler set up correctly just open up a command line and browse to where you unziped the tutorials. The compile using:

C:\Cpp\tutorial\examples\section_1_2> bcc32 -tW section_1_2.cpp


The -tW parameter tells the compiler to link to the windows library and make a windows application and not a dos application. Some of the more complicated examples have a makefile.mak file, this is a make file. Just browse to that directory and type "make" (without quotes) to compile that example. I do not support people who e-mail me about Dev-C++. Personally I do not like that program. The IDE is great but the compiler is probably the worst I have ever used. So just use the program for an IDE and the use Borland for a compiler its what I did for a while until I found a better IDE (its listed in the tools section).

B. Tools

Every great programmer has to have a few good tools he uses. The first being his compiler and IDE. Here I have listed a few tools I find that might be useful.


  • Borland Free 5.5 Compiler - A great overall windows compiler, and its FREE!
  • MultiEdit - A good overall IDE that I use all the time
  • Komodo - Another really good IDE that I find myself using alot
  • Win API Reference - You cannot consider yourself a windows programmer unless you have this, it is a must have!
  • LCC-Win32 - A fairly nice compiler with a user interface and a good resource editor
That probably pretty much covers it for the major tools I would recommend. C. Links

These are a few sights I find invaluable to learning programming of any type.