Click here to Skip to main content
15,896,278 members
Articles / Desktop Programming / MFC

Adding high score capability to MS Solitaire

Rate me:
Please Sign up or sign in to vote.
4.70/5 (16 votes)
25 Jul 2007CPOL14 min read 44.1K   713   18  
An application that manages MS Solitaire high scores by reading and writing Solitaire memory
#include "stdafx.h"
#include "solitairehighscore.h"
#include "aboutdlg.h"
#include "settingsdlg.h"
#include "ntray.h"
#include "MainFrm.h"
#include "usernamedlg.h"
#include "highscoredlg.h"
#include <afxdisp.h>        // MFC Automation classes

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

////////////////////////////////////// Implementation /////////////////////////////////

////////////////////////////////////// Message map   /////////////////////////////////
BEGIN_MESSAGE_MAP(CSolitaireHighscoreApp, CWinAppEx)
	//{{AFX_MSG_MAP(CSolitaireHighscoreApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	ON_COMMAND(ID_APP_CHECKHIGHSCORE, OnCheckHighscore)
	ON_COMMAND(ID_APP_SHOWSCORES, OnShowHighscores)	
	ON_COMMAND(ID_APP_NOTSCORE, OnNotScore)
	ON_COMMAND(ID_APP_HELP, OnHelp)
	ON_COMMAND(ID_APP_SETTINGS, OnSettings)
	ON_COMMAND(ID_APP_STARTSOLITAIRE, OnStartSolitaire)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

////////////////////////////////////// Construction   /////////////////////////////////

STHighscoreManager::InstPtr STHighscoreManager::sm_ptr;

CSolitaireHighscoreApp::CSolitaireHighscoreApp()
{
	m_hsMgr = STHighscoreManager::Instance();
	m_hsMgr->OnHighscoreCreatedEvent += new HighscoreEvent::T<CSolitaireHighscoreApp>(this, &CSolitaireHighscoreApp::OnHighscoreCreated);
	m_hsMgr->OnNotHighscoreEvent     += new HighscoreEvent::T<CSolitaireHighscoreApp>(this, &CSolitaireHighscoreApp::OnNotHighscore);
	m_hsMgr->OnHighscoreDeletedEvent += new HighscoreEvent::T<CSolitaireHighscoreApp>(this, &CSolitaireHighscoreApp::OnHighscoreDeleted); 
}

////////////////////////////////////// App instance   /////////////////////////////////
CSolitaireHighscoreApp theApp;

BOOL CSolitaireHighscoreApp::InitInstance()
{
	if(!CWinAppEx::InitInstance( _T("{AF7CDDE4-8CD4-4f87-8834-F83C72939EA0}")))
		return FALSE;

	CMainFrame* pMainFrame = new CMainFrame;
    m_pMainWnd = pMainFrame;
    if (!pMainFrame->Create(NULL,SOLITAIRE_NAME))
		return FALSE;

	// application settings
	m_setMgr.Init();
	m_setMgr.ReadSettings();
	if (m_setMgr.GetFirstRun())
	{
		OnNotScore();
		m_setMgr.SetFirstRun(FALSE);
		m_setMgr.SetStartup(TRUE);
		m_setMgr.SetStartSolitaire(TRUE);
	}
	if (m_setMgr.GetStartSolitaire())
	{
		StartSolitaire(TRUE);
		// we can leave a place here to load best highscore...
	}
	m_pMainWnd->ShowWindow(SW_HIDE);
	m_pMainWnd->UpdateWindow();
	return TRUE;
}

////////////////////////////////////// App overrides   /////////////////////////////////
BOOL CSolitaireHighscoreApp::PreTranslateMessage(MSG* pMsg) 
{
	if( pMsg->message == WM_KEYDOWN )
    {
		if (CTRL_W == pMsg->wParam) // CTRL + W down
		{
			WriteDefDeal();
		}
    }
    return CWinAppEx::PreTranslateMessage(pMsg);
}

////////////////////////////////////// Menu handlers   /////////////////////////////////
void CSolitaireHighscoreApp::OnAppAbout()
{
	//Show the window
    CAboutDlg aboutdlg;
	((CMainFrame*)m_pMainWnd)->EnableMenu(FALSE);
    aboutdlg.DoModal();
	((CMainFrame*)m_pMainWnd)->EnableMenu(TRUE);
}

void CSolitaireHighscoreApp::OnCheckHighscore()
{
	CheckHighscore();
}

void CSolitaireHighscoreApp::OnShowHighscores()
{
	((CMainFrame*)m_pMainWnd)->EnableMenu(FALSE);
	CHighscoreDlg dlg;
	dlg.OnLoadHighScoreEvent += new LoadHighscoreEvent::T<CSolitaireHighscoreApp>(this, &CSolitaireHighscoreApp::OnLoadHighscore);
	dlg.DoModal();
	((CMainFrame*)m_pMainWnd)->EnableMenu(TRUE);
}

void CSolitaireHighscoreApp::OnHelp()
{
	CString error;
	CString strFileName = CString(m_pszHelpFilePath).TrimRight(_T(".HLP"))+".htm";
	error.Format(_T("Error openning file : \r\n%s"),strFileName);
	// open help file
	if( (int)(ShellExecute(NULL,
		                   CString("open"),
				           strFileName,
				           NULL,
				           NULL,
				           0)) <= 32 )
					       ShowMessageBox(error, MB_OK|MB_ICONEXCLAMATION);
}

void CSolitaireHighscoreApp::OnNotScore()
{
	int icon = MB_OKCANCEL|MB_ICONINFORMATION;
	if (m_setMgr.GetFirstRun())
		icon = MB_OK|MB_ICONINFORMATION;
	CString msg;
	msg.Format(_T("%s will try and scan MS Solitaire.\r\nIf MS Solitaire is running, please close it.\r\nPlease wait until the scan is complete."), SOLITAIRE_NAME);
	if (ShowMessageBox(msg, icon) == IDOK)
	{
		icon = MB_OK|MB_ICONEXCLAMATION;
		((CMainFrame*)m_pMainWnd)->EnableMenu(FALSE);
		int nRes = ScanForAddresses();
		if (nRes == RES_SUCC_ALL)
		{
			icon = MB_OK|MB_ICONINFORMATION;
			msg = _T("Scan succeeded.\r\nPlease try checking highscore again now.");
		}
		if (nRes == RES_SOL_NOT_RUN)
			msg.Format(IDS_SOLNOTRUNNING);
		if ((nRes & RES_FAIL_SCORE) != 0)
			msg = _T("Scan failed.\r\nPlease contact support (click About...)");
		/*if ((nRes & RES_FAIL_DEAL) != 0)
			msg = _T("Scan failed.\r\nPlease contact support (click About...)");*/
		ShowMessageBox(msg, icon);
		((CMainFrame*)m_pMainWnd)->EnableMenu(TRUE);
	}
}

void CSolitaireHighscoreApp::OnSettings()
{
	// init setting dialog
    CSettingsDlg setdlg;
	setdlg.UserName = m_setMgr.GetUserName();
	setdlg.Startup = m_setMgr.GetStartup();
	setdlg.StartSolitaire = m_setMgr.GetStartSolitaire();
	((CMainFrame*)m_pMainWnd)->EnableMenu(FALSE);
	//Show the window
	if (setdlg.DoModal() == IDOK)
	{
		m_setMgr.SetStartup(setdlg.Startup);
		m_setMgr.SetStartSolitaire(setdlg.StartSolitaire);
		m_setMgr.SetUserName(setdlg.UserName);
	}
	((CMainFrame*)m_pMainWnd)->EnableMenu(TRUE);
}

void CSolitaireHighscoreApp::OnStartSolitaire()
{
	((CMainFrame*)m_pMainWnd)->EnableMenu(FALSE);
	StartSolitaire(TRUE);
	((CMainFrame*)m_pMainWnd)->EnableMenu(TRUE);
}

////////////////////////////////////// Score handlers   /////////////////////////////////
#pragma warning(disable:4100)
void CSolitaireHighscoreApp::OnHighscoreCreated(long lScore)
{
	// we could call update only once after show and then we don't
	// need the on deleted call, but this is the correct way, since
	// we are not certain that we didn't delete scores elsewhere.
	m_setMgr.UpdateHighscoreSettings();
	OnShowHighscores();
}

void CSolitaireHighscoreApp::OnNotHighscore(long lScore)
{
	CString msg;
	if (lScore > 0)
		msg.Format(_T("Sorry.\r\n%d is not a highscore"), lScore);
	else
		msg.Format(_T("%d is not a highscore it's a low score.\r\nTry again when your score is above 0."), lScore);
	ShowMessageBox(msg, MB_OK|MB_ICONINFORMATION);
}

void CSolitaireHighscoreApp::OnHighscoreDeleted(long lScore)
{
	m_setMgr.UpdateHighscoreSettings();
}
#pragma warning(default:4100)

void CSolitaireHighscoreApp::OnLoadHighscore(long lScore)
{
	HANDLE hProc = m_procMgr.GetProcessHandle(SOLITAIRE_EXE);
	if (hProc != NULL)
	{
		m_procMgr.WriteProcessAddress(hProc, m_setMgr.GetScoreAddress(), lScore);
		HWND hWnd = FindSolitaireWindow();
		if (hWnd != NULL)
		{	// will cause a redraw
			SendMessage(hWnd, WM_SIZE, SIZE_MINIMIZED, 0);
		}
		m_procMgr.CloseProcess(hProc);
	}
	else
	{
		CString msg;
		msg.Format(IDS_SOLNOTRUNNING);
		ShowMessageBox(msg, MB_OK|MB_ICONEXCLAMATION);
	}
}////////////////////////////////////// What's this???   /////////////////////////////////
void CSolitaireHighscoreApp::WriteDefDeal()
{
	if (m_setMgr.GetDefaultDealValue() == -52)
		m_setMgr.SetDefaultDealValue(100);
	else
		m_setMgr.SetDefaultDealValue(-52);

	HANDLE hProc = m_procMgr.GetProcessHandle(SOLITAIRE_EXE);
	if (hProc != NULL)
	{
		m_procMgr.WriteProcessAddress(hProc, DEALSUB_DEF_ADDR, m_setMgr.GetDefaultDealValue());
		m_procMgr.CloseProcess(hProc);
	}	
}

