HUMAN.CPP
// Filename: Human.cpp 
// 
// Description:  Implementation of CHuman 
// This file contains the code that handles the interaction between two 
// human players. 
// 
// This file is provided as part of the Microsoft Transaction Server Samples 
// 
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT  
// WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,  
// INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES  
// OF MERCHANTABILITY AND/OR FITNESS FOR A  PARTICULAR  
// PURPOSE. 
// 
// Copyright (C) 1997 Microsoft Corporation, All rights reserved 
 
#include "stdafx.h" 
#include "tServer.h" 
#include "Human.h" 
 
#include <mtx.h> 
#include <mtxspm.h> 
 
 
STDMETHODIMP CHuman::InterfaceSupportsErrorInfo(REFIID riid) 
{ 
static const IID* arr[] =  
{ 
&IID_IHuman, 
}; 
 
for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++) 
{ 
if (InlineIsEqualGUID(*arr[i],riid)) 
return S_OK; 
} 
return S_FALSE; 
} 
 
 
STDMETHODIMP CHuman::EnterNewGame (IN BSTR bstrLocalName, OUT VARIANT* pvGameID, OUT VARIANT* pvName, 
   OUT VARIANT* pvOrder) { 
 
HRESULT hr = S_OK; 
 
IObjectContext* pObjectContext = NULL; 
 
ISharedPropertyGroupManager* spmMgr = NULL; 
ISharedPropertyGroup* spmGroup = NULL; 
 
ISharedProperty* spmPropCounter = NULL; 
ISharedProperty* spmPropFirstName = NULL; 
ISharedProperty* spmPropSecondName = NULL; 
ISharedProperty* spmPropState = NULL; 
 
pvGameID->vt = VT_I4; 
pvName->vt = VT_BSTR; 
pvOrder->vt = VT_I4; 
 
long lGameID = 0; 
long lOrder = 0; 
 
try { 
 
// Get the object context 
THROW_ERR ( hr = GetObjectContext (&pObjectContext) ); 
if (pObjectContext == NULL) { 
THROW_ERR ( E_FAIL ); 
} 
 
// Create the SharedPropertyGroupManager 
THROW_ERR ( pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr) ); 
 
// Create the SharedPropertyGroup 
LONG lIsolationMode = LockMethod; 
LONG lReleaseMode = Process; 
VARIANT_BOOL bExists = VARIANT_FALSE; 
THROW_ERR ( spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup) ); 
 
// Create SharedProperties 
CComVariant vCounter; 
 
THROW_ERR ( spmGroup->CreateProperty (L"Counter", &bExists, &spmPropCounter) ); 
THROW_ERR ( spmPropCounter->get_Value (&vCounter) ); 
 
CComVariant vSecondName; 
CComVariant vState; 
 
TCHAR szBuf [512]; 
BSTR bstrSecondName; 
BSTR bstrFirstName; 
BSTR bstrState; 
 
// Find first open two-human game 
if (vCounter.lVal == 0) { 
vCounter.lVal = 1; 
} else { 
long lScan = 1; 
bool bFlag = false; 
while (lScan < vCounter.lVal && !bFlag) { 
 
wsprintf (szBuf, _T("%dState"), lScan); 
bstrState = TCHAR2BSTR (szBuf); 
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) ); 
::SysFreeString (bstrState); 
 
THROW_ERR ( spmPropState->get_Value (&vState) ); 
 
if (vState.lVal < 2) { 
 
// Found a game 
bFlag = true; 
} else 
lScan ++; 
 
THROW_ERR ( spmPropState->Release() ); 
} 
 
vCounter.lVal = lGameID = lScan; 
} 
 
wsprintf (szBuf, _T("%dSecondName"), vCounter.lVal); 
bstrSecondName = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dState"), vCounter.lVal); 
bstrState = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dFirstName"), vCounter.lVal); 
bstrFirstName = TCHAR2BSTR (szBuf); 
 
THROW_ERR ( spmGroup->CreateProperty (bstrFirstName, &bExists, &spmPropFirstName) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrSecondName, &bExists, &spmPropSecondName) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) ); 
 
::SysFreeString (bstrFirstName); 
::SysFreeString (bstrSecondName); 
::SysFreeString (bstrState); 
 
// Get current state 
THROW_ERR ( spmPropState->get_Value (&vState) ); 
 
// State table 
// =========== 
// 0 - No one in game 
// 1 - One player lurking 
// 2 - Second player joined 
// 3 - First player's turn 
// 4 - Second player's turn 
 
// Is this the first time this property has been accessed? 
if (!bExists) 
vState.lVal = 0; 
 
// Is there someone lurking already? 
if (vState.lVal == 1) { 
 
CComVariant vFirstName; 
 
// Get opponent's name 
THROW_ERR ( spmPropFirstName->get_Value (&vFirstName) ); 
 
// Submit own name 
CComVariant vSecondName2; 
vSecondName2.vt = VT_BSTR; 
 
vSecondName2.bstrVal = ::SysAllocString (bstrLocalName); 
THROW_ERR ( spmPropSecondName->put_Value (vSecondName2) ); 
::SysFreeString (vSecondName2.bstrVal); 
 
// Update state 
vState.lVal = 2; 
THROW_ERR ( spmPropState->put_Value (vState) ); 
 
// Assign gameID 
lGameID = vCounter.lVal; 
 
// Initialize the game screen array 
InitArray(); 
THROW_ERR ( SaveArray(lGameID) ); 
 
// Prepare return values 
pvName->bstrVal = ::SysAllocString (vFirstName.bstrVal); 
pvOrder->lVal = 2; 
pvGameID->lVal = lGameID; 
 
} else { 
 
CComVariant vFirstName; 
vFirstName.vt = VT_BSTR; 
 
// Update first name 
vFirstName.bstrVal = ::SysAllocString (bstrLocalName); 
THROW_ERR ( spmPropFirstName->put_Value (vFirstName) ); 
::SysFreeString (vFirstName.bstrVal); 
 
// Update counter for next game 
vCounter.lVal = vCounter.lVal + 1; 
THROW_ERR ( spmPropCounter->put_Value (vCounter) ); 
 
// Update state 
vState.lVal = 1; 
THROW_ERR ( spmPropState->put_Value (vState) ); 
 
// Assign gameID 
lGameID = vCounter.lVal - 1; 
 
// Prepare return values 
pvName->bstrVal = ::SysAllocString (L"No one"); 
pvOrder->lVal = 1; 
pvGameID->lVal = lGameID; 
} 
 
// We're finished and happy 
pObjectContext->SetComplete(); 
 
} catch (HRESULT hr) { 
 
// Create an ErrorInfo object 
ICreateErrorInfo* pCreateErrInfo = NULL; 
IErrorInfo* pErrInfo = NULL; 
 
CreateErrorInfo (&pCreateErrInfo); 
pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo); 
 
// Fill in error information 
TCHAR szErr [512]; 
wsprintf (szErr, _T("Error %d occurred in Human::EnterNewGame()"), hr); 
BSTR bstrDesc = TCHAR2BSTR (szErr); 
 
pCreateErrInfo->SetGUID (IID_IComputer); 
pCreateErrInfo->SetSource (L"Computer"); 
pCreateErrInfo->SetDescription (bstrDesc); 
::SysFreeString (bstrDesc); 
 
// Confirm error information 
SetErrorInfo (0, pErrInfo); 
 
// Clean up the error objects 
if (pCreateErrInfo) 
pCreateErrInfo->Release(); 
 
if (pErrInfo) 
pErrInfo->Release(); 
 
// Indicate our unhappiness 
if (pObjectContext) 
pObjectContext->SetAbort(); 
 
hr = E_FAIL; 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
if (spmMgr) 
spmMgr->Release(); 
 
if (spmGroup) 
spmGroup->Release(); 
 
if (spmPropCounter) 
spmPropCounter->Release(); 
 
if (spmPropFirstName) 
spmPropFirstName->Release(); 
 
if (spmPropSecondName) 
spmPropSecondName->Release(); 
 
if (spmPropState) 
spmPropState->Release(); 
 
return hr; 
} 
 
 
STDMETHODIMP CHuman::NewMove (IN long lGameID, IN long lOrder, IN long lX, IN long lY, OUT VARIANT* pvWin) { 
 
// Win protocol 
// ============ 
// 0  -> move did not end game 
// 1  -> player won 
// -1 -> remote player won (won't occur in this function) 
// 2  -> player's move tied the game 
// -2 -> remote player's move tied the game (won't occur in this function) 
 
HRESULT hr = S_OK; 
 
IObjectContext* pObjectContext = NULL; 
 
ISharedPropertyGroupManager* spmMgr = NULL; 
ISharedPropertyGroup* spmGroup = NULL; 
 
ISharedProperty* spmPropState = NULL; 
ISharedProperty* spmPropX = NULL; 
ISharedProperty* spmPropY = NULL; 
ISharedProperty* spmPropWin = NULL; 
 
pvWin->vt = VT_I4; 
 
long lWin = 0; 
 
 
try { 
 
// Get the object context 
THROW_ERR ( hr = GetObjectContext(&pObjectContext) ); 
if (pObjectContext == NULL) { 
THROW_ERR ( E_FAIL ); 
} 
 
// Create the SharedPropertyGroupManager 
THROW_ERR ( pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager,  
IID_ISharedPropertyGroupManager, (void**)&spmMgr) ); 
 
// Create the SharedPropertyGroup 
LONG lIsolationMode = LockMethod; 
LONG lReleaseMode = Process; 
VARIANT_BOOL bExists = VARIANT_FALSE; 
THROW_ERR ( spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, 
&bExists, &spmGroup) ); 
 
// Create SharedProperties 
BSTR bstrState; 
BSTR bstrX; 
BSTR bstrY; 
BSTR bstrWin; 
TCHAR szBuf[512]; 
wsprintf (szBuf, _T("%dState"), lGameID); 
bstrState = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dX"), lGameID); 
bstrX = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dY"), lGameID); 
bstrY = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dWin"), lGameID); 
bstrWin = TCHAR2BSTR (szBuf); 
 
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrX, &bExists, &spmPropX) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrY, &bExists, &spmPropY) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrWin, &bExists, &spmPropWin) ); 
 
::SysFreeString (bstrState); 
::SysFreeString (bstrX); 
::SysFreeString (bstrY); 
::SysFreeString (bstrWin); 
 
CComVariant vState; 
CComVariant vX; 
CComVariant vY; 
CComVariant vWin; 
vState.vt = VT_I4; 
vX.vt = VT_I4; 
vY.vt = VT_I4; 
vWin.vt = VT_I4; 
 
// Load screen data 
THROW_ERR ( LoadArray(lGameID) ); 
 
// Save player's move to local storage and to Spam 
plField[lX][lY] = lOrder; 
 
vX.lVal = lX; 
vY.lVal = lY; 
THROW_ERR ( spmPropX->put_Value (vX) ); 
THROW_ERR ( spmPropY->put_Value (vY) ); 
 
// Submit new state 
if (lOrder == 1) 
vState.lVal = 4; 
else 
vState.lVal = 3; 
 
THROW_ERR ( spmPropState->put_Value (vState) ); 
 
// Commit move to global storage 
THROW_ERR ( SaveArray (lGameID) ); 
 
// Check for win 
if (IsWin (lOrder)) { 
 
// Player wins 
lWin = 1; 
 
// Other player loses 
vWin.lVal = - 1; 
THROW_ERR ( spmPropWin->put_Value (vWin) ); 
} 
 
else if (HowManyTurns() == 9) { 
 
// Player ties on his move 
lWin = 2; 
 
// Other player ties on opponent's move 
vWin.lVal = - 2; 
THROW_ERR ( spmPropWin->put_Value (vWin) ); 
} 
 
// Prepare return values 
pvWin->lVal = lWin; 
 
// We're happy and finished 
pObjectContext->SetComplete(); 
 
} catch (HRESULT hr) { 
 
// Create an ErrorInfo object 
ICreateErrorInfo* pCreateErrInfo = NULL; 
IErrorInfo* pErrInfo = NULL; 
 
CreateErrorInfo (&pCreateErrInfo); 
pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo); 
 
// Fill in error information 
TCHAR szErr [512]; 
wsprintf (szErr, _T("Error %d occurred in Human::NewMove()"), hr); 
BSTR bstrDesc = TCHAR2BSTR (szErr); 
pCreateErrInfo->SetGUID (IID_IComputer); 
pCreateErrInfo->SetSource (L"Computer"); 
pCreateErrInfo->SetDescription (bstrDesc); 
::SysFreeString (bstrDesc); 
 
// Confirm error information 
SetErrorInfo (0, pErrInfo); 
 
// Clean up the error objects 
if (pCreateErrInfo) 
pCreateErrInfo->Release(); 
 
if (pErrInfo) 
pErrInfo->Release(); 
 
// Indicate our unhappiness 
if (pObjectContext) 
pObjectContext->SetAbort(); 
 
hr = E_FAIL; 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
if (spmMgr) 
spmMgr->Release(); 
 
if (spmGroup) 
spmGroup->Release(); 
 
if (spmPropState) 
spmPropState->Release(); 
 
if (spmPropX) 
spmPropX->Release(); 
 
if (spmPropY) 
spmPropY->Release(); 
 
if (spmPropWin) 
spmPropWin->Release(); 
 
return hr; 
 
} 
 
 
STDMETHODIMP CHuman::GetNewMove (IN long lGameID, IN long lOrder, OUT VARIANT* pvName, 
 OUT VARIANT* pvFoeX, OUT VARIANT* pvFoeY, OUT VARIANT* pvWin, 
 OUT VARIANT* pvUpdate) { 
 
// Win protocol 
// ============ 
// 0  -> move did not end game 
// 1  -> player won (won't occur in this function) 
// -1 -> remote player won  
// 2  -> player's move tied the game (won't occur in this function) 
// -2 -> remote player's move tied the game  
 
HRESULT hr = S_OK; 
 
IObjectContext* pObjectContext = NULL; 
 
ISharedPropertyGroupManager* spmMgr = NULL; 
ISharedPropertyGroup* spmGroup = NULL; 
ISharedProperty* spmPropState = NULL; 
ISharedProperty* spmPropX = NULL; 
ISharedProperty* spmPropY = NULL; 
ISharedProperty* spmPropWin = NULL; 
ISharedProperty* spmPropSecondName = NULL; 
 
pvName->vt = VT_BSTR; 
pvFoeX->vt = VT_I4; 
pvFoeY->vt = VT_I4; 
pvWin->vt = VT_I4; 
pvUpdate->vt = VT_I4; 
 
long lWin = 0; 
long lUpdate = 0; 
long lX = 0; 
long lY = 0; 
 
 
try { 
 
// Get the object context 
THROW_ERR ( hr = GetObjectContext(&pObjectContext) ); 
if (pObjectContext == NULL) { 
THROW_ERR ( E_FAIL ); 
} 
 
// Create the SharedPropertyGroupManager 
THROW_ERR ( pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, 
IID_ISharedPropertyGroupManager, (void**)&spmMgr) ); 
 
// Create the SharedPropertyGroup 
LONG lIsolationMode = LockMethod; 
LONG lReleaseMode = Process; 
VARIANT_BOOL bExists = VARIANT_FALSE; 
THROW_ERR ( spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, 
&bExists, &spmGroup) ); 
 
// Create SharedProperties 
BSTR bstrState; 
BSTR bstrX; 
BSTR bstrY; 
BSTR bstrWin; 
BSTR bstrSecondName; 
TCHAR szBuf[512]; 
wsprintf (szBuf, _T("%dState"), lGameID); 
bstrState = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dX"), lGameID); 
bstrX = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dY"), lGameID); 
bstrY = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dWin"), lGameID); 
bstrWin = TCHAR2BSTR (szBuf); 
wsprintf (szBuf, _T("%dSecondName"), lGameID); 
bstrSecondName = TCHAR2BSTR (szBuf); 
 
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrX, &bExists, &spmPropX) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrY, &bExists, &spmPropY) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrWin, &bExists, &spmPropWin) ); 
THROW_ERR ( spmGroup->CreateProperty (bstrSecondName, &bExists, &spmPropSecondName) ); 
 
::SysFreeString (bstrState); 
::SysFreeString (bstrX); 
::SysFreeString (bstrY); 
::SysFreeString (bstrWin); 
::SysFreeString (bstrSecondName); 
 
// Get state and move parameters 
CComVariant vState; 
CComVariant vX; 
CComVariant vY; 
CComVariant vWin; 
CComVariant vSecondName; 
THROW_ERR ( spmPropState->get_Value (&vState) ); 
THROW_ERR ( spmPropX->get_Value (&vX) ); 
THROW_ERR ( spmPropY->get_Value (&vY) ); 
THROW_ERR ( spmPropWin->get_Value (&vWin) ); 
THROW_ERR ( spmPropSecondName->get_Value (&vSecondName) ); 
 
switch (vState.lVal) { 
 
case (0): 
 
// Shouldn't happen 
THROW_ERR ( E_FAIL); 
break; 
 
case (1): 
 
// Still waiting for next player to join 
lUpdate = 0; 
break; 
 
case (2): 
 
// Other player just joined 
if (lOrder == 1) { 
lUpdate = 2; 
pvName->bstrVal = ::SysAllocString (vSecondName.bstrVal); 
 
// It's now the first player's turn 
vState.lVal = 3; 
THROW_ERR ( spmPropState->put_Value (vState) ); 
} 
 
break; 
 
case (3): 
case (4): 
 
long lTemp; 
if (vState.lVal == 3) 
lTemp = 1; 
else 
lTemp = 2; 
 
// Has other player moved? 
if (lOrder == lTemp) { 
 
// Other player has moved 
lX = vX.lVal; 
lY = vY.lVal; 
lWin = vWin.lVal; 
lUpdate = 1; 
 
} else { 
 
// Waiting for other player to move 
lUpdate = 0; 
} 
 
break; 
 
default: 
 
// Shouldn't happen 
THROW_ERR ( E_FAIL ); 
break; 
} 
 
// Prepare return values 
pvFoeX->lVal = lX; 
pvFoeY->lVal = lY; 
pvWin->lVal = lWin; 
pvUpdate->lVal = lUpdate; 
 
// We are finished and happy 
pObjectContext->SetComplete(); 
 
} catch (HRESULT hr) { 
 
// Create an ErrorInfo object 
ICreateErrorInfo* pCreateErrInfo = NULL; 
IErrorInfo* pErrInfo = NULL; 
 
CreateErrorInfo (&pCreateErrInfo); 
pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo); 
 
// Fill in error information 
TCHAR szErr [512]; 
wsprintf (szErr, _T("Error %d occurred in Human::GetNewMove()"), hr); 
BSTR bstrDesc = TCHAR2BSTR (szErr); 
pCreateErrInfo->SetGUID (IID_IComputer); 
pCreateErrInfo->SetSource (L"Computer"); 
pCreateErrInfo->SetDescription (bstrDesc); 
::SysFreeString (bstrDesc); 
 
// Confirm error information 
SetErrorInfo (0, pErrInfo); 
 
// Clean up the error objects 
if (pCreateErrInfo) 
pCreateErrInfo->Release(); 
 
if (pErrInfo) 
pErrInfo->Release(); 
 
// Indicate our unhappiness 
if (pObjectContext) 
pObjectContext->SetAbort(); 
 
hr = E_FAIL; 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
if (spmMgr) 
spmMgr->Release(); 
 
if (spmGroup) 
spmGroup->Release(); 
 
if (spmPropState) 
spmPropState->Release(); 
 
if (spmPropX) 
spmPropX->Release(); 
 
if (spmPropY) 
spmPropY->Release(); 
 
if (spmPropWin) 
spmPropWin->Release(); 
 
if (spmPropSecondName) 
spmPropSecondName->Release(); 
 
return hr; 
} 
 
 
bool CHuman::IsWin (long lPlayer) { 
 
bool bWin = false; 
 
for (int i = 0; i < 3; i ++) { 
if (plField [i][0] == plField [i][1] && plField [i][1] == plField [i][2] && plField [i][0] == lPlayer) 
bWin = true; 
if (plField [0][i] == plField [1][i] && plField [1][i] == plField [2][i] && plField [0][i] == lPlayer) 
bWin = true; 
} 
 
if (plField [0][0] == plField [1][1] && plField [1][1] == plField [2][2] && plField [1][1] == lPlayer) 
bWin = true; 
if (plField [2][0] == plField [1][1] && plField [1][1] == plField [0][2] && plField [1][1] == lPlayer) 
bWin = true; 
 
return bWin; 
} 
 
 
long CHuman::HowManyTurns () { 
 
long lTurns = 0; 
 
for (int i = 0; i < 3; i ++) { 
for (int j = 0; j < 3; j ++) { 
if (plField[i][j] > 0) { 
lTurns ++; 
} 
} 
} 
 
return lTurns; 
} 
 
 
HRESULT CHuman::LoadArray (long lGameID) { 
 
HRESULT hr = S_OK; 
 
IObjectContext* pObjectContext = NULL; 
 
ISharedPropertyGroupManager* spmMgr = NULL; 
ISharedPropertyGroup* spmGroup = NULL; 
ISharedProperty* spmPropField[3][3]; 
 
// Get context 
hr = GetObjectContext(&pObjectContext); 
 
// Create the SharedPropertyGroupManager 
hr = pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr); 
 
// Create the SharedPropertyGroup 
LONG lIsolationMode = LockMethod; 
LONG lReleaseMode = Process; 
VARIANT_BOOL bExists = VARIANT_FALSE; 
hr = spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup); 
 
// Load the field SharedProperties 
TCHAR szBuf [512]; 
BSTR bstrField; 
CComVariant vField; 
 
for (long i = 0; i < 3; i ++ ) { 
for (long j = 0; j < 3; j ++) { 
 
wsprintf (szBuf, _T("%dField%d%d"), lGameID, i, j); 
bstrField = TCHAR2BSTR (szBuf); 
hr = spmGroup->CreateProperty (bstrField, &bExists, &spmPropField[i][j]); 
::SysFreeString (bstrField); 
 
hr = spmPropField[i][j]->get_Value (&vField); 
plField[i][j] = vField.lVal; 
} 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
if (spmMgr) 
spmMgr->Release(); 
 
if (spmGroup) 
spmGroup->Release(); 
 
for (i = 0; i < 3; i ++) { 
for (long j = 0; j < 3; j ++) { 
if (spmPropField[i][j]) { 
spmPropField[i][j]->Release(); 
} 
} 
} 
 
return hr; 
} 
 
 
HRESULT CHuman::SaveArray (long lGameID) { 
 
HRESULT hr = S_OK; 
 
IObjectContext* pObjectContext = NULL; 
 
ISharedPropertyGroupManager* spmMgr = NULL; 
ISharedPropertyGroup* spmGroup = NULL; 
ISharedProperty* spmPropField[3][3]; 
 
// Get context 
hr = GetObjectContext(&pObjectContext); 
 
// Create the SharedPropertyGroupManager 
hr = pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr); 
 
// Create the SharedPropertyGroup 
LONG lIsolationMode = LockMethod; 
LONG lReleaseMode = Process; 
VARIANT_BOOL bExists = VARIANT_FALSE; 
hr = spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup); 
 
// Save the field shared properties 
TCHAR szBuf [512]; 
BSTR bstrField; 
CComVariant vField; 
vField.vt = VT_I4; 
 
for (long i = 0; i < 3; i ++ ) { 
for (long j = 0; j < 3; j ++) { 
 
wsprintf (szBuf, _T("%dField%d%d"), lGameID, i, j); 
bstrField = TCHAR2BSTR (szBuf); 
hr = spmGroup->CreateProperty (bstrField, &bExists, &spmPropField[i][j]); 
::SysFreeString (bstrField); 
 
vField.lVal = plField[i][j]; 
hr = spmPropField[i][j]->put_Value (vField); 
} 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
if (spmMgr) 
spmMgr->Release(); 
 
if (spmGroup) 
spmGroup->Release(); 
 
for (i = 0; i < 3; i ++) { 
for (long j = 0; j < 3; j ++) { 
if (spmPropField[i][j]) { 
spmPropField[i][j]->Release(); 
} 
} 
} 
 
return hr; 
}