
// testVirtualMemoryUnusableDlg.cpp : implementation file
//

#include "stdafx.h"
#include "testVirtualMemoryWasted.h"
#include "testVirtualMemoryWastedDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define COL_ADDRESS	0
#define COL_SIZE	1
#define COL_STATE	2
#define COL_TYPE	3
#define COL_PROTECT	4

// CAboutDlg dialog used for App About

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CtestVirtualMemoryUnusableDlg dialog

CtestVirtualMemoryUnusableDlg::CtestVirtualMemoryUnusableDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CtestVirtualMemoryUnusableDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CtestVirtualMemoryUnusableDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CtestVirtualMemoryUnusableDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_DEALLOCATE, &CtestVirtualMemoryUnusableDlg::OnBnClickedButtonDeallocate)
END_MESSAGE_MAP()


// CtestVirtualMemoryUnusableDlg message handlers

BOOL CtestVirtualMemoryUnusableDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	comboSize.SubclassDlgItem(IDC_COMBO_SIZE, this);
	comboAction.SubclassDlgItem(IDC_COMBO_FOLLOWING_BLOCK, this);
	list.SubclassDlgItem(IDC_LIST_ALLOCATIONS, this);

	int	ix;
	
	ix = list.InsertColumn(COL_ADDRESS, _T("Address"), LVCFMT_LEFT, 130, 0);
	ix = list.InsertColumn(COL_SIZE, _T("Size"), LVCFMT_LEFT, 80, 1);
	ix = list.InsertColumn(COL_STATE, _T("State"), LVCFMT_LEFT, 100, 1);
	ix = list.InsertColumn(COL_TYPE, _T("Type"), LVCFMT_LEFT, 100, 1);
	ix = list.InsertColumn(COL_PROTECT, _T("Protect"), LVCFMT_LEFT, 100, 1);
	list.Invalidate(FALSE);
	list.UpdateWindow();

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	SetDlgItemInt(IDC_EDIT_COUNT, 10, FALSE);

	addToCombo(comboSize, 1);				// size that is not a multiple of 4K
	addToCombo(comboSize, 512);				// size that is not a multiple of 4K
	addToCombo(comboSize, 1024 * 1);		// size that is not a multiple of 4K
	addToCombo(comboSize, 1024 * 2);		// size that is not a multiple of 4K
	addToCombo(comboSize, 1024 * 4);
	addToCombo(comboSize, (1024 * 4) + 1);	// size that is not a multiple of 4K
	addToCombo(comboSize, 1024 * 6);		// size that is not a multiple of 4K
	addToCombo(comboSize, 1024 * 8);
	addToCombo(comboSize, 1024 * 10);		// size that is not a multiple of 4K
	addToCombo(comboSize, 1024 * 16);
	addToCombo(comboSize, 1024 * 24);
	addToCombo(comboSize, 1024 * 32);
	addToCombo(comboSize, 1024 * 64);
	addToCombo(comboSize, 1024 * 96);
	addToCombo(comboSize, 1024 * 128);
	addToCombo(comboSize, 1024 * 192);
	comboSize.SetCurSel(7);

	addToCombo(comboAction, _T("Allocator1: VirtualAlloc() Leak"), FBA_ALLOCATOR1_VIRTUAL_ALLOC_LEAK);
	addToCombo(comboAction, _T("Allocator2: Simple Heap Manager"), FBA_ALLOCATOR2_SIMPLE_HEAP_MANAGER);

	comboAction.SetCurSel(0);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CtestVirtualMemoryUnusableDlg::addToCombo(CComboBox	&combo,
											   DWORD		size)
{
	int		ix;
	CString	str;

	str.Format(_T("%u"), size);
	ix = combo.AddString(str);
	combo.SetItemData(ix, size);
}

void CtestVirtualMemoryUnusableDlg::addToCombo(CComboBox				&combo,
											   const TCHAR				*str,
											   FOLLOWING_BLOCK_ACTION	action)
{
	int		ix;

	ix = combo.AddString(str);
	combo.SetItemData(ix, (DWORD)action);
}

void CtestVirtualMemoryUnusableDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CtestVirtualMemoryUnusableDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CtestVirtualMemoryUnusableDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CtestVirtualMemoryUnusableDlg::OnOK()
{
	FOLLOWING_BLOCK_ACTION	action;
	DWORD					count;
	DWORD					i;
	DWORD					size;
	int						ix;
	int						reserveWholeParagraphFirst;

	count = GetDlgItemInt(IDC_EDIT_COUNT, NULL, FALSE);
	reserveWholeParagraphFirst = IsDlgButtonChecked(IDC_CHECK_RESERVE_FIRST);

	ix = comboSize.GetCurSel();
	size = (DWORD)comboSize.GetItemData(ix);

	ix = comboAction.GetCurSel();
	action = (FOLLOWING_BLOCK_ACTION)comboAction.GetItemData(ix);

	if (action == FBA_ALLOCATOR2_SIMPLE_HEAP_MANAGER)
	{
		for(i = 0; i < count; i++)
		{
			allocateVM(size);
		}
	}
	else
	{
		for(i = 0; i < count; i++)
		{
			addAllocation(size, action, reserveWholeParagraphFirst);
		}
	}
}