////////////////////////////////////// Helper functions  /////////////////////////////////
int CSolitaireHighscoreApp::ShowMessageBox(const CString& msg, UINT nType)
{
	int iRes = 0;
	((CMainFrame*)m_pMainWnd)->EnableMenu(FALSE);
	iRes = m_pMainWnd->MessageBox(msg, SOLITAIRE_NAME, nType);
	((CMainFrame*)m_pMainWnd)->EnableMenu(TRUE);
	return iRes;
}

void CSolitaireHighscoreApp::CheckHighscore()
{
	HANDLE hProc = m_procMgr.GetProcessHandle(SOLITAIRE_EXE);
	if (hProc != NULL)
	{
		long lScore = m_procMgr.ReadProcessAddress(hProc, m_setMgr.GetScoreAddress());
		m_hsMgr->CreateHighscore(m_setMgr.GetUserName(), lScore);
		m_procMgr.CloseProcess(hProc);
	}
	else
	{
		CString msg;
		msg.Format(IDS_SOLNOTRUNNING);
		ShowMessageBox(msg, MB_OK|MB_ICONEXCLAMATION);
	}
}

BOOL CSolitaireHighscoreApp::StartSolitaire(BOOL bShow)
{
	HANDLE hProc = NULL;
	TCHAR szPath[MAX_PATH] = {'\0'};
	if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, 0, szPath))) 
	{
		PathAppend(szPath, TEXT(SOLITAIRE_EXE));
		CString fileName(szPath);
		hProc = m_procMgr.CreateNewProcess(fileName, bShow);
	}
	return (hProc != NULL);
}

void CSolitaireHighscoreApp::PostSolitaireDealMsg()
{
	HWND hWnd = FindSolitaireWindow();
	if (hWnd != NULL)
		PostMessage(hWnd, WM_KEYDOWN, VK_F2, 0);
}

BOOL CSolitaireHighscoreApp::ScanForAddresses()
{
	long lOptions = m_setMgr.GetMsSolitaireOptions();
	m_setMgr.SetMsSolitaireOptions(MSSOLITAIRE_OPTIONS_VEGAS); // vegas cumulative
#ifdef _DEBUG
	StartSolitaire(TRUE);
#else
	StartSolitaire(FALSE);
#endif
	PostSolitaireDealMsg();
	HANDLE hProc = m_procMgr.GetProcessHandle(SOLITAIRE_EXE);
	int nRes = RES_SUCC_ALL;
	if (hProc != NULL)
	{
		// looking for the score address in the data segment
		long lValPtr = m_procMgr.FindValueInProcessMem(hProc, 0x000a000, 0x000bffff, -104);
		if (-1 != lValPtr)
			m_setMgr.SetScoreAddress(lValPtr);
		else
			nRes = RES_FAIL_SCORE;
		// looking for the deal decrease
		lValPtr = m_procMgr.FindValueInProcessMem(hProc, 0x01006000, 0x01010000, -52);
		if (-1 != lValPtr)
			m_setMgr.SetDefaultDealAddress(lValPtr);
		else
			nRes |= RES_FAIL_DEAL;

		// close original
		m_procMgr.KillProcess(hProc);
		m_procMgr.CloseProcess(hProc);
		// kill other running instances of solitaire
		while ( (hProc = m_procMgr.GetProcessHandle(SOLITAIRE_EXE)) != NULL)
		{
			m_procMgr.KillProcess(hProc);
			m_procMgr.CloseProcess(hProc);
		}
		// restore ms solitaire options
		m_setMgr.SetMsSolitaireOptions(lOptions);
	}
	else
	{
		nRes = RES_SOL_NOT_RUN;
	}
	return nRes;
}

HWND CSolitaireHighscoreApp::FindSolitaireWindow()
{
	int scanNum = 100;
	CString strSol(SOLITAIRE);
	HWND hWnd = NULL;
	// we'll try and find the window for about 10 seconds
	for (int i=0; i < scanNum; i++)
	{
		if ((hWnd = FindWindow(strSol, NULL)) != NULL)
		{
			break;
		}
		else
		{
			Sleep(100);
		}
	}
	return hWnd;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Israel Israel
Software designer and programmer.
Programming languages:
MFC, C++, Java , C#, VB and sometimes C and assembly.

Comments and Discussions