VOID ShowMineSweeperBoard() { // Easily work with the RAM of winmine.exe by declaring variables as “extern”. extern struct BOARDSIZE { INT iWidth; INT iHeight; } e_bwSize = { "winmine.exe", 0x5334 }; // The width and height of the board. extern BYTE e_pbBoard[32*32] = { "winmine.exe", 0x5361 }; // The board array itself. // Note that 32*32 here is meaningless, except to show us how // large the array is in the target process (it does not actually // declare the array in the target process; it already exists). CHAR * pcRow = Malloc( e_bwSize.iWidth + 1 ); // Allocate a string long enough to handle the longest row print-out. // No cast is required; it is done for us. PrintF( "%X %X", e_pbBoard[0], e_pbBoard[1] ); PrintF( "%d×%d", e_bwSize.iWidth, e_bwSize.iHeight ); // Print the width and height of the board. for ( INT J = 0; J < e_bwSize.iHeight; J++ ) { for ( INT I = 0; I < e_bwSize.iWidth; I++ ) { pcRow[I] = (e_pbBoard[(32*J)+I] & 0x80) ? '1' : '0'; // Set the current character to a 1 or a 0 depending on // whether there is a mine here or not. 1 indicates a mine. } pcRow[e_bwSize.iWidth] = '\0'; // Terminate with NULL. PrintF( pcRow ); // Print the line. } Free( pcRow ); // Just as in C, you must free your resources. SolveMinesweeperBoard(); } VOID SolveMinesweeperBoard() { // “extern” only works if we are attached to the process. if ( StrICmp( GetCurProcessName(), "winmine.exe" ) != 0 ) { PrintF( "You must be attached to Minesweeper to solve the board!" ); return; } HWND hWnd = FindWindow( "Minesweeper" ); if ( !hWnd ) { return; } // If the user is holding certain keys it will cause problems when we try to send clicks // to the Minesweeper board. Release problematic keys. Even if the user is still holding // them, this forces them to be “released”. KeyboardEvent( VK_SHIFT, KEYEVENTF_KEYUP ); KeyboardEvent( VK_CONTROL, KEYEVENTF_KEYUP ); KeyboardEvent( VK_MENU, KEYEVENTF_KEYUP ); // Put the Minesweeper board in front if possible. ShowWindowAsync( hWnd, SW_RESTORE ); while ( GetForegroundWindow() != hWnd ) { SetForegroundWindow( hWnd ); Sleep( 10 ); } // Get its position. INT iX; INT iY; GetWindowPos( hWnd, &iX, &iY ); PrintF( "Minesweeper board located at %d %d.", iX, iY ); if ( iX < 0 || iY < 0 ) { PrintF( "Minesweeper board is out of range!" ); return; } extern struct BOARDSIZE { INT iWidth; INT iHeight; } e_bwSize = { "winmine.exe", 0x5334 }; // The width and height of the board. extern BYTE e_pbBoard[32*32] = { "winmine.exe", 0x5361 }; // The board array itself. // Note that 32*32 here is meaningless, // except to show us how large the // array is in the target process (it // does not actually declare the array // in the target process; it already // exists). PrintF( "Minesweeper size %d %d.", e_bwSize.iWidth, e_bwSize.iHeight ); POINT pClick; // To calculate the final click positions we need the screen // resolution. INT iScreenW = GetPrimScreenWidth(); INT iScreenH = GetPrimScreenHeight(); // System settings change the location of mines on the board, so // get the settings needed to know where to click. INT iBorderHeight = GetSystemMetrics( SM_CYEDGE ); // Window border (height). INT iCaptionHeight = GetSystemMetrics( SM_CYCAPTION ); // Title bar height. INT iMenuHeight = GetSystemMetrics( SM_CYMENU ); // Menu height. INT iFinalYOffset = iBorderHeight + iCaptionHeight + iMenuHeight; INT iFinalXOffset = GetSystemMetrics( SM_CXEDGE ); // Window border (width). // Save the externs to locals. This makes it faster to scan the // board. INT iBoardWidth = e_bwSize.iWidth; INT iBoardHeight = e_bwSize.iHeight; // Run through the board and click on each square that has no mine. for ( INT J = 0; J < iBoardHeight; J++ ) { for ( INT I = 0; I < iBoardWidth; I++ ) { if ( (e_pbBoard[(32*J)+I] & 0x80) == 0 ) { GetClickPos( iFinalXOffset, iFinalYOffset, iX, iY, &pClick, I, J ); // Translate the click position into the actual screen coordinates (Windows® // forces us to do this). pClick.x = pClick.x * 65535.0f / iScreenW; pClick.y = pClick.y * 65535.0f / iScreenH; // Move there. MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, pClick.x, pClick.y, 0 ); // Click there (down and up). MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN, 0, 0, // X and Y positions aren’t used. 0 ); MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP, 0, 0, 0 ); // Optionally sleep for a bit in case you like to see the action unfold. //Sleep( 1 ); } } } } // Constants that tell us the dimensions of the board. const INT iSquareSize = 16; // Size of the actual squares we click. const INT iXOff = 14; // Offset to the left-most square from the border. const INT iYOff = 56; // Offset to the top-most square from the bottom of the menu. const INT iFinalX = iXOff + (iSquareSize / 2);// The final click offset (X). const INT iFinalY = iYOff + (iSquareSize / 2);// The final click offset (Y). VOID GetClickPos( INT iScreenOffX, INT iScreenOffY, INT iPosX, INT iPosY, POINT * ppRet, INT iX, INT iY ) { // The window is at (iPosX, iPosY). From there, go to the corner of the board // and then offset based on which square we are hitting. // iScreenOffX and iScreenOffY are derived by using system settings to get the // border, caption, and menu widths/heights. ppRet->x = iPosX + iX * iSquareSize + iFinalX + iScreenOffX; ppRet->y = iPosY + iY * iSquareSize + iFinalY + iScreenOffY; }