// RTSSSharedMemorySampleDlg.cpp : implementation file
//
// created by Unwinder
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "RTSSSharedMemorySample.h"
#include "RTSSSharedMemorySampleDlg.h"

#include "RTSSSharedMemory.h"
#include <shlwapi.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
/////////////////////////////////////////////////////////////////////////////
class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}
/////////////////////////////////////////////////////////////////////////////
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}
/////////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CRTSSSharedMemorySampleDlg dialog
/////////////////////////////////////////////////////////////////////////////
CRTSSSharedMemorySampleDlg::CRTSSSharedMemorySampleDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CRTSSSharedMemorySampleDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CRTSSSharedMemorySampleDlg)
	m_strStatus = _T("");
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	m_hMapFile = NULL;
	m_pMapAddr = NULL;
	m_nTimerID = NULL;

	m_strAppID.Format("RTSSSharedMemorySample%08X", GetTickCount());
		//to demonstrate connection to multiple OSD slots we'll use timer
		//dependent OSD slot owner name

		//this will allow mutiple application instances to connect to different slots

	m_bRTSSSharedMemoryV2 = FALSE;
}
/////////////////////////////////////////////////////////////////////////////
void CRTSSSharedMemorySampleDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CRTSSSharedMemorySampleDlg)
	DDX_Text(pDX, IDC_STATUS, m_strStatus);
	//}}AFX_DATA_MAP
}
/////////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CRTSSSharedMemorySampleDlg, CDialog)
	//{{AFX_MSG_MAP(CRTSSSharedMemorySampleDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_TIMER()
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CRTSSSharedMemorySampleDlg message handlers
/////////////////////////////////////////////////////////////////////////////
BOOL CRTSSSharedMemorySampleDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// 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)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		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
	
	m_nTimerID = SetTimer(0x1234, 1000, NULL);
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}
/////////////////////////////////////////////////////////////////////////////
void CRTSSSharedMemorySampleDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::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 CRTSSSharedMemorySampleDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (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
	{
		CDialog::OnPaint();
	}
}
/////////////////////////////////////////////////////////////////////////////
// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
/////////////////////////////////////////////////////////////////////////////
HCURSOR CRTSSSharedMemorySampleDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}
/////////////////////////////////////////////////////////////////////////////
// This function is used to \ to RivaTunerStatisticsServer's
// shared memory
/////////////////////////////////////////////////////////////////////////////
void CRTSSSharedMemorySampleDlg::Connect()
{
	Disconnect();
		//we must disconnect from the previously connected shared memory before
		//connecting to new one

	m_hMapFile				= OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "RTSSSharedMemory");
		//first, try to connect to v1.x shared memory
	m_bRTSSSharedMemoryV2	= FALSE;
	
	if (!m_hMapFile)
		//if the connection failed, try to connect to v2.x shared memory

		//Take a note that v1.x and v2.x RTSS shared memory layouts differ drastically, that is
		//why the applications supporting RTSS shared memory v1.x will not work with the server
		//using v2.x shared memory layout. 
		//RTSS shared memory v2.x uses different name to prevent old applications from crashing
		//due to accessing new memory layout improperly 

		//Our sample supports both v1.x and v2.x layouts, we'll use m_bRTSSSharedMemoryV2 flag
		//to let the application to distinguish them
	{
		m_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "RTSSSharedMemoryV2");
		if (m_hMapFile)
			m_bRTSSSharedMemoryV2 = TRUE;
	}

	if (m_hMapFile)
		m_pMapAddr = MapViewOfFile(m_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
}
/////////////////////////////////////////////////////////////////////////////
// This function is used to disconnect from RivaTunerStatisticsServer's
// shared memory
/////////////////////////////////////////////////////////////////////////////
void CRTSSSharedMemorySampleDlg::Disconnect()
{
	if (m_pMapAddr)
	{
		if (m_bRTSSSharedMemoryV2)
			//v2.x shared memory specific disconnection codepath
		{
			LPRTSS_SHARED_MEMORY lpMem = (LPRTSS_SHARED_MEMORY)m_pMapAddr;

			if (lpMem->dwSignature == 'RTSS')
				//if we're disconnecting from the shared memory, which is currently in use by the server,
				//we _must_ release all OSD text slots we've captured before
			{
				for (DWORD dwOSD=0; dwOSD<lpMem->dwOSDArrSize; dwOSD++)
					//run though all OSD slots and free the slots we've captured
				{
					RTSS_SHARED_MEMORY::LPRTSS_SHARED_MEMORY_OSD_ENTRY pEntry = GetOSDEntry(lpMem, dwOSD);
						//get ptr to OSD slot descriptor entry

						//Take a note that we don't access entries array directly to
						//ensure that the sample will work even if entry format/size
						//will change in future versions

						//It is _strongly_ recommended to use similar behavior in your code

					if (!strcmp(pEntry->szOSDOwner, m_strAppID))
						//check if the slot is captured by our sample and release it if necessary
					{
						strcpy(pEntry->szOSDOwner	, "");
						strcpy(pEntry->szOSD		, "");

						lpMem->dwOSDFrame++;
							//tell the server that it is necessary to update OSD
					}
				}
			}
		}
		else
			//v1.x shared memory specific disconnection codepath
		{
			LPRTSS_SHARED_MEMORY_V_1_3 lpMem = (LPRTSS_SHARED_MEMORY_V_1_3)m_pMapAddr;

			if (lpMem->dwSignature == 'RTSS')
				//if we're disconnecting from the shared memory, which is currently in use by the server,
				//we _must_ release all OSD text slots we've captured before
			{
				if (lpMem->dwVersion >= 0x00010001)
					//server's shared memory v1.1 has only one text slot
					if (!strcmp(lpMem->szOSDOwner, m_strAppID))
						//check if the slot is captured by our sample and release it if necessary
					{
						strcpy(lpMem->szOSDOwner, "");
						strcpy(lpMem->szOSD		, "");

						lpMem->dwOSDFlags |= OSDFLAG_UPDATED;
							//tell the server that it is necessary to update OSD
					}

				if (lpMem->dwVersion >= 0x00010002)
					//server's shared memory v1.2 has 4 text slots, but our sample connects to slots 1-3 only
				{
					if (!strcmp(lpMem->szOSD1Owner, m_strAppID))
						//check if the slot 1 is captured by our sample and release it if necessary
					{
						strcpy(lpMem->szOSD1Owner	, "");
						strcpy(lpMem->szOSD1		, "");

						lpMem->dwOSDFlags |= OSDFLAG_UPDATED;
							//tell the server that it is necessary to update OSD
					}

					if (!strcmp(lpMem->szOSD2Owner, m_strAppID))
						//check if the slot 2 is captured by our sample and release it if necessary
					{
						strcpy(lpMem->szOSD2Owner	, "");
						strcpy(lpMem->szOSD2		, "");

						lpMem->dwOSDFlags |= OSDFLAG_UPDATED;
							//tell the server that it is necessary to update OSD

					}
					if (!strcmp(lpMem->szOSD3Owner, m_strAppID))
						//check if the slot 3 is captured by our sample and release it if necessary
					{
						strcpy(lpMem->szOSD3Owner	, "");
						strcpy(lpMem->szOSD3		, "");

						lpMem->dwOSDFlags |= OSDFLAG_UPDATED;
							//tell the server that it is necessary to update OSD
					}
				}
			}
		}

		UnmapViewOfFile(m_pMapAddr);
	}

	m_pMapAddr = NULL;

	if (m_hMapFile)
		CloseHandle(m_hMapFile);

	m_hMapFile = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// we'll update sample's window on timer once per second
/////////////////////////////////////////////////////////////////////////////
void CRTSSSharedMemorySampleDlg::OnTimer(UINT nIDEvent) 
{
	//if we're not connected to the server's shared memory yet - do it now

	if (!m_pMapAddr)
		Connect();

	if (m_pMapAddr)
		//if we're connected to shared memory, we must check if it is valid or not
	{
		if (m_bRTSSSharedMemoryV2)
			//v2.x shared memory specific re-connection codepath
		{
			LPRTSS_SHARED_MEMORY lpMem = (LPRTSS_SHARED_MEMORY)m_pMapAddr;

			if (lpMem->dwSignature == 0xDEAD)
				//if the memory is marked as dead (e.g. the server was unloaded), we should disconnect from it and
				//try to connect again
				Connect();
		}
		else
			//v1.x shared memory specific re-connection codepath
		{
			LPRTSS_SHARED_MEMORY_V_1_3 lpMem = (LPRTSS_SHARED_MEMORY_V_1_3)m_pMapAddr;

			if (lpMem->dwSignature == 0xDEAD)
				//if the memory is marked as dead (e.g. the server was unloaded), we should disconnect from it and
				//try to connect again
				Connect();
		}
	}

	if (m_pMapAddr)
	{
		if (m_bRTSSSharedMemoryV2)
			//v2.x shared memory specific server info update codepath
		{
			LPRTSS_SHARED_MEMORY lpMem = (LPRTSS_SHARED_MEMORY)m_pMapAddr;

			if (lpMem->dwSignature == 'RTSS')
				//check if we're connected to valid server's memory
			{
				m_strStatus.Format("Connected to RTSS shared memory v%d.%d\r\n\r\n", lpMem->dwVersion>>16, lpMem->dwVersion & 0xffff);

				CString strBuf;

				for (DWORD dwApp=0; dwApp<lpMem->dwAppArrSize; dwApp++)
						//shared memory v2.0 and newer contains independent info for multiple
						//active 3D applications

						//we'll run through all application entries and display independednt info
						//for each one
				{
					RTSS_SHARED_MEMORY::LPRTSS_SHARED_MEMORY_APP_ENTRY pEntry = GetAppEntry(lpMem, dwApp);
						//get ptr to application descriptor entry

						//Take a note that we don't access entries array directly to
						//ensure that the sample will work even if entry format/size
						//will change in future versions

						//It is _strongly_ recommended to use similar behavior in your code

					if (pEntry->dwProcessID)
						//skip empty entries containing invalid process IDs
					{
						char szApp[MAX_PATH];
						strcpy(szApp, pEntry->szName);
						PathStripPath(szApp);
							//get current 3D application name

						strBuf.Format("Application             : %s", szApp);
						m_strStatus += strBuf;

						//format applicattion's 3D API usage string
						
						strBuf = "";
						if (pEntry->dwFlags & APPFLAG_DD)
							strBuf += "DirectDraw";

						if (pEntry->dwFlags & APPFLAG_D3D8)
						{
							if (!strBuf.IsEmpty())
								strBuf += ", ";
							strBuf += "Direct3D8";
						}
							
						if (pEntry->dwFlags & APPFLAG_D3D9)
						{
							if (!strBuf.IsEmpty())
								strBuf += ",";
							strBuf += "Direct3D9";
						}
							
						if (pEntry->dwFlags & APPFLAG_OGL)
						{
							if (!strBuf.IsEmpty())
								strBuf += ",";
							strBuf += "OpenGL";
						}

						if (!strBuf.IsEmpty())
							m_strStatus += " (" + strBuf + ")";

						m_strStatus += "\r\n";

						//3D application must render at least 2 frames within at least one second in order to allow the
						//server to calculate framerate
						//this condition is true only when dwTime0 is set to non-zero value

						if (pEntry->dwTime0)
							//at least two frames have been rendered, calculate framerate by dividing frame counter by time period
						{
							if (pEntry->dwFrameTime)
								strBuf.Format("Frames count            : %d\r\nRendering period        : %d ms\r\nPost-rendering period   : %d ms\r\nInstantaneous framerate : %.1f/%.1f FPS\r\n", pEntry->dwFrames, pEntry->dwTime1 - pEntry->dwTime0, GetTickCount() - pEntry->dwTime1, 1000.0f * pEntry->dwFrames / (pEntry->dwTime1 - pEntry->dwTime0), 1000000.0f / pEntry->dwFrameTime);
							else
								strBuf.Format("Frames count            : %d\r\nRendering period        : %d ms\r\nPost-rendering period   : %d ms\r\nInstantaneous framerate : %.1f FPS\r\n", pEntry->dwFrames, pEntry->dwTime1 - pEntry->dwTime0, GetTickCount() - pEntry->dwTime1, 1000.0f * pEntry->dwFrames / (pEntry->dwTime1 - pEntry->dwTime0));
						}
						else
							//there is not enough data to calculate framerate
							strBuf = "Not egough frames rendered to estimate framerate\r\n";

						m_strStatus += strBuf;
						m_strStatus += "\r\n";
					}
				}

				LPSTR lpszOSD = NULL;
					//this variable will be initialized with ptr to our OSD text slot

				for (DWORD dwOSD=1; dwOSD<lpMem->dwOSDArrSize; dwOSD++)
					//shared memory v2.x provides access to multiple independent OSD text
					//slots

					//in our sample we'll use OSD slots 1..N only, and allow the server 
					//to display its' own framerate statistics in slot 0

					//first, we'll try to find OSD slot we've already captured before
				{
					RTSS_SHARED_MEMORY::LPRTSS_SHARED_MEMORY_OSD_ENTRY pEntry = GetOSDEntry(lpMem, dwOSD);
						//get ptr to OSD slot descriptor entry

						//Take a note that we don't access entries array directly to
						//ensure that the sample will work even if entry format/size
						//will change in future versions

						//It is _strongly_ recommended to use similar behavior in your code

					if (!strcmp(pEntry->szOSDOwner, m_strAppID))
						//we've found our OSD slot!
					{
						lpszOSD = pEntry->szOSD;
							//save slot ptr
						break;
					}
				}

				if (!lpszOSD)
					//if there is no our own slot, we'll try to find the fist free slot and
					//capture it by copying our application's ID to the slot's owner name
				{
					for (DWORD dwOSD=1; dwOSD<lpMem->dwOSDArrSize; dwOSD++)
						//try to find a free slot
					{
						RTSS_SHARED_MEMORY::LPRTSS_SHARED_MEMORY_OSD_ENTRY pEntry = GetOSDEntry(lpMem, dwOSD);
							//get ptr to OSD slot descriptor entry

							//Take a note that we don't access entries array directly to
							//ensure that the sample will work even if entry format/size
							//will change in future versions

							//It is _strongly_ recommended to use similar behavior in your code


						if (!strlen(pEntry->szOSDOwner))
							//we've found a free slot and can capture it!
						{
							strcpy(pEntry->szOSDOwner, m_strAppID);
								//capture slot by copying our application's ID to the slot's owner name
							lpszOSD = pEntry->szOSD;
								//save slot ptr
							break;
						}
					}
				}

				if (!lpszOSD)
					//there are no free slots for us, bad luck!
					strBuf = "\r\nOSD slots are currently exclusively owned by other applications!";

				if (lpszOSD)
					//if we've successfully captured OSD text slot, we can display system time in it
				{
					CTime t = CTime::GetCurrentTime();
					strBuf = t.Format("%H:%M:%S");
						//format current system time

					strcpy(lpszOSD, strBuf);
						//send it to owr OSD slot

					lpMem->dwOSDFrame++;
						//tell the server that it is necessary to update OSD

					strBuf = "\r\nOSD text                : " + strBuf;
				}

				m_strStatus += strBuf;
			}
			else
				m_strStatus = "Connected to uninitialized RTSS shared memory";
		}
		else
			//v1.x shared memory specific info update codepath
		{
			LPRTSS_SHARED_MEMORY_V_1_3 lpMem = (LPRTSS_SHARED_MEMORY_V_1_3)m_pMapAddr;

			if (lpMem->dwSignature == 'RTSS')
				//check if we're connected to valid server's memory
			{
				//3D application must render at least 2 frames within at least one second in order to allow the
				//server to calculate framerate
				//this condition is true only when dwTime0 is set to non-zero value

				if (lpMem->dwTime0)
					//at least two frames have been rendered, calculate framerate by dividing frame counter by time period
					m_strStatus.Format("Connected to RTSS shared memory v%d.%d\r\n\r\nFrames count            : %d\r\nRendering period        : %d ms\r\nPost-rendering period   : %d ms\r\nInstantaneous framerate : %.1f FPS", lpMem->dwVersion>>16, lpMem->dwVersion & 0xffff, lpMem->dwFrames, lpMem->dwTime1 - lpMem->dwTime0, GetTickCount() - lpMem->dwTime1, 1000.0f * lpMem->dwFrames / (lpMem->dwTime1 - lpMem->dwTime0));
				else
					//there is not enough data to calculate framerate
					m_strStatus.Format("Connected to RTSS shared memory v%d.%d\r\n\r\nNot egough frames rendered to estimate framerate", lpMem->dwVersion>>16, lpMem->dwVersion & 0xffff);

				if (lpMem->dwVersion >= 0x00010001)
					//shared memory v1.1 and newer provides access to the server's OSD
					//we'll demonstrate OSD usage via displaying current time in it
				{
					CString strBuf;

					LPSTR lpszOSD = NULL;
						//this variable will be initialized with ptr to our OSD text slot

					if (lpMem->dwVersion >= 0x00010002)
						//shared memory v1.2 and newer provids access to 4 independent OSD text slots
						//we'll use slots 1-3 only, and allow the server to display its' own framerate
						//statistics in slot 0
					{
						//first, we'll try to find OSD slot we've already captured

						if (!strcmp(lpMem->szOSD1Owner, m_strAppID))
							lpszOSD = lpMem->szOSD1;
						else
							if (!strcmp(lpMem->szOSD2Owner, m_strAppID))
								lpszOSD = lpMem->szOSD2;
							else
								if (!strcmp(lpMem->szOSD3Owner, m_strAppID))
									lpszOSD = lpMem->szOSD3;
			
						if (!lpszOSD)
							//if there is no our own slot, we'll try to find the fist free slot 
							//and capture it by copying our application's name to the slot's owner field
						{
							if (!strlen(lpMem->szOSD1Owner))
							{
								strcpy(lpMem->szOSD1Owner, m_strAppID);
								lpszOSD = lpMem->szOSD1;
							}
							else
								if (!strlen(lpMem->szOSD2Owner))
								{
									strcpy(lpMem->szOSD2Owner, m_strAppID);
									lpszOSD = lpMem->szOSD2;
								}
								else
									if (!strlen(lpMem->szOSD3Owner))
									{
										strcpy(lpMem->szOSD3Owner, m_strAppID);
										lpszOSD = lpMem->szOSD3;
									}
						}

						if (!lpszOSD)
							//there are no free slots for us, bad luck!
							strBuf = "\r\nOSD slots 1-3 are currently exclusively owned by other applications!";
					}
					else
						//shared memory v1.1 has only one OSD text slot
					{
						//capture slot if it is free

						if (!strlen(lpMem->szOSDOwner))
							strcpy(lpMem->szOSDOwner, m_strAppID);

						//check if the slot is captured by our application

						if (strcmp(lpMem->szOSDOwner, m_strAppID))
							strBuf.Format("\r\nOSD is currently exclusively owned by %s!", lpMem->szOSDOwner);
						else
							lpszOSD = lpMem->szOSD;
					}

					if (lpszOSD)
						//if we've successfully captured OSD text slot, we can display system time in it
					{
						CTime t = CTime::GetCurrentTime();
						strBuf = t.Format("%H:%M:%S");
							//format current system time

						strcpy(lpszOSD, strBuf);
							//send it to owr OSD slot

						lpMem->dwOSDFlags |= OSDFLAG_UPDATED;
							//tell the server that it is necessary to update OSD

						strBuf = "\r\nOSD text                : " + strBuf;
					}
				}
			}
			else
				m_strStatus = "Connected to uninitialized RTSS shared memory";
		}
	}
	else
		m_strStatus = "Failed to connect to RTSS shared memory, please start RivaTunerStatisticsServer";
	
	UpdateData(FALSE);
	
	CDialog::OnTimer(nIDEvent);
}
/////////////////////////////////////////////////////////////////////////////
void CRTSSSharedMemorySampleDlg::OnDestroy() 
{
	if (m_nTimerID)
		KillTimer(m_nTimerID);

	m_nTimerID = NULL;

	MSG msg; 
	while (PeekMessage(&msg, m_hWnd, WM_TIMER, WM_TIMER, PM_REMOVE));

	Disconnect();

	CDialog::OnDestroy();	
}
/////////////////////////////////////////////////////////////////////////////
RTSS_SHARED_MEMORY::LPRTSS_SHARED_MEMORY_OSD_ENTRY CRTSSSharedMemorySampleDlg::GetOSDEntry(LPRTSS_SHARED_MEMORY pMem, DWORD dwEntry)
{
	//get ptr to specified OSD slot descriptor entry

	//it is strongly recommended to use similar behavior in your code instead of accessing entries
	//arrays directly to ensure that your code will work even if entry size/format will change in
	//future versions of the server

	return (RTSS_SHARED_MEMORY::LPRTSS_SHARED_MEMORY_OSD_ENTRY)((LPBYTE)pMem + pMem->dwOSDArrOffset + dwEntry * pMem->dwOSDEntrySize);
}
/////////////////////////////////////////////////////////////////////////////
RTSS_SHARED_MEMORY::LPRTSS_SHARED_MEMORY_APP_ENTRY CRTSSSharedMemorySampleDlg::GetAppEntry(LPRTSS_SHARED_MEMORY pMem, DWORD dwEntry)
{
	//get ptr to specified application descriptor entry

	//it is strongly recommended to use similar behavior in your code instead of accessing entries
	//arrays directly to ensure that your code will work even if entry size/format will change in
	//future versions of the server

	return (RTSS_SHARED_MEMORY::LPRTSS_SHARED_MEMORY_APP_ENTRY)((LPBYTE)pMem + pMem->dwAppArrOffset + dwEntry * pMem->dwAppEntrySize);
}
/////////////////////////////////////////////////////////////////////////////