void CtestVirtualMemoryUnusableDlg::OnCancel()
{
	CDialogEx::OnCancel();
}


void CtestVirtualMemoryUnusableDlg::OnBnClickedButtonDeallocate()
{
	flush();
}

void CtestVirtualMemoryUnusableDlg::displayData(void	*ptr, 
												DWORD	size)
{
	int		ix;
	CString	str;

	str.Format(_T("0x%p"), ptr);

	ix = list.InsertItem(list.GetItemCount(), str);
	if (ix != -1)
	{
		str.Format(_T("%u"), size);
		list.SetItemText(ix, COL_SIZE, str);

		MEMORY_BASIC_INFORMATION	mbi;

		if (VirtualQuery(ptr, &mbi, sizeof(mbi)) != 0)
		{
			CString	state, type, protect;

			state.Format(_T("0x%08x "), mbi.State);
			type.Format(_T("0x%08x "), mbi.Type);
			protect.Format(_T("0x%08x "), mbi.Protect);

			list.SetItemText(ix, COL_STATE, state);
			list.SetItemText(ix, COL_TYPE, type);
			list.SetItemText(ix, COL_PROTECT, protect);
		}

		list.Invalidate(FALSE);
		list.UpdateWindow();
	}
}

static const DWORD PAGE_SIZE			= 1024 * 4;		// Memory is protected in 4KB chunks
static const DWORD VIRTUAL_ALLOC_SIZE	= 1024 * 64;	// VirtualAlloc() allocates in 64KB chunks

void CtestVirtualMemoryUnusableDlg::addAllocation(DWORD						size,
												  FOLLOWING_BLOCK_ACTION	action,
												  int						reserveWholeParagraphFirst)
{
	void	*ptr;
	void	*theAddr = NULL;

	if (reserveWholeParagraphFirst)
	{
		DWORD	paraSize;

		paraSize = size % VIRTUAL_ALLOC_SIZE;
		if (paraSize != 0)
			paraSize = VIRTUAL_ALLOC_SIZE - paraSize;
		paraSize += size;

		theAddr = VirtualAlloc(theAddr, paraSize, MEM_RESERVE, PAGE_NOACCESS);
	}

	ptr = VirtualAlloc(theAddr, size, MEM_COMMIT, PAGE_READWRITE);
	if (ptr != NULL)
	{
		allocations.push_back(ptr);

		displayData(ptr, size);

		// if size is not on a 64K boundary, get the memory protection for the region immediately after this memory allocation

		if (size % (VIRTUAL_ALLOC_SIZE) != 0)
		{
			DWORD_PTR	addr;
			DWORD		diff;

			addr = (DWORD_PTR)ptr;
			addr += size;
			ptr = (void *)addr;

			diff = size;
			while(diff > VIRTUAL_ALLOC_SIZE)
				diff -= VIRTUAL_ALLOC_SIZE;

			diff = VIRTUAL_ALLOC_SIZE - diff;
			displayData(ptr, diff);
		}
	}
}

void CtestVirtualMemoryUnusableDlg::flush()
{
	std::vector<void *>::iterator	iter;

	for(iter = allocations.begin(); iter != allocations.end(); iter++)
		VirtualFree(*iter, 0, MEM_RELEASE);

	allocations.clear();

	list.DeleteAllItems();
	list.UpdateWindow();
}

// crude VirtualAlloc memory manager

static void  *reservedPtr = NULL;
static DWORD reservedSize = 0;

void *CtestVirtualMemoryUnusableDlg::allocateVM(DWORD	size)
{
	void	*ptr = NULL;

	// reserve the memory, if necessary

	if (reservedSize == 0)
	{
		reservedPtr = VirtualAlloc(NULL, VIRTUAL_ALLOC_SIZE, MEM_RESERVE, PAGE_NOACCESS);
		if (reservedPtr != NULL)
		{
			reservedSize = VIRTUAL_ALLOC_SIZE;
			allocations.push_back(reservedPtr);
		}
	}

	// allocate from the reserved memory
	
	if (reservedSize != 0)
	{
		ptr = VirtualAlloc(reservedPtr, size, MEM_COMMIT, PAGE_READWRITE);
		if (ptr != NULL)
		{
			DWORD	vaSize;
			
			// calculate the size of the memory committed (if not a multiple of the page size, it will be rounded up to the next page size)
			
			vaSize = size % PAGE_SIZE;
			if (vaSize != 0)
				vaSize = PAGE_SIZE - vaSize;
			vaSize += size;
			
			displayData(ptr, vaSize);

			// step along to where the next reserved location
			
			reservedPtr = (void *)((DWORD_PTR)reservedPtr + vaSize);	
			reservedSize -= vaSize;
		}
	}

	return ptr;
}
