C++ To realize Tetris (windows API)

  • 2020-06-07 04:52:50
  • OfStack

This article to share these tetris code is written I recently have a holiday at home, although used to have a look at the others write code, but the game code seems to be not very comprehensive, unable to realize all the squares and the generation of random arbitrary direction of diamonds, it basically is forgotten, now the code, the code below is my recently wrote, without reference to other people's code, just felt really really write the tetris is quite difficult, the key lies in the rotation of the square. Of course, the following code is only a framework, can only achieve the general function, is not comprehensive, posted and we exchange learning.

The compiler is code: : block + MinGW, I feel CB IDE is really too powerful, too great, the following code directly copied to VC inside the run should not make mistakes, there is a problem 1 I do not know how to solve, is to update the client area when the window is always flashing I do not know who can point me 1. Some are windows API, windows programming is not very understand, I hope you leave a lot of comments, give me 1.


#include <windows.h> 
#include <iostream> 
#include <cstdlib> 
#include <ctime> 
using namespace std; 
#define CellWidth 20 
#define MAP_WIDTH 12 
#define MAP_HEIGHT 18 
#define ID_TIMER 1 
class map_floor; 
class Block; 
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); 
/* Make the class name into a global variable */ 
char szClassName[ ] = "CodeBlocksWindowsApp"; 
int WINAPI WinMain (HINSTANCE hThisInstance, 
           HINSTANCE hPrevInstance, 
           LPSTR lpszArgument, 
           int nCmdShow) 
{ 
  HWND hwnd;        /* This is the handle for our window */ 
  MSG messages;      /* Here messages to the application are saved */ 
  WNDCLASSEX wincl;    /* Data structure for the windowclass */ 
 
  /* The Window structure */ 
  wincl.hInstance = hThisInstance; 
  wincl.lpszClassName = szClassName; 
  wincl.lpfnWndProc = WindowProcedure;   /* This function is called by windows */ 
  wincl.style = CS_DBLCLKS|CS_HREDRAW | CS_VREDRAW;         /* Catch double-clicks */ 
  wincl.cbSize = sizeof (WNDCLASSEX); 
 
  /* Use default icon and mouse-pointer */ 
  wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); 
  wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); 
  wincl.hCursor = LoadCursor (NULL, IDC_ARROW); 
  wincl.lpszMenuName = NULL;         /* No menu */ 
  wincl.cbClsExtra = 0;           /* No extra bytes after the window class */ 
  wincl.cbWndExtra = 0;           /* structure or the window instance */ 
  /* Use Windows's default colour as the background of the window */ 
  wincl.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH);//COLOR_BACKGROUND; 
 
  /* Register the window class, and if it fails quit the program */ 
  if (!RegisterClassEx (&wincl)) 
    return 0; 
 
  /* The class is registered, let's create the program*/ 
  hwnd = CreateWindowEx ( 
      0,         /* Extended possibilites for variation */ 
      szClassName,     /* Classname */ 
      "Code::Blocks Template Windows App",    /* Title Text */ 
      WS_OVERLAPPEDWINDOW, /* default window */ 
      CW_USEDEFAULT,    /* Windows decides the position */ 
      CW_USEDEFAULT,    /* where the window ends up on the screen */ 
      CW_USEDEFAULT,         /* The programs width */ 
      CW_USEDEFAULT,         /* and height in pixels */ 
      NULL,    /* The window is a child-window to desktop */ 
      NULL,        /* No menu */ 
      hThisInstance,    /* Program Instance handler */ 
      NULL         /* No Window Creation data */ 
      ); 
 
  /* Make the window visible on the screen */ 
  ShowWindow (hwnd, nCmdShow); 
 
  /* Run the message loop. It will run until GetMessage() returns 0 */ 
  while (GetMessage (&messages, NULL, 0, 0)) 
  { 
    /* Translate virtual-key messages into character messages */ 
    TranslateMessage(&messages); 
    /* Send message to WindowProcedure */ 
    DispatchMessage(&messages); 
  } 
 
  /* The program return-value is 0 - The value that PostQuitMessage() gave */ 
  return messages.wParam; 
} 
 
enum{e_LINE,e_CORNER,e_STAIR,e_TANCK,e_TIAN}; 
const int TOTAL_BLOCK_STYLE = 5;// The box types are 4 Kind of  
class Block 
{ 
  public: 
      Block(int x = 100, int y = 100); 
      Block(const Block & rh)// Copy constructors, it might not be useful, but let's define them anyway. Okay  
      { 
        this->m_style = rh.m_style; 
        this->m_direct = rh.m_direct; 
        for(int i = 0 ; i < 4 ; i ++) 
          this->m_block[i] = rh.m_block[i]; 
      } 
      Block & operator = (const Block& rh)// overloading = Number, to achieve the square of the assignment  
      { 
        this->m_style = rh.m_style; 
        this->m_direct = rh.m_direct; 
        for(int i = 0 ; i < 4 ; i ++) 
          this->m_block[i] = rh.m_block[i]; 
        return *this; 
      } 
      ~Block(){} 
  int   create_block(int x = 100 , int y = 100); 
  // Displays squares that move in the play area  
  int   show_block(HDC hdc,const POINT& GameLeftTop); 
  // Displays the square that will appear, the one to the left of the game area  
  int   show_next_block(HDC hdc); 
  // Rotation, the function is difficult to implement, the amount of code is also relatively large, there will be time to slowly optimize in the future, about the definition of parsing  
  int   rotate(); 
  // Generate random squares  
  int   random_block(); 
 
  // The following is the member function that the square moves  
  int   get_block_height(){ return m_block[1].y;} 
  int   move_down(const RECT& GameClient); 
  int   move_left(const RECT& GameClient); 
  int   move_right(const RECT& GameClient); 
  int   move_up(const RECT& GameClient); 
  int   move_to(int x , int y); 
  // Checks if the cube is in the game area  
//  int   check_block(const map_floor& map, const RECT& GameClent); 
  int   check_block(const map_floor& map, const POINT& LeftTopScrCdnt); 
  int   print_to_map(map_floor& map , const POINT& LeftTopScrCdnt); 
  private: 
  int   m_style;// The style of the square is specified by the enumeration variable  
  int   m_direct;// The direction of the cube, yes m_style Specific data of  
 POINT   m_block[4];// The subscript for 1 Is the central coordinate of the square, and the rotation is carried out around the square, which is conducive to rotation and clear logic  
}; 
class map_floor 
{ 
  public: 
    map_floor() 
    { 
      ZeroMemory(m_block_bar,sizeof(int )*12*18); 
    } 
    ~map_floor(){} 
  void show_block_bar(HDC hdc , const POINT& LeftTopScrCdnt) 
  { 
    for(int i = 0 ; i < MAP_HEIGHT ; ++ i) 
    { 
      for(int j = 0 ; j < MAP_WIDTH ; ++ j) 
      { 
        if(m_block_bar[i][j]) 
        { 
          Rectangle(hdc,LeftTopScrCdnt.x + j*CellWidth , LeftTopScrCdnt.y + i*CellWidth, 
                 LeftTopScrCdnt.x + (j+1)*CellWidth , LeftTopScrCdnt.y + (i+1)*CellWidth); 
        } 
      } 
    } 
 
  } 
  friend class Block; 
  protected: 
 
  private: 
 
  int     m_block_bar[MAP_HEIGHT][MAP_WIDTH];// Play area floor, with 18*12 the 2 Dimensional array representation  
 
 
}; 
 
Block::Block(int x , int y) 
{ 
//  ZeroMemory(m_block_bar,sizeof(int )*12*18); 
  srand( (unsigned)time( NULL ) );// Initializes a random number used to generate squares  
//  POINT pt = {100,100}; 
  create_block(x,y); 
} 
int Block::random_block() 
{ 
  m_style = rand()%TOTAL_BLOCK_STYLE; 
//  m_style = e_CORNER; // The test of  
//  m_style = e_LINE; // The test of  
  if(m_style == e_STAIR || m_style == e_TANCK) 
    m_direct = rand()%4; 
  else if(m_style == e_LINE) 
    m_direct = rand()%2; 
  else if(m_style == e_CORNER) 
    m_direct = rand()%8; 
  else if(m_style == e_TIAN) 
    m_direct = 0; 
  m_direct = 1; 
 
} 
int  Block::check_block(const map_floor& map, const POINT& LeftTopScrCdnt) 
{ 
  int x , y ; //x , y  Is the coordinates of the square relative to the map, the upper left corner is ( 0 . 0 )  
  for(int i = 0 ; i < 4 ; i ++) 
  { 
    x = (m_block[i].x - LeftTopScrCdnt.x)/CellWidth; 
    y = (m_block[i].y - LeftTopScrCdnt.y)/CellWidth; 
    if(x < 0 || x >= MAP_WIDTH || y >= MAP_HEIGHT)// Don't need to test y < 0  In the case  
    return 0; 
    if(y < 0) continue; 
    if(map.m_block_bar[y][x]) 
    return 0; 
  } 
  return 1; 
} 
int Block::move_down(const RECT& GameClient)// Down, called by the timer message  
{ 
  int i; 
//  for (i = 0 ; i < 4 ; i ++ ) 
//  { 
//   if(m_block[i].y == GameClient.bottom - CellWidth) 
//   return 0; 
//  } 
  for (i = 0; i < 4 ;i ++ ) 
  { 
    m_block[i].y += CellWidth; 
  } 
  return 1; 
} 
int Block::move_up(const RECT& GameClient) 
{ 
  move_to(m_block[1].x,m_block[1].y - CellWidth); 
  return 1; 
} 
int Block::move_left(const RECT& GameClient) 
{ 
  move_to(m_block[1].x - CellWidth,m_block[1].y); 
  return 1; 
} 
int Block::move_right(const RECT& GameClient) 
{ 
  move_to(m_block[1].x + CellWidth , m_block[1].y); 
  return 1; 
} 
int Block::create_block(int x , int y) 
{ 
  m_block[1].x = x; 
  m_block[1].y = y; 
  random_block(); 
  rotate(); 
  return 1; 
} 
int Block::move_to(int x , int y) 
{ 
  int Vx = x - m_block[1].x; 
  int Vy = y - m_block[1].y; 
  for(int i = 0 ; i < 4 ; i ++) 
  { 
    m_block[i].x += Vx; 
    m_block[i].y += Vy; 
  } 
} 
int Block::print_to_map(map_floor& map , const POINT& LeftTopScrCdnt) 
{ 
  int x , y; 
  int i , j; 
  for(i = 0 ; i < 4 ; i ++ ) 
  { 
    x = (m_block[i].x - LeftTopScrCdnt.x)/CellWidth; 
    y = (m_block[i].y - LeftTopScrCdnt.y)/CellWidth; 
    if(x<0 || x >= MAP_WIDTH || y <0 || y >= MAP_HEIGHT)// To secure   , for testing purposes, will be commented out after completion  
      return 0; 
    map.m_block_bar[y][x] = 1 ; 
    for(j = 0 ; j < MAP_WIDTH ; j ++) 
    { 
      if(map.m_block_bar[y][j] != 1) 
        break; 
    } 
    if(MAP_WIDTH == j) 
    { 
      for(j = 0 ; j < MAP_WIDTH ; j ++) 
      { 
        map.m_block_bar[y][j] = 5;// digital 5 That's the rows to get rid of  
      } 
    } 
 
  } 
  int idx; 
  for(i = 0 ; i < MAP_WIDTH ; i ++) 
  { 
    for(idx = j = MAP_HEIGHT - 1 ; j >= 0 ; j --) 
    { 
      if(map.m_block_bar[j][i] != 5) 
      { 
        map.m_block_bar[idx--][i] = map.m_block_bar[j][i]; 
      } 
    } 
    while(idx >= 0) 
    { 
      map.m_block_bar[idx--][i] = 0; 
    } 
  } 
  return 1; 
} 
 
// The following function is to achieve square rotation, can be said to be the whole [Tetris] difficult, but also its core part  
// Squares are used for arrays block 【 4 ], the rest 3 I'm going to go around each of these squares block 【 1 Rotation, square due to the asymmetric square  
// Yes, I was meant to 7 Sort of, but the amount of code behind it is too much, so I'm going to classify the square according to the style 4 Species, respectively:  
// 
//e_LINE  The linear     is 1 The one of the lines, this is a symmetric square, you just have to go in two directions: horizontal and vertical  
//         with m_direct Hold on to the other squares 1 sample  
// 
//e_TANCK  Tank shape   This is a square and it's symmetric 4 Species direction, according to m_direct right 4 The way to do the remainder can be greatly reduced  
//         The amount of code, for the following two squares, is also a way to simplify a lot, so that the code is not so redundant,  
//         That's what I came up with later.  
// 
//e_STAIR  Floor trapezoidal   This square is a little bit more difficult than the first two, mainly because it's not symmetric, but relative to the one below  
//         It's easier to say, I didn't use it right m_direct The way I did it, I divided it up e_STAIR_BACK and e_STAIR_FRONT 
//         Two categories to discuss, and later found that the code can be reduced before it is classified as 1 Classes just remember block 【 0  】  and block 【 1 "Will not  
//         Change, change block 【 2  】  and block 【 3  】 , block 【 2 Relative  】  block 【 1 Up or down, x Coordinate with block 【 1 】  
//         The same, block 【 3 】 .y1 Straight in block 【 1  】  the following 1 Row, relative to its left and right  
// 
//e_CORNER  Angle   This square is personally the most difficult square to rotate, with the top 1 It's a different species. I originally divided it into e_CORNER_FRONT , e_CORNER_BACK 
//         Two classes, each of them 4 According to the change of the direction, the remainder can be the same 1 The change in direction becomes 1 Kind of, just block 【 3  【  Number square want  
//         According to the m_direct Direction to adjust  
 
int Block::rotate() 
{ 
      switch (m_style) 
      { 
        case e_LINE: 
        { 
          switch(m_direct) 
          { 
            case 0:// Transverse to longitudinal  
            { 
              for(int i = 0 ; i < 4 ; i ++) 
                { 
                  m_block[i].x = m_block[1].x; 
                  m_block[i].y = m_block[1].y + (1-i)*CellWidth; 
                } 
              m_direct = 1; 
            } 
              break; 
            case 1:// Vertical to horizontal  
            { 
              for(int i = 0 ; i < 4 ; i ++) 
              { 
                  m_block[i].y = m_block[1].y; 
                  m_block[i].x = m_block[1].x + (1-i)*CellWidth; 
              } 
              m_direct = 0; 
            } 
              break; 
          } 
        } 
          break; 
        // The following is the stair style square. Due to its asymmetric classification, there are two kinds of positive and negative styles, and there are two kinds of positive and negative styles.  
        //m_direct% == 0 It's the same change on both sides  
        case e_STAIR: 
        { 
          int flag; 
          flag = m_direct < 2 ? 1 : -1; 
          m_block[0].x = m_block[1].x + flag*CellWidth; 
          m_block[0].y = m_block[1].y; 
          m_block[2].x = m_block[1].x; 
          m_block[3].y = m_block[1].y + CellWidth; 
          if(m_direct%2 == 0) 
          { 
            m_block[2].y = m_block[1].y - CellWidth; 
            m_block[3].x = m_block[1].x + flag*CellWidth; 
            m_direct++; 
          } 
          else 
          { 
            m_block[2].y = m_block[1].y + CellWidth; 
            m_block[3].x = m_block[1].x - flag*CellWidth; 
            if(m_direct < 2) m_direct = 0; 
            else       m_direct = 2; 
          } 
        } 
          break; 
        // Corner square, with building trapezoidal square 1 It's asymmetrical. It has two heads and two tails, each of them 4 Kind of change,  
        // According to the below m_direct%4 These changes are sorted out by the value of, which corresponds to the same for both heads and tails  
        // The direction of change, only block 【 3 Square position no 1 You can see the comparison of the pictures I drew  
        case e_CORNER: 
        { 
          switch (m_direct%4) 
          { 
            case 0: 
            { 
              m_block[0].x = m_block[1].x+CellWidth; 
              m_block[0].y = m_block[2].y = m_block[1].y; 
              m_block[2].x = m_block[1].x-CellWidth; 
              m_block[3].x = m_block[1].x-CellWidth; 
              if(m_direct>=4) m_block[3].y = m_block[1].y-CellWidth; 
              else       m_block[3].y = m_block[1].y+CellWidth; 
              m_direct ++; 
            } 
              break; 
            case 1: 
            { 
              m_block[0].x = m_block[2].x = m_block[1].x; 
              m_block[0].y = m_block[1].y+CellWidth; 
              m_block[2].y = m_block[1].y-CellWidth; 
              if(m_direct>=4)   m_block[3].x = m_block[1].x+CellWidth; 
              else       m_block[3].x = m_block[1].x-CellWidth; 
              m_block[3].y = m_block[1].y-CellWidth; 
              m_direct ++; 
            } 
              break; 
            case 2: 
            { 
              m_block[0].x = m_block[1].x-CellWidth; 
              m_block[0].y = m_block[2].y = m_block[1].y; 
              m_block[2].x = m_block[1].x+CellWidth; 
              m_block[3].x = m_block[1].x+CellWidth; 
              if (m_direct>=4)  m_block[3].y = m_block[1].y+CellWidth; 
              else       m_block[3].y = m_block[1].y-CellWidth; 
 
              m_direct ++; 
            } 
              break; 
            case 3: 
            { 
              m_block[0].x = m_block[2].x = m_block[1].x; 
              m_block[0].y = m_block[1].y-CellWidth; 
              m_block[2].y = m_block[1].y+CellWidth; 
              if(m_direct>=4)  { m_block[3].x = m_block[1].x-CellWidth; m_direct = 4;} 
              else       { m_block[3].x = m_block[1].x+CellWidth; m_direct = 0;} 
              m_block[3].y = m_block[1].y+CellWidth; 
            } 
              break; 
            default: 
              break; 
          } 
 
        } 
          break; 
        case e_TANCK:// Tank - shaped squares, and line - shaped squares 1 The sample is symmetric 4 Kind of change  
        { 
          switch (m_direct%2) 
          { 
            case 0: 
            { 
              m_block[0].x = m_block[2].x = m_block[1].x; 
              m_block[0].y = m_block[1].y - CellWidth; 
              m_block[2].y = m_block[1].y + CellWidth; 
              int flag = m_direct == 0 ? 1 : -1; 
              m_block[3].x = m_block[1].x + flag*CellWidth; 
              m_block[3].y = m_block[1].y; 
              m_direct++; 
            } 
              break; 
            case 1: 
            { 
              m_block[0].y = m_block[2].y = m_block[1].y; 
              m_block[0].x = m_block[1].x - CellWidth; 
              m_block[2].x = m_block[1].x + CellWidth; 
              m_block[3].x = m_block[1].x; 
              int flag = m_direct == 3 ? -1:1; 
              m_block[3].y = m_block[1].y + flag*CellWidth; 
              if(m_direct == 3) m_direct = 0; 
              else m_direct++; 
 
            } 
              break; 
            default: 
              break; 
          } 
 
        } 
          break; 
        case e_TIAN: 
        { 
          m_block[0].y = m_block[1].y; 
          m_block[0].x = m_block[1].x + CellWidth; 
          m_block[2].x = m_block[1].x; 
          m_block[2].y = m_block[1].y + CellWidth; 
          m_block[3].x = m_block[1].x + CellWidth; 
          m_block[3].y = m_block[1].y + CellWidth; 
        } 
          break; 
 
        default: 
          break; 
      } 
 
        return 0; 
} 
int Block::show_block(HDC hdc,const POINT& GameLeftTop) 
{ 
  for (int i = 0 ; i < 4 ; i ++ ) 
  { 
    if(m_block[i].y >= GameLeftTop.y) 
      Rectangle(hdc,m_block[i].x,m_block[i].y,m_block[i]. 
           x+CellWidth,m_block[i].y+CellWidth); 
    if(i==0)// Test use, will be commented out after completion  
    {MoveToEx(hdc,m_block[i].x,m_block[i].y,NULL); 
    LineTo(hdc,m_block[i].x+CellWidth,m_block[i].y+CellWidth);} 
 
  } 
  return 1; 
} 
int Block::show_next_block(HDC hdc) 
{ 
   for (int i = 0 ; i < 4 ; i ++ ) 
  { 
 
    Rectangle(hdc,m_block[i].x,m_block[i].y,m_block[i]. 
           x+CellWidth,m_block[i].y+CellWidth); 
  } 
  return 1; 
} 
Block block , next_block , try_block; 
map_floor map;int d = 0; 
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
   HDC     hdc ; 
   PAINTSTRUCT ps ; 
   // Game client area  
   static RECT GameClient; 
   //1 The pixels of these squares are CellWidth = 20  The game zone width  12  A grid   high  18  A grid  
   const int  Width = 240 ,Height = 360; 
   static POINT LeftTopScrCdnt;// The upper left corner coordinates for the game area  
 
   switch (message) 
   { 
   case WM_CREATE: 
     SetTimer(hwnd,ID_TIMER,500,NULL); 
     return 0 ; 
   case WM_SIZE: 
     GetClientRect(hwnd,&GameClient); 
     LeftTopScrCdnt.x = (GameClient.right-GameClient.left)/2 - Width/2; 
     LeftTopScrCdnt.y = GameClient.top + 50; 
     GameClient.left  = LeftTopScrCdnt.x; 
     GameClient.top  = LeftTopScrCdnt.y; 
     GameClient.right = LeftTopScrCdnt.x + Width; 
     GameClient.bottom = LeftTopScrCdnt.y + Height; 
     // To create the 1 Three squares that will appear  
     next_block.create_block(GameClient.right+2*CellWidth,(GameClient.bottom+GameClient.top)/2-3*CellWidth); 
     block.move_to((GameClient.right+GameClient.left)/2,GameClient.top-CellWidth); 
 
     break; 
   case WM_TIMER: 
     block.move_down(GameClient); 
     if(!block.check_block(map,LeftTopScrCdnt))// Detect the collision of squares, if it indicates the bottom of the square, move it up and print it into the map  
     { 
       block.move_up(GameClient); 
       if(!block.check_block(map,LeftTopScrCdnt) || 
         block.get_block_height() <= LeftTopScrCdnt.y )// Check if the game is over  
         { 
           KillTimer(hwnd,ID_TIMER); 
           d = 4; 
         } 
       block.print_to_map(map,LeftTopScrCdnt); 
       SendMessage(hwnd,WM_KEYDOWN,VK_ESCAPE,0); 
     } 
     InvalidateRect(hwnd,NULL,true); 
     break; 
   case WM_PAINT: 
     hdc = BeginPaint (hwnd, &ps) ; 
     MoveToEx(hdc,LeftTopScrCdnt.x,LeftTopScrCdnt.y,NULL); 
     Rectangle(hdc,GameClient.left,GameClient.top,GameClient.right,GameClient.bottom);// Game area border  
     SelectObject(hdc,GetStockObject(BLACK_BRUSH)); 
     map.show_block_bar(hdc,LeftTopScrCdnt); 
     block.show_block(hdc,LeftTopScrCdnt); 
     next_block.show_next_block(hdc); 
     EndPaint (hwnd, &ps); 
     break; 
   case WM_KEYDOWN: 
     InvalidateRect(hwnd,NULL,true); 
     switch (wParam) 
     { 
      case VK_SPACE: 
      { 
        try_block = block; 
        try_block.rotate(); 
        if(try_block.check_block(map ,LeftTopScrCdnt)) 
          block = try_block; 
        break; 
      } 
      case VK_LEFT: 
      { 
        block.move_left(GameClient); 
        if(!block.check_block(map ,LeftTopScrCdnt)) 
          block.move_right(GameClient); 
      } 
        break; 
      case VK_RIGHT: 
      { 
        block.move_right(GameClient); 
        if (!block.check_block(map ,LeftTopScrCdnt)) 
          block.move_left(GameClient); 
      } 
        break; 
      case VK_DOWN: 
      { 
//        block.move_down(GameClient); 
         SendMessage(hwnd,WM_TIMER,0,0); 
      } 
        break; 
 
      case VK_ESCAPE:// Tests will be commented out when completed  
      { 
        block = next_block; 
        next_block.create_block(GameClient.right+2*CellWidth,(GameClient.bottom+GameClient.top)/2-3*CellWidth); 
        block.move_to((GameClient.right+GameClient.left)/2,GameClient.top-CellWidth); 
      } 
        break; 
      default: 
        break; 
     } 
     break; 
   case WM_DESTROY: 
     PostQuitMessage (0) ; 
     return 0 ; 
   } 
   return DefWindowProc (hwnd, message, wParam, lParam) ; 
} 

Related articles: