594 lines
15 KiB
C++
594 lines
15 KiB
C++
|
// This node class represents the root node of our snap-in
|
||
|
#include "stdafx.h"
|
||
|
#include "MyNodes.h"
|
||
|
#include "DomSel.h"
|
||
|
#include "TSync.hpp"
|
||
|
#include "ResStr.h"
|
||
|
#include "HrMsg.h"
|
||
|
|
||
|
//#import "\bin\DBManager.tlb" no_namespace,named_guids
|
||
|
//#import "\bin\McsVarSetMin.tlb" no_namespace, named_guids
|
||
|
#import "DBMgr.tlb" no_namespace,named_guids
|
||
|
#import "VarSet.tlb" no_namespace, named_guids rename("property", "aproperty")
|
||
|
#import "UpdateMOT.tlb" no_namespace,named_guids
|
||
|
|
||
|
// {C8C24622-3FA1-11d3-8AED-00A0C9AFE114}
|
||
|
static const GUID CRootGUID_NODETYPE =
|
||
|
{ 0xc8c24622, 0x3fa1, 0x11d3, { 0x8a, 0xed, 0x0, 0xa0, 0xc9, 0xaf, 0xe1, 0x14 } };
|
||
|
|
||
|
const GUID* CRootNode::m_NODETYPE = &CRootGUID_NODETYPE;
|
||
|
const OLECHAR* CRootNode::m_SZNODETYPE = OLESTR("C8C24622-3FA1-11d3-8AED-00A0C9AFE114");
|
||
|
const OLECHAR* CRootNode::m_SZDISPLAY_NAME = NULL;
|
||
|
const CLSID* CRootNode::m_SNAPIN_CLASSID = &CLSID_DomMigrator;
|
||
|
static LONG SnapInCount = -1;
|
||
|
|
||
|
extern "C" int runWizard(int whichWizard, HWND hParentWindow);
|
||
|
#define WIZARD_SEMNAME L"McsDomMigrAgent.990000.Sem"
|
||
|
|
||
|
CSnapInToolbarInfo m_toolBar;
|
||
|
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
// DisplayError Helper Function
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
void DisplayError(HRESULT hr, UINT uFormatId)
|
||
|
{
|
||
|
_com_error ce = GetError(hr);
|
||
|
|
||
|
if (FAILED(ce.Error()))
|
||
|
{
|
||
|
CString strTitle;
|
||
|
strTitle.LoadString(IDS_Title);
|
||
|
|
||
|
CString strMessage;
|
||
|
strMessage.Format(uFormatId);
|
||
|
|
||
|
_bstr_t bstrSource = ce.Source();
|
||
|
|
||
|
if (bstrSource.length() > 0)
|
||
|
{
|
||
|
strMessage += _T(" : ");
|
||
|
strMessage += bstrSource;
|
||
|
}
|
||
|
|
||
|
_bstr_t bstrDescription = ce.Description();
|
||
|
|
||
|
if (bstrDescription.length() > 0)
|
||
|
{
|
||
|
strMessage += _T(" : ");
|
||
|
strMessage += bstrDescription;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CString strError;
|
||
|
strError.Format(_T(" : %s (%08lX)"), ce.ErrorMessage(), ce.Error());
|
||
|
|
||
|
strMessage += strError;
|
||
|
}
|
||
|
|
||
|
MessageBox(NULL, strMessage, strTitle, MB_OK|MB_ICONERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
CRootNode::CRootNode() :
|
||
|
m_hwndMainWindow(0)
|
||
|
{
|
||
|
// Initialize the array of children
|
||
|
CReportingNode * pNode = new CReportingNode;
|
||
|
|
||
|
pNode->UpdateChildren(NULL);
|
||
|
m_ChildArray.Add(pNode);
|
||
|
UpdateMigratedObjectsTable();
|
||
|
UpdateAccountReferenceTable();
|
||
|
CheckForFailedActions(FALSE);
|
||
|
//m_ChildArray.Add(new CPruneGraftNode);
|
||
|
if (InterlockedIncrement(&SnapInCount) == 0)
|
||
|
m_SZDISPLAY_NAME = GET_BSTR(IDS_ActiveDirectoryMigrationTool).copy();
|
||
|
}
|
||
|
|
||
|
CRootNode::~CRootNode()
|
||
|
{
|
||
|
if ((m_SZDISPLAY_NAME) && (InterlockedDecrement(&SnapInCount) < 0))
|
||
|
{
|
||
|
SysFreeString(const_cast<OLECHAR*>(m_SZDISPLAY_NAME));
|
||
|
m_SZDISPLAY_NAME = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class CWizardRunner
|
||
|
{
|
||
|
public:
|
||
|
int RunTheWizard(int wizardNdx, HWND hwndParent)
|
||
|
{
|
||
|
int result = 0;
|
||
|
TSemaphoreNamed cSem; // named semaphore
|
||
|
BOOL bExisted = FALSE;
|
||
|
CString message;
|
||
|
CString title;
|
||
|
|
||
|
DWORD rcOs = cSem.Create( WIZARD_SEMNAME, 0, 1, &bExisted );
|
||
|
|
||
|
if ( rcOs || bExisted )
|
||
|
{
|
||
|
message.LoadString(IDS_WizardAlreadyRunning);
|
||
|
title.LoadString(IDS_Title);
|
||
|
MessageBox(NULL,message,title,MB_OK | MB_ICONERROR);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result = runWizard(wizardNdx, hwndParent);
|
||
|
|
||
|
// if user cancels wizard or an error occurs
|
||
|
|
||
|
if (result == 0)
|
||
|
{
|
||
|
// if able to retrieve error information
|
||
|
// then an error occurred, notify user
|
||
|
|
||
|
// Note: It is currently possible for errors
|
||
|
// to occur without the error information being set
|
||
|
|
||
|
DisplayError(S_OK, IDS_ERR_RUN_WIZARD);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
void CRootNode::CheckUndoable()
|
||
|
{
|
||
|
IIManageDBPtr pDB;
|
||
|
HRESULT hr;
|
||
|
_bstr_t sWizard = L"Options.Wizard";
|
||
|
long lAction = -2;
|
||
|
VARIANT var;
|
||
|
_variant_t vnt;
|
||
|
|
||
|
hr = pDB.CreateInstance(CLSID_IManageDB);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
hr = pDB->raw_GetCurrentActionID(&lAction);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
VariantInit(&var);
|
||
|
hr = pDB->raw_GetActionHistoryKey(lAction, sWizard, &var);
|
||
|
vnt.Attach(var);
|
||
|
}
|
||
|
if ( SUCCEEDED(hr) && (V_VT(&vnt) == VT_BSTR) )
|
||
|
{
|
||
|
sWizard = vnt;
|
||
|
if (sWizard.length() > 0)
|
||
|
{
|
||
|
IsUndoable = ( !_wcsicmp(sWizard, L"user") || !_wcsicmp(sWizard, L"group") || !_wcsicmp(sWizard, L"computer") );
|
||
|
if ( IsUndoable )
|
||
|
{
|
||
|
sWizard = GET_BSTR(DCTVS_Options_NoChange);
|
||
|
VariantInit(&var);
|
||
|
hr = pDB->raw_GetActionHistoryKey(lAction, sWizard, &var);
|
||
|
vnt.Attach(var);
|
||
|
if ( SUCCEEDED(hr) && (V_VT(&vnt) == VT_BSTR) )
|
||
|
{
|
||
|
sWizard = vnt;
|
||
|
if (!sWizard || !UStrICmp(sWizard,GET_STRING(IDS_YES)) )
|
||
|
{
|
||
|
IsUndoable = false; // can't undo a no-change mode operation
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IsUndoable = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IsUndoable = false;
|
||
|
}
|
||
|
if ( hr == 0x800a0bb9 )
|
||
|
{
|
||
|
// the database is missing or corrupt
|
||
|
CString msg;
|
||
|
CString title;
|
||
|
|
||
|
msg.LoadString(IDS_NoDatabase);
|
||
|
title.LoadString(IDS_Title);
|
||
|
MessageBox(NULL,msg,title,MB_ICONERROR | MB_OK);
|
||
|
throw new _com_error(hr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CRootNode::CheckForST()
|
||
|
{
|
||
|
IIManageDBPtr pDB;
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
long cnt;
|
||
|
|
||
|
CanUseST = false;
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
hr = pDB.CreateInstance(CLSID_IManageDB);
|
||
|
}
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
hr = pDB->raw_AreThereAnyMigratedObjects(&cnt);
|
||
|
}
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
if ( cnt > 0 )
|
||
|
{
|
||
|
// there are some migrated objects
|
||
|
CanUseST = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CRootNode::CheckForFailedActions(BOOL bPrompt)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
HRESULT hr = S_OK;
|
||
|
IIManageDBPtr pDB;
|
||
|
IVarSetPtr pVarSet(CLSID_VarSet);
|
||
|
IUnknown * pUnk = NULL;
|
||
|
long lAction = -2;
|
||
|
|
||
|
CanRetry = false;
|
||
|
hr = pDB.CreateInstance(CLSID_IManageDB);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
hr = pVarSet.QueryInterface(IID_IUnknown,&pUnk);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
// we will also check the last action type and set the IsUndoable flag.
|
||
|
CheckUndoable();
|
||
|
CheckForST();
|
||
|
hr = pDB->raw_GetFailedDistributedActions(-1,&pUnk);
|
||
|
pUnk->Release();
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
_bstr_t numItemsText = pVarSet->get(L"DA");
|
||
|
long nItems = _wtoi(numItemsText);
|
||
|
|
||
|
if ( nItems )
|
||
|
{
|
||
|
CString str;
|
||
|
CString title;
|
||
|
|
||
|
title.LoadString(IDS_Title);
|
||
|
str.FormatMessage(IDS_FailedActions,nItems);
|
||
|
|
||
|
CanRetry = true;
|
||
|
|
||
|
if ( bPrompt && IDYES == MessageBox(NULL,str,title,MB_YESNO) )
|
||
|
{
|
||
|
bool bHandled;
|
||
|
|
||
|
OnRetry(bHandled,NULL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DisplayError(hr, IDS_ERR_CHECK_FAILED_ACTIONS);
|
||
|
|
||
|
_com_issue_error(hr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CRootNode::OnMigrateUsers(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
int result;
|
||
|
CWizardRunner r;
|
||
|
|
||
|
result = r.RunTheWizard(1, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
{
|
||
|
CheckUndoable();
|
||
|
CheckForST();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnMigrateGroups(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
int result;
|
||
|
CWizardRunner r;
|
||
|
|
||
|
result = r.RunTheWizard(2, m_hwndMainWindow);
|
||
|
|
||
|
|
||
|
if (result)
|
||
|
{
|
||
|
CheckUndoable();
|
||
|
CheckForST();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnMigrateComputers(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
CWizardRunner r;
|
||
|
|
||
|
int result = r.RunTheWizard(3, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
{
|
||
|
CheckUndoable();
|
||
|
CheckForFailedActions(FALSE);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnTranslateSecurity(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
CWizardRunner r;
|
||
|
int result = r.RunTheWizard(4, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
IsUndoable = false;
|
||
|
CheckForFailedActions(FALSE);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnMigrateExchangeServer(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
CWizardRunner r;
|
||
|
int result = r.RunTheWizard(11, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
IsUndoable = false;
|
||
|
return hr;
|
||
|
}
|
||
|
HRESULT CRootNode::OnMigrateExchangeDirectory(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
CWizardRunner r;
|
||
|
int result = r.RunTheWizard(7, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
IsUndoable = false;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnMigrateServiceAccounts(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
CWizardRunner r;
|
||
|
int result = r.RunTheWizard(5, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
IsUndoable = false;
|
||
|
CheckForFailedActions(FALSE);
|
||
|
return hr;
|
||
|
}
|
||
|
HRESULT CRootNode::OnReporting(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
CWizardRunner r;
|
||
|
int result = r.RunTheWizard(8, m_hwndMainWindow);
|
||
|
IConsole * pConsole = NULL;
|
||
|
|
||
|
// Reload the Child-Nodes for the reporting node
|
||
|
CReportingNode * pRept = (CReportingNode*)m_ChildArray[0];
|
||
|
|
||
|
if ( pRept )
|
||
|
{
|
||
|
hr = GetConsoleFromCSnapInObjectRootBase(pObj,&pConsole);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
pRept->UpdateChildren(pConsole);
|
||
|
}
|
||
|
}
|
||
|
if (result)
|
||
|
IsUndoable = false;
|
||
|
CheckForFailedActions(FALSE);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnUndo(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
CWizardRunner r;
|
||
|
int result = r.RunTheWizard(6, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
{
|
||
|
IsUndoable = false;
|
||
|
CheckForST();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnRetry(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
CWizardRunner r;
|
||
|
int result = r.RunTheWizard(9, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
IsUndoable = false;
|
||
|
CheckForFailedActions(FALSE);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnMigrateTrusts(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
int result;
|
||
|
CWizardRunner r;
|
||
|
|
||
|
result = r.RunTheWizard(10, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
IsUndoable = false;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CRootNode::OnGroupMapping(bool &bHandled, CSnapInObjectRootBase* pObj)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CWaitCursor wait;
|
||
|
HRESULT hr = S_OK;
|
||
|
int result;
|
||
|
CWizardRunner r;
|
||
|
|
||
|
result = r.RunTheWizard(12, m_hwndMainWindow);
|
||
|
|
||
|
if (result)
|
||
|
IsUndoable = false;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CRootNode::UpdateMenuState(UINT id, LPTSTR pBuf, UINT *flags)
|
||
|
{
|
||
|
switch (id)
|
||
|
{
|
||
|
case ID_TOP_UNDO:
|
||
|
if ( !IsUndoable )
|
||
|
*flags = MF_DISABLED | MF_GRAYED;
|
||
|
else
|
||
|
*flags = MF_ENABLED;
|
||
|
break;
|
||
|
|
||
|
case ID_TOP_MIGRATEEXCHANGEDIRECTORY:
|
||
|
if ( ! CanUseST )
|
||
|
*flags = MF_DISABLED | MF_GRAYED;
|
||
|
else
|
||
|
*flags = MF_ENABLED;
|
||
|
break;
|
||
|
case ID_TOP_TRANSLATESECURITY:
|
||
|
//always allow the Security Translation wizards now
|
||
|
//that we can reACL using a sid mapping file
|
||
|
*flags = MF_ENABLED;
|
||
|
break;
|
||
|
case ID_TOP_RETRY:
|
||
|
if ( ! CanRetry )
|
||
|
*flags = MF_DISABLED | MF_GRAYED;
|
||
|
else
|
||
|
*flags = MF_ENABLED;
|
||
|
break;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
void CRootNode::UpdateMigratedObjectsTable()
|
||
|
{
|
||
|
ISrcSidUpdatePtr pSrcUpdate(CLSID_SrcSidUpdate);
|
||
|
HRESULT hr;
|
||
|
VARIANT_BOOL bvar;
|
||
|
VARIANT_BOOL bHide = VARIANT_FALSE;
|
||
|
CString title;
|
||
|
CString sFormat;
|
||
|
CString msg;
|
||
|
|
||
|
//see if the new Source domain Sid column is in this migrated object's table
|
||
|
hr = pSrcUpdate->raw_QueryForSrcSidColumn(&bvar);
|
||
|
if ( FAILED(hr) )
|
||
|
{
|
||
|
_bstr_t sDescription = HResultToText(hr);
|
||
|
title.LoadString(IDS_QUERYCLM_TITLE);
|
||
|
sFormat.LoadString(IDS_ERR_QUERYCLM_MSG);
|
||
|
msg.Format(sFormat, (WCHAR*)sDescription);
|
||
|
|
||
|
MessageBox(NULL, msg, title, MB_ICONERROR | MB_OK);
|
||
|
_com_issue_error(hr);
|
||
|
return;
|
||
|
}
|
||
|
//if not then run the code to add it
|
||
|
if ( bvar == VARIANT_FALSE )
|
||
|
{
|
||
|
//add and populate the new source Sid column
|
||
|
hr = pSrcUpdate->raw_CreateSrcSidColumn(bHide, &bvar);
|
||
|
if ( FAILED(hr) )
|
||
|
{
|
||
|
_bstr_t sDescription = HResultToText(hr);
|
||
|
title.LoadString(IDS_NOSRCSIDCLM_TITLE);
|
||
|
sFormat.LoadString(IDS_ERR_NOSRCSIDCLM_MSG);
|
||
|
msg.Format(sFormat, (WCHAR*)sDescription);
|
||
|
|
||
|
MessageBox(NULL, msg, title, MB_ICONERROR | MB_OK);
|
||
|
_com_issue_error(hr);
|
||
|
}
|
||
|
if ( bvar == VARIANT_FALSE )
|
||
|
{
|
||
|
// title.LoadString(IDS_NOSRCSIDCLM_TITLE);
|
||
|
// msg.LoadString(IDS_ERR_NOSRCSIDCLM_MSG);
|
||
|
|
||
|
// MessageBox(NULL, msg, title, MB_ICONERROR | MB_OK);
|
||
|
_com_issue_error(hr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CRootNode::UpdateAccountReferenceTable()
|
||
|
{
|
||
|
IIManageDBPtr pDB(CLSID_IManageDB);
|
||
|
VARIANT_BOOL bFound = VARIANT_FALSE;
|
||
|
|
||
|
//see if the new AccountSid column has already been added to
|
||
|
//the AccountRefs table
|
||
|
bFound = pDB->SidColumnInARTable();
|
||
|
|
||
|
//if not there, create it
|
||
|
if (!bFound)
|
||
|
pDB->CreateSidColumnInAR();
|
||
|
}
|