1844 lines
59 KiB
C++
1844 lines
59 KiB
C++
|
/*++
|
|||
|
|
|||
|
<EFBFBD> 1998 Seagate Software, Inc. All rights reserved.
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Wsbdb.cpp
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
These classes provide support for data bases.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Ron White [ronw] 19-Nov-1996
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "stdafx.h"
|
|||
|
|
|||
|
#include "wsbdbsys.h"
|
|||
|
#include "wsbdbses.h"
|
|||
|
#include "wsbdbkey.h"
|
|||
|
|
|||
|
|
|||
|
#include <mbstring.h>
|
|||
|
|
|||
|
#define JET_DATA_COLUMN_NAME "Data"
|
|||
|
#define JET_INDEX_COLUMN_NAME "Index"
|
|||
|
#define JET_INFO_TABLE_NAME "Info"
|
|||
|
#define JET_SEQNUM_COLUMN_NAME "SeqNum"
|
|||
|
#define SESSION_INFO_INITIAL_SIZE 4
|
|||
|
#define SESSION_INFO_EXPANSION 6
|
|||
|
|
|||
|
#define JET_CURRENT_SESSION (pDbInfo->SessionInfo[m_SessionIndex].SessionId)
|
|||
|
#define JET_CURRENT_DB (pDbInfo->SessionInfo[m_SessionIndex].DbId)
|
|||
|
|
|||
|
#define CATCH_ANY_EXCEPTION catch (...) { \
|
|||
|
WsbTraceAndLogEvent(WSB_MESSAGE_IDB_EXCEPTION, 0, NULL, NULL); \
|
|||
|
WsbTrace(OLESTR("GetLastError = %ld\n"), GetLastError()); \
|
|||
|
hr = WSB_E_IDB_EXCEPTION; }
|
|||
|
|
|||
|
|
|||
|
// Local stuff
|
|||
|
|
|||
|
// These structures hold extra implementation data
|
|||
|
typedef struct {
|
|||
|
} IMP_KEY_INFO;
|
|||
|
|
|||
|
typedef struct {
|
|||
|
IMP_KEY_INFO* Key;
|
|||
|
} IMP_REC_INFO;
|
|||
|
|
|||
|
// IMP_TABLE_INFO holds information for each open table
|
|||
|
typedef struct {
|
|||
|
JET_TABLEID TableId;
|
|||
|
JET_COLUMNID ColId;
|
|||
|
} IMP_TABLE_INFO;
|
|||
|
|
|||
|
// IMP_SESSION_INFO holds information for each thread
|
|||
|
typedef struct {
|
|||
|
JET_SESID SessionId; // The Jet session
|
|||
|
JET_DBID DbId; // The session's DB ID for this DB
|
|||
|
IMP_TABLE_INFO* pTableInfo; // Array of table information
|
|||
|
} IMP_SESSION_INFO;
|
|||
|
|
|||
|
|
|||
|
typedef struct {
|
|||
|
BOOL IsLoaded; // DB info is loaded into memory
|
|||
|
USHORT OpenCount; // Open ref. count
|
|||
|
IMP_REC_INFO* RecInfo; // Array of record info
|
|||
|
|
|||
|
SHORT nSessions;
|
|||
|
IMP_SESSION_INFO* SessionInfo;
|
|||
|
} IMP_DB_INFO;
|
|||
|
|
|||
|
// These structures are saved in the data file
|
|||
|
typedef struct {
|
|||
|
ULONG Type; // Key type ID
|
|||
|
ULONG Size; // Key size in bytes
|
|||
|
ULONG Flags; // IDB_KEY_FLAG_* flags
|
|||
|
} FILE_KEY_INFO;
|
|||
|
|
|||
|
typedef struct {
|
|||
|
ULONG Type; // Record type ID
|
|||
|
CLSID EntityClassId; // Derived entity class ID
|
|||
|
ULONG Flags; // IDB_REC_FLAG_* flags
|
|||
|
ULONG MinSize; // (Minimum) record size in bytes
|
|||
|
ULONG MaxSize; // Maximum record size
|
|||
|
USHORT nKeys; // Number of keys in this record type
|
|||
|
FILE_KEY_INFO Key[IDB_MAX_KEYS_PER_REC];
|
|||
|
} FILE_REC_INFO;
|
|||
|
|
|||
|
typedef struct {
|
|||
|
USHORT nRecTypes; // Number of record types
|
|||
|
ULONG version; // DB version
|
|||
|
} FILE_DB_INFO;
|
|||
|
|
|||
|
|
|||
|
//***************************************************************
|
|||
|
// Local function prototypes
|
|||
|
|
|||
|
static HRESULT jet_get_column_id(JET_SESID jet_session, JET_DBID DbId,
|
|||
|
char* pTableName, char* pColumnName, JET_COLUMNID* pColId);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//***************************************************************
|
|||
|
// Function definitions
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::Create(
|
|||
|
IN OLECHAR* path,
|
|||
|
ULONG flags
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDb::Create
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::Create"), OLESTR("path = <%ls>"), path);
|
|||
|
|
|||
|
try {
|
|||
|
int key_index;
|
|||
|
ULONG memSize;
|
|||
|
int rec_index;
|
|||
|
|
|||
|
WsbAssert(0 != path, E_POINTER);
|
|||
|
WsbAssert(0 != m_RecInfo, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAssert(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
Lock();
|
|||
|
WsbAffirm(!pDbInfo->IsLoaded, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirm(!pDbInfo->RecInfo, WSB_E_NOT_INITIALIZED);
|
|||
|
|
|||
|
// Save the path
|
|||
|
m_path = path;
|
|||
|
|
|||
|
// Check validity of some info that the derived class is
|
|||
|
// suppose to supply.
|
|||
|
WsbAffirm(m_version != 0, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirm(m_nRecTypes > 0, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirm(m_nRecTypes <= IDB_MAX_REC_TYPES, WSB_E_INVALID_DATA);
|
|||
|
pDbInfo->IsLoaded = TRUE;
|
|||
|
|
|||
|
// Allocate the RecInfo array
|
|||
|
memSize = m_nRecTypes * sizeof(IMP_REC_INFO);
|
|||
|
pDbInfo->RecInfo = (IMP_REC_INFO*)WsbAlloc(memSize);
|
|||
|
WsbAffirm(pDbInfo->RecInfo, E_OUTOFMEMORY);
|
|||
|
ZeroMemory(pDbInfo->RecInfo, memSize);
|
|||
|
|
|||
|
char index_names[IDB_MAX_KEYS_PER_REC + 1][20];
|
|||
|
JET_COLUMNCREATE jet_columns[IDB_MAX_KEYS_PER_REC + 2];
|
|||
|
JET_INDEXCREATE jet_indices[IDB_MAX_KEYS_PER_REC + 1];
|
|||
|
JET_TABLECREATE jet_table;
|
|||
|
JET_ERR jstat;
|
|||
|
char key_names[IDB_MAX_KEYS_PER_REC + 1][22];
|
|||
|
char* name;
|
|||
|
char table_name[20];
|
|||
|
JET_GRBIT createFlags = 0;
|
|||
|
|
|||
|
// Start a Jet session for this thread
|
|||
|
WsbAffirmHr(jet_init());
|
|||
|
|
|||
|
// Make sure there's room for another DB
|
|||
|
CComQIPtr<IWsbDbSysPriv, &IID_IWsbDbSysPriv> pDbSysPriv = m_pWsbDbSys;
|
|||
|
WsbAffirmPointer(pDbSysPriv);
|
|||
|
WsbAffirmHr(pDbSysPriv->DbAttachedAdd(path, FALSE));
|
|||
|
|
|||
|
// Set creation flag
|
|||
|
if (flags & IDB_CREATE_FLAG_NO_TRANSACTION) {
|
|||
|
// Setting this flag stil allow transaction calls - they are just being ignored and MT-safe is not guaranteed
|
|||
|
createFlags |= (JET_bitDbVersioningOff & JET_bitDbRecoveryOff);
|
|||
|
}
|
|||
|
|
|||
|
// Create the DB
|
|||
|
WsbAffirmHr(wsb_db_jet_fix_path(path, L"." IDB_DB_FILE_SUFFIX, &name));
|
|||
|
jstat = JetCreateDatabase(JET_CURRENT_SESSION, name, NULL, &JET_CURRENT_DB, createFlags);
|
|||
|
WsbTrace(OLESTR("JetCreateDB = %ld\n"), (LONG)jstat);
|
|||
|
WsbFree(name);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
|
|||
|
// Set up constant part of table structure
|
|||
|
jet_table.cbStruct = sizeof(JET_TABLECREATE);
|
|||
|
jet_table.szTemplateTableName = NULL;
|
|||
|
jet_table.ulPages = 4; // ????
|
|||
|
jet_table.ulDensity = 50; // ?????
|
|||
|
jet_table.rgcolumncreate = jet_columns;
|
|||
|
jet_table.rgindexcreate = jet_indices;
|
|||
|
jet_table.grbit = 0;
|
|||
|
|
|||
|
// Set up the constant part of the column structures
|
|||
|
ZeroMemory(&jet_columns, sizeof(jet_columns));
|
|||
|
ZeroMemory(&jet_indices, sizeof(jet_indices));
|
|||
|
jet_columns[0].cbStruct = sizeof(JET_COLUMNCREATE);
|
|||
|
jet_columns[0].szColumnName = JET_DATA_COLUMN_NAME;
|
|||
|
jet_columns[1].cbStruct = sizeof(JET_COLUMNCREATE);
|
|||
|
|
|||
|
// Create a "table" to hold info about this DB
|
|||
|
jet_table.szTableName = JET_INFO_TABLE_NAME;
|
|||
|
jet_table.cColumns = 2;
|
|||
|
jet_table.cIndexes = 1;
|
|||
|
jet_columns[0].coltyp = JET_coltypLongBinary;
|
|||
|
jet_columns[0].cbMax = sizeof(FILE_REC_INFO);
|
|||
|
jet_columns[1].szColumnName = JET_INDEX_COLUMN_NAME;
|
|||
|
jet_columns[1].coltyp = JET_coltypShort;
|
|||
|
jet_columns[1].cbMax = sizeof(SHORT);
|
|||
|
jet_indices[0].cbStruct = sizeof(JET_INDEXCREATE);
|
|||
|
jet_indices[0].szIndexName = JET_INDEX_COLUMN_NAME;
|
|||
|
ZeroMemory(key_names[0], 22);
|
|||
|
sprintf(key_names[0], "+%s", JET_INDEX_COLUMN_NAME);
|
|||
|
jet_indices[0].szKey = key_names[0];
|
|||
|
jet_indices[0].cbKey = strlen(key_names[0]) + 2;
|
|||
|
jet_indices[0].grbit |= JET_bitIndexPrimary;
|
|||
|
jet_indices[0].ulDensity = 90;
|
|||
|
jstat = JetCreateTableColumnIndex(JET_CURRENT_SESSION, JET_CURRENT_DB, &jet_table);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Create: JetCreateTableColumnIndex status = %ld\n"), jstat);
|
|||
|
if (JET_errSuccess != jstat) {
|
|||
|
WsbTrace(OLESTR("CWsbDb::Create: JetCreateTableColumnIndex, cCreated = %ld\n"), jet_table.cCreated);
|
|||
|
}
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
jstat = JetCloseTable(JET_CURRENT_SESSION, jet_table.tableid);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Create: close TableId = %ld, jstat = %ld\n"),
|
|||
|
jet_table.tableid, jstat);
|
|||
|
|
|||
|
// Write DB info
|
|||
|
jstat = JetBeginTransaction(JET_CURRENT_SESSION);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Create: JetBeginTransaction = %ld\n"), jstat);
|
|||
|
jstat = jet_save_info();
|
|||
|
if (JET_errSuccess == jstat) {
|
|||
|
jstat = JetCommitTransaction(JET_CURRENT_SESSION, 0);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Create: JetCommitTransaction = %ld\n"), jstat);
|
|||
|
} else {
|
|||
|
HRESULT hr2 = jet_error(jstat);
|
|||
|
|
|||
|
jstat = JetRollback(JET_CURRENT_SESSION, 0);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Create: JetRollback = %ld\n"), jstat);
|
|||
|
WsbThrow(hr2);
|
|||
|
}
|
|||
|
|
|||
|
// We create a table for each record type. The first column of each
|
|||
|
// table is the record (stored as a blob). The second column is a
|
|||
|
// unique sequence number for each record. The rest of the columns are for
|
|||
|
// key values used as indices.
|
|||
|
jet_columns[1].szColumnName = JET_SEQNUM_COLUMN_NAME;
|
|||
|
jet_columns[1].coltyp = JET_coltypLong;
|
|||
|
jet_columns[1].cbMax = sizeof(ULONG);
|
|||
|
jet_columns[1].grbit = JET_bitColumnAutoincrement;
|
|||
|
jet_indices[0].cbStruct = sizeof(JET_INDEXCREATE);
|
|||
|
strcpy(index_names[0], JET_SEQNUM_COLUMN_NAME);
|
|||
|
jet_indices[0].szIndexName = index_names[0];
|
|||
|
ZeroMemory(key_names[0], 22);
|
|||
|
sprintf(key_names[0], "+%s", index_names[0]);
|
|||
|
jet_indices[0].szKey = key_names[0];
|
|||
|
jet_indices[0].cbKey = strlen(key_names[0]) + 2;
|
|||
|
jet_indices[0].grbit = 0;
|
|||
|
jet_indices[0].ulDensity = 90;
|
|||
|
|
|||
|
// Loop over record types
|
|||
|
for (rec_index = 0; rec_index < m_nRecTypes; rec_index++) {
|
|||
|
WsbAffirm(m_RecInfo[rec_index].Type > 0, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirm(m_RecInfo[rec_index].nKeys > 0, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirm(m_RecInfo[rec_index].nKeys <= IDB_MAX_KEYS_PER_REC, WSB_E_INVALID_DATA);
|
|||
|
|
|||
|
// Allocate the Key array
|
|||
|
memSize = m_RecInfo[rec_index].nKeys * sizeof(IMP_KEY_INFO);
|
|||
|
pDbInfo->RecInfo[rec_index].Key = (IMP_KEY_INFO*)WsbAlloc(memSize);
|
|||
|
WsbAffirm(pDbInfo->RecInfo[rec_index].Key, E_OUTOFMEMORY);
|
|||
|
ZeroMemory(pDbInfo->RecInfo[rec_index].Key, memSize);
|
|||
|
|
|||
|
// Fill in the table structure with info specific to this
|
|||
|
// record type
|
|||
|
WsbAffirmHr(jet_make_table_name(m_RecInfo[rec_index].Type, table_name, 20));
|
|||
|
jet_table.szTableName = table_name;
|
|||
|
jet_table.cColumns = m_RecInfo[rec_index].nKeys + 2;
|
|||
|
jet_table.cIndexes = m_RecInfo[rec_index].nKeys + 1;
|
|||
|
|
|||
|
// Fill in the column structure for the record itself
|
|||
|
if (m_RecInfo[rec_index].MaxSize < 255) {
|
|||
|
jet_columns[0].coltyp = JET_coltypBinary;
|
|||
|
} else {
|
|||
|
jet_columns[0].coltyp = JET_coltypLongBinary;
|
|||
|
}
|
|||
|
jet_columns[0].cbMax = m_RecInfo[rec_index].MaxSize;
|
|||
|
|
|||
|
|
|||
|
// Loop over keys
|
|||
|
for (key_index = 0; key_index < m_RecInfo[rec_index].nKeys;
|
|||
|
key_index++) {
|
|||
|
WsbAffirm(m_RecInfo[rec_index].Key[key_index].Type > 0, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirm(m_RecInfo[rec_index].Key[key_index].Size <= IDB_MAX_KEY_SIZE,
|
|||
|
WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirm(!(m_RecInfo[rec_index].Key[key_index].Flags & IDB_KEY_FLAG_PRIMARY) ||
|
|||
|
!(m_RecInfo[rec_index].Key[key_index].Flags & IDB_KEY_FLAG_DUP_ALLOWED),
|
|||
|
WSB_E_IDB_PRIMARY_UNIQUE);
|
|||
|
|
|||
|
|
|||
|
// Fill in a column structure for each key
|
|||
|
jet_columns[key_index + 2].cbStruct = sizeof(JET_COLUMNCREATE);
|
|||
|
WsbAffirmHr(jet_make_index_name(m_RecInfo[rec_index].Key[key_index].Type,
|
|||
|
index_names[key_index + 1], 20));
|
|||
|
jet_columns[key_index + 2].szColumnName = index_names[key_index + 1];
|
|||
|
jet_columns[key_index + 2].grbit = JET_bitColumnFixed;
|
|||
|
jet_columns[key_index + 2].pvDefault = NULL;
|
|||
|
jet_columns[key_index + 2].cbDefault = 0;
|
|||
|
if (m_RecInfo[rec_index].Key[key_index].Size < 255) {
|
|||
|
jet_columns[key_index + 2].coltyp = JET_coltypBinary;
|
|||
|
} else {
|
|||
|
jet_columns[key_index + 2].coltyp = JET_coltypLongBinary;
|
|||
|
}
|
|||
|
jet_columns[key_index + 2].cbMax = m_RecInfo[rec_index].Key[key_index].Size;
|
|||
|
|
|||
|
// Fill in an index structure for each key
|
|||
|
jet_indices[key_index + 1].cbStruct = sizeof(JET_INDEXCREATE);
|
|||
|
jet_indices[key_index + 1].szIndexName = index_names[key_index + 1];
|
|||
|
ZeroMemory(key_names[key_index + 1], 22);
|
|||
|
sprintf(key_names[key_index + 1], "+%s\0", index_names[key_index + 1]);
|
|||
|
jet_indices[key_index + 1].szKey = key_names[key_index + 1];
|
|||
|
jet_indices[key_index + 1].cbKey = strlen(key_names[key_index + 1]) + 2;
|
|||
|
if (m_RecInfo[rec_index].Key[key_index].Flags & IDB_KEY_FLAG_DUP_ALLOWED) {
|
|||
|
jet_indices[key_index + 1].grbit = 0;
|
|||
|
} else {
|
|||
|
jet_indices[key_index + 1].grbit = JET_bitIndexUnique;
|
|||
|
}
|
|||
|
if (m_RecInfo[rec_index].Key[key_index].Flags & IDB_KEY_FLAG_PRIMARY) {
|
|||
|
jet_indices[key_index + 1].grbit |= JET_bitIndexPrimary;
|
|||
|
}
|
|||
|
jet_indices[key_index + 1].ulDensity = 50;
|
|||
|
} // End of key loop
|
|||
|
|
|||
|
// Set table creation flags
|
|||
|
if (flags & IDB_CREATE_FLAG_FIXED_SCHEMA) {
|
|||
|
jet_table.grbit |= JET_bitTableCreateFixedDDL;
|
|||
|
}
|
|||
|
|
|||
|
// Create the "table" for each record type; this call defines
|
|||
|
// the columns (fields) and index keys
|
|||
|
jstat = JetCreateTableColumnIndex(JET_CURRENT_SESSION, JET_CURRENT_DB, &jet_table);
|
|||
|
WsbTrace(OLESTR("JetCreateTableColumnIndex = %ld\n"), jstat);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
jstat = JetCloseTable(JET_CURRENT_SESSION, jet_table.tableid);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Create: close TableId = %ld, jstat = %ld\n"),
|
|||
|
jet_table.tableid, jstat);
|
|||
|
} // End of record loop
|
|||
|
|
|||
|
jstat = JetCloseDatabase(JET_CURRENT_SESSION, JET_CURRENT_DB, 0);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Create: JetCloseDatabase = %ld\n"),
|
|||
|
(LONG)jstat);
|
|||
|
JET_CURRENT_DB = 0;
|
|||
|
|
|||
|
pDbInfo->OpenCount = 0;
|
|||
|
|
|||
|
} WsbCatchAndDo(hr,
|
|||
|
WsbLogEvent(WSB_MESSAGE_IDB_CREATE_FAILED, 0, NULL,
|
|||
|
WsbAbbreviatePath(path, 120), NULL);
|
|||
|
)
|
|||
|
CATCH_ANY_EXCEPTION
|
|||
|
|
|||
|
if (pDbInfo) {
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::Create"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::Delete(
|
|||
|
IN OLECHAR *path,
|
|||
|
ULONG flags
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDb::Delete
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
char* name = NULL;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::Delete"), OLESTR("path = <%ls>"),
|
|||
|
WsbStringAsString(path));
|
|||
|
|
|||
|
try {
|
|||
|
CWsbStringPtr DeletePath;
|
|||
|
|
|||
|
WsbAssert(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
Lock();
|
|||
|
|
|||
|
// Can't delete it if it's open
|
|||
|
WsbAffirm(pDbInfo->OpenCount == 0, E_UNEXPECTED);
|
|||
|
|
|||
|
if (NULL == path) {
|
|||
|
path = m_path;
|
|||
|
}
|
|||
|
WsbAffirm(path && wcslen(path), S_FALSE);
|
|||
|
WsbAffirmHr(wsb_db_jet_fix_path(path, L"." IDB_DB_FILE_SUFFIX, &name));
|
|||
|
|
|||
|
// Detach (if attached)
|
|||
|
CComQIPtr<IWsbDbSysPriv, &IID_IWsbDbSysPriv> pDbSysPriv = m_pWsbDbSys;
|
|||
|
WsbAffirmPointer(pDbSysPriv);
|
|||
|
WsbAffirmHr(pDbSysPriv->DbAttachedRemove(path));
|
|||
|
|
|||
|
// Now delete it
|
|||
|
DeletePath = name;
|
|||
|
if (!DeleteFile(DeletePath)) {
|
|||
|
DWORD err = GetLastError();
|
|||
|
WsbTrace(OLESTR("CWsbDb::Delete: DeleteFile(%ls) failed, error = %ld\n"),
|
|||
|
static_cast<OLECHAR*>(DeletePath), err);
|
|||
|
WsbThrow(HRESULT_FROM_WIN32(err));
|
|||
|
}
|
|||
|
|
|||
|
// Put message in event log
|
|||
|
if (flags & IDB_DELETE_FLAG_NO_ERROR) {
|
|||
|
WsbLogEvent(WSB_E_IDB_DATABASE_DELETED_NO_ERROR, 0, NULL,
|
|||
|
WsbAbbreviatePath(DeletePath, 120), NULL);
|
|||
|
} else {
|
|||
|
WsbLogEvent(WSB_E_IDB_DATABASE_DELETED, 0, NULL,
|
|||
|
WsbAbbreviatePath(DeletePath, 120), NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
WsbCatch(hr)
|
|||
|
|
|||
|
if (pDbInfo) {
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
if (name) {
|
|||
|
WsbFree(name);
|
|||
|
}
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::Delete"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::Dump(
|
|||
|
IN OLECHAR* Filename,
|
|||
|
IN ULONG Flags,
|
|||
|
IN ULONG Data
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDb::Dump
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HANDLE hFile = 0;
|
|||
|
HRESULT hr = S_OK;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
CComPtr<IWsbDbSession> pSession;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::Dump"), OLESTR("path = <%ls>"), Filename);
|
|||
|
|
|||
|
try {
|
|||
|
DWORD CreateFlags;
|
|||
|
int i;
|
|||
|
int index;
|
|||
|
CComPtr<IWsbDbEntity> pIRec;
|
|||
|
CComPtr<IStream> pStream;
|
|||
|
|
|||
|
WsbAssert(0 != Filename, E_POINTER);
|
|||
|
WsbAssert(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
Lock();
|
|||
|
// WsbAffirmHr(session_current_index(Session));
|
|||
|
|
|||
|
// Open the Db
|
|||
|
// SteveW
|
|||
|
// added code to ensure that a database was opened
|
|||
|
// if not go on to the next database, but do not
|
|||
|
// throw an exception.
|
|||
|
//
|
|||
|
hr = Open(&pSession);
|
|||
|
if (hr == S_OK) {
|
|||
|
|
|||
|
// Open/create the output file
|
|||
|
if (Flags & IDB_DUMP_FLAG_APPEND_TO_FILE) {
|
|||
|
CreateFlags = OPEN_ALWAYS;
|
|||
|
} else {
|
|||
|
CreateFlags = CREATE_ALWAYS;
|
|||
|
}
|
|||
|
hFile = CreateFile(Filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
|||
|
CreateFlags, FILE_ATTRIBUTE_NORMAL, NULL);
|
|||
|
WsbAffirmHandle(hFile);
|
|||
|
if (Flags & IDB_DUMP_FLAG_APPEND_TO_FILE) {
|
|||
|
// Position to the end of the file
|
|||
|
SetFilePointer(hFile, 0, NULL, FILE_END);
|
|||
|
}
|
|||
|
|
|||
|
// Create the output stream
|
|||
|
WsbAffirmHr(CreateStreamOnHGlobal(NULL, TRUE, &pStream));
|
|||
|
|
|||
|
// Dump general DB info
|
|||
|
if (Flags & IDB_DUMP_FLAG_DB_INFO) {
|
|||
|
WsbAffirmHr(WsbPrintfToStream(pStream, OLESTR("Dump of DB: %ls\n"),
|
|||
|
static_cast<WCHAR *>(m_path)));
|
|||
|
WsbAffirmHr(WsbPrintfToStream(pStream, OLESTR(" version = %ld, # record types = %d\n"),
|
|||
|
m_version, m_nRecTypes));
|
|||
|
WsbAffirmHr(WsbStreamToFile(hFile, pStream, TRUE));
|
|||
|
}
|
|||
|
|
|||
|
// Loop over record types
|
|||
|
for (i = 0; i < m_nRecTypes; i++) {
|
|||
|
|
|||
|
// Dump record info
|
|||
|
if (Flags & IDB_DUMP_FLAG_REC_INFO) {
|
|||
|
WsbAffirmHr(WsbPrintfToStream(pStream, OLESTR("RecType = %8ld, Flags = %0.8lx, "),
|
|||
|
m_RecInfo[i].Type, m_RecInfo[i].Flags));
|
|||
|
WsbAffirmHr(WsbPrintfToStream(pStream, OLESTR("MaxSize = %8ld, # keys = %4d\n"),
|
|||
|
m_RecInfo[i].MaxSize, m_RecInfo[i].nKeys));
|
|||
|
WsbAffirmHr(WsbStreamToFile(hFile, pStream, TRUE));
|
|||
|
}
|
|||
|
|
|||
|
// Dump key info
|
|||
|
if (Flags & IDB_DUMP_FLAG_KEY_INFO) {
|
|||
|
for (int j = 0; j < m_RecInfo[i].nKeys; j++) {
|
|||
|
WsbAffirmHr(WsbPrintfToStream(pStream, OLESTR(" KeyType = %8ld, Size = %8ld, Flags = %0.8lx\n"),
|
|||
|
m_RecInfo[i].Key[j].Type, m_RecInfo[i].Key[j].Size, m_RecInfo[i].Key[j].Flags));
|
|||
|
}
|
|||
|
WsbAffirmHr(WsbStreamToFile(hFile, pStream, TRUE));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Dump records
|
|||
|
if (Flags & (IDB_DUMP_FLAG_RECORDS | IDB_DUMP_FLAG_RECORD_TYPE)) {
|
|||
|
for (i = 0; i < m_nRecTypes; i++) {
|
|||
|
if (!(Flags & IDB_DUMP_FLAG_RECORDS) &&
|
|||
|
m_RecInfo[i].Type != Data) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
WsbAffirmHr(WsbPrintfToStream(pStream, OLESTR("\n*** Dump of records of Type = %ld ***\n"),
|
|||
|
m_RecInfo[i].Type));
|
|||
|
|
|||
|
// Get a DB entity
|
|||
|
pIRec = 0;
|
|||
|
WsbAffirmHr(GetEntity(pSession, m_RecInfo[i].Type, IID_IWsbDbEntity,
|
|||
|
(void**)&pIRec));
|
|||
|
|
|||
|
// Loop over records
|
|||
|
index = 0;
|
|||
|
hr = pIRec->First();
|
|||
|
while (S_OK == hr) {
|
|||
|
WsbAffirmHr(WsbPrintfToStream(pStream, OLESTR("%0.5d "), index));
|
|||
|
WsbAffirmHr(pIRec->Print(pStream));
|
|||
|
WsbAffirmHr(WsbPrintfToStream(pStream, OLESTR("\n")));
|
|||
|
WsbAffirmHr(WsbStreamToFile(hFile, pStream, TRUE));
|
|||
|
|
|||
|
hr = pIRec->Next();
|
|||
|
index++;
|
|||
|
}
|
|||
|
if (WSB_E_NOTFOUND == hr) {
|
|||
|
hr = S_OK;
|
|||
|
} else {
|
|||
|
WsbAffirmHr(hr);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} WsbCatch(hr)
|
|||
|
CATCH_ANY_EXCEPTION
|
|||
|
|
|||
|
if (hFile) {
|
|||
|
CloseHandle(hFile);
|
|||
|
}
|
|||
|
if (pSession) {
|
|||
|
Close(pSession);
|
|||
|
}
|
|||
|
if (pDbInfo) {
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::Dump"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::Locate(
|
|||
|
IN OLECHAR *path
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDb::Locate
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::Locate"), OLESTR("path = <%ls>"), path);
|
|||
|
|
|||
|
try {
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
Lock();
|
|||
|
WsbAffirm(pDbInfo->OpenCount == 0, E_UNEXPECTED);
|
|||
|
m_path = path;
|
|||
|
|
|||
|
JET_ERR jstat;
|
|||
|
char* name;
|
|||
|
|
|||
|
// Start a Jet session for this thread
|
|||
|
WsbAffirmHr(jet_init());
|
|||
|
|
|||
|
WsbAffirmHr(wsb_db_jet_fix_path(path, L"." IDB_DB_FILE_SUFFIX, &name));
|
|||
|
|
|||
|
hr = S_OK;
|
|||
|
try {
|
|||
|
CComQIPtr<IWsbDbSysPriv, &IID_IWsbDbSysPriv> pDbSysPriv = m_pWsbDbSys;
|
|||
|
WsbAffirmPointer(pDbSysPriv);
|
|||
|
WsbAffirmHr(pDbSysPriv->DbAttachedAdd(path, TRUE));
|
|||
|
jstat = JetOpenDatabase(JET_CURRENT_SESSION, name, NULL, &JET_CURRENT_DB, 0);
|
|||
|
if (jstat == JET_errDatabaseNotFound) {
|
|||
|
WsbThrow(STG_E_FILENOTFOUND);
|
|||
|
} else {
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
WsbFree(name);
|
|||
|
WsbAffirmHr(hr);
|
|||
|
|
|||
|
// Load information about this DB
|
|||
|
hr = jet_load_info();
|
|||
|
jstat = JetCloseDatabase(JET_CURRENT_SESSION, JET_CURRENT_DB, 0);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Locate: JetCloseDatabase = %ld\n"),
|
|||
|
(LONG)jstat);
|
|||
|
JET_CURRENT_DB = 0;
|
|||
|
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
pDbInfo->OpenCount = 0;
|
|||
|
|
|||
|
} WsbCatch(hr)
|
|||
|
CATCH_ANY_EXCEPTION
|
|||
|
|
|||
|
if (pDbInfo) {
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::Locate"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::Open(
|
|||
|
OUT IWsbDbSession** ppSession
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDb::Open
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::Open"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
ULONG Size = 0;
|
|||
|
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
Lock();
|
|||
|
|
|||
|
WsbAffirmHr(m_path.GetLen(&Size));
|
|||
|
WsbAffirm(Size > 0, WSB_E_NOT_INITIALIZED);
|
|||
|
|
|||
|
// Make sure we have a session
|
|||
|
WsbAffirm(0 != ppSession, E_POINTER);
|
|||
|
if (0 == *ppSession) {
|
|||
|
WsbAffirmHr(m_pWsbDbSys->NewSession(ppSession));
|
|||
|
WsbTrace(OLESTR("CWsbDb::Open: session created\n"));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
int i;
|
|||
|
JET_ERR jstat;
|
|||
|
ULONG memSize;
|
|||
|
char* name;
|
|||
|
JET_SESID SessionId;
|
|||
|
int s_index;
|
|||
|
IMP_SESSION_INFO* s_info = pDbInfo->SessionInfo;
|
|||
|
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = *ppSession;
|
|||
|
|
|||
|
WsbAffirm(0 < pDbInfo->nSessions, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirm(pSessionPriv, E_FAIL);
|
|||
|
|
|||
|
// Get the JET session ID
|
|||
|
WsbAffirmHr(pSessionPriv->GetJetId(&SessionId));
|
|||
|
|
|||
|
// We need to save session info; look for an empty slot.
|
|||
|
WsbTrace(OLESTR("CWsbDb::Open: nSessions = %d, SessionId = %lx\n"), (
|
|||
|
int)pDbInfo->nSessions, SessionId);
|
|||
|
s_index = pDbInfo->nSessions;
|
|||
|
for (i = 0; i < pDbInfo->nSessions; i++) {
|
|||
|
WsbTrace(OLESTR("CWsbDb::Open: s_info[%d] = %lx\n"), i,
|
|||
|
s_info[i].SessionId);
|
|||
|
|
|||
|
// Check for a duplicate session ID already in the table
|
|||
|
if (SessionId == s_info[i].SessionId) {
|
|||
|
s_index = i;
|
|||
|
break;
|
|||
|
|
|||
|
// Check for an unused slot
|
|||
|
} else if (0 == s_info[i].SessionId && 0 == s_info[i].DbId &&
|
|||
|
s_index == pDbInfo->nSessions) {
|
|||
|
s_index = i;
|
|||
|
}
|
|||
|
}
|
|||
|
WsbTrace(OLESTR("CWsbDb::Open: s_index = %d\n"), s_index);
|
|||
|
|
|||
|
if (s_index == pDbInfo->nSessions) {
|
|||
|
SHORT newNum;
|
|||
|
|
|||
|
// Didn't find an empty slot; expand the array
|
|||
|
newNum = (SHORT) ( s_index + SESSION_INFO_EXPANSION );
|
|||
|
WsbTrace(OLESTR("CWsbDb::Open: expanding session table from %d to %d\n"),
|
|||
|
s_index, newNum);
|
|||
|
memSize = newNum * sizeof(IMP_SESSION_INFO);
|
|||
|
s_info = (IMP_SESSION_INFO*)WsbRealloc(pDbInfo->SessionInfo,
|
|||
|
memSize);
|
|||
|
WsbAffirm(s_info, E_OUTOFMEMORY);
|
|||
|
ZeroMemory(&s_info[s_index], SESSION_INFO_EXPANSION *
|
|||
|
sizeof(IMP_SESSION_INFO));
|
|||
|
pDbInfo->SessionInfo = s_info;
|
|||
|
pDbInfo->nSessions = newNum;
|
|||
|
}
|
|||
|
|
|||
|
// Save the session ID and index
|
|||
|
m_SessionIndex = s_index;
|
|||
|
s_info[s_index].SessionId = SessionId;
|
|||
|
WsbTrace(OLESTR("CWsbDB::Open, s_info = %lx, SessionId[%d] = %lx\n"),
|
|||
|
(LONG)s_index, m_SessionIndex, JET_CURRENT_SESSION);
|
|||
|
|
|||
|
WsbAffirmHr(wsb_db_jet_fix_path(m_path, L"." IDB_DB_FILE_SUFFIX, &name));
|
|||
|
|
|||
|
CComQIPtr<IWsbDbSysPriv, &IID_IWsbDbSysPriv> pDbSysPriv = m_pWsbDbSys;
|
|||
|
WsbAffirmPointer(pDbSysPriv);
|
|||
|
WsbAffirmHr(pDbSysPriv->DbAttachedAdd(m_path, TRUE));
|
|||
|
jstat = JetOpenDatabase(JET_CURRENT_SESSION, name, NULL,
|
|||
|
&JET_CURRENT_DB, 0);
|
|||
|
WsbFree(name);
|
|||
|
if (jstat == JET_errDatabaseNotFound) {
|
|||
|
WsbThrow(STG_E_FILENOTFOUND);
|
|||
|
} else {
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
}
|
|||
|
|
|||
|
// Allocate/zero the table info array
|
|||
|
memSize = m_nRecTypes * sizeof(IMP_TABLE_INFO);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Open: pTableInfo = %lx\n"),
|
|||
|
s_info[m_SessionIndex].pTableInfo);
|
|||
|
if (NULL == s_info[m_SessionIndex].pTableInfo) {
|
|||
|
s_info[m_SessionIndex].pTableInfo = (IMP_TABLE_INFO*)WsbAlloc(memSize);
|
|||
|
WsbAffirm(s_info[m_SessionIndex].pTableInfo, E_OUTOFMEMORY);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Open: new pTableInfo = %lx\n"),
|
|||
|
s_info[m_SessionIndex].pTableInfo);
|
|||
|
}
|
|||
|
ZeroMemory(s_info[m_SessionIndex].pTableInfo, memSize);
|
|||
|
|
|||
|
pDbInfo->OpenCount++;
|
|||
|
|
|||
|
} WsbCatchAndDo(hr,
|
|||
|
WsbLogEvent(WSB_MESSAGE_IDB_OPEN_FAILED, 0, NULL,
|
|||
|
WsbAbbreviatePath(m_path, 120), NULL);
|
|||
|
)
|
|||
|
CATCH_ANY_EXCEPTION
|
|||
|
|
|||
|
if (pDbInfo) {
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::Open"), OLESTR("hr =<%ls>"),
|
|||
|
WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::Close(
|
|||
|
IN IWsbDbSession* pSession
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDb::Close
|
|||
|
- The element was added.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::Close"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
Lock();
|
|||
|
|
|||
|
WsbAffirm(pDbInfo->OpenCount, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirmHr(session_current_index(pSession));
|
|||
|
|
|||
|
pDbInfo->OpenCount--;
|
|||
|
|
|||
|
JET_ERR jstat;
|
|||
|
IMP_SESSION_INFO* s_info = pDbInfo->SessionInfo;
|
|||
|
|
|||
|
WsbTrace(OLESTR("CWsbDb::Close: closing DB, SessionId = %lx, DbId = %lx\n"),
|
|||
|
(LONG)s_info[m_SessionIndex].SessionId, (LONG)s_info[m_SessionIndex].DbId);
|
|||
|
jstat = JetCloseDatabase(s_info[m_SessionIndex].SessionId,
|
|||
|
s_info[m_SessionIndex].DbId, 0);
|
|||
|
WsbTrace(OLESTR("CWsbDb::Close: JetCloseDatabase = %ld\n"),
|
|||
|
(LONG)jstat);
|
|||
|
if (s_info[m_SessionIndex].pTableInfo) {
|
|||
|
WsbTrace(OLESTR("CWsbDb::Close: releasing pTableInfo\n"));
|
|||
|
WsbFree(s_info[m_SessionIndex].pTableInfo);
|
|||
|
s_info[m_SessionIndex].pTableInfo = NULL;
|
|||
|
}
|
|||
|
s_info[m_SessionIndex].SessionId = 0;
|
|||
|
s_info[m_SessionIndex].DbId = 0;
|
|||
|
|
|||
|
} WsbCatch(hr)
|
|||
|
CATCH_ANY_EXCEPTION
|
|||
|
|
|||
|
if (pDbInfo) {
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::Close"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::GetEntity(
|
|||
|
IN IWsbDbSession* pSession,
|
|||
|
IN ULONG RecId,
|
|||
|
IN REFIID riid,
|
|||
|
OUT void** ppEntity
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDb::GetEntity
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
CComPtr<IWsbDbEntityPriv> pEntity;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::GetEntity"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
CComQIPtr<IWsbDb, &IID_IWsbDb> pIWsbDb = (IWsbDbPriv*)this;
|
|||
|
int rec_index;
|
|||
|
|
|||
|
WsbAssert(0 != ppEntity, E_POINTER);
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
Lock();
|
|||
|
WsbAffirmHr(session_current_index(pSession));
|
|||
|
|
|||
|
// Find the record info
|
|||
|
for (rec_index = 0; rec_index < m_nRecTypes; rec_index++) {
|
|||
|
if (m_RecInfo[rec_index].Type == RecId) break;
|
|||
|
}
|
|||
|
WsbAffirm(rec_index < m_nRecTypes, E_INVALIDARG);
|
|||
|
|
|||
|
// Create the instance, initialize it to point to this DB, and
|
|||
|
// return the pointer to the caller.
|
|||
|
WsbAffirmHr(CoCreateInstance(m_RecInfo[rec_index].EntityClassId, NULL,
|
|||
|
CLSCTX_SERVER | CLSCTX_INPROC_HANDLER,
|
|||
|
IID_IWsbDbEntityPriv, (void**) &pEntity));
|
|||
|
WsbAffirmHr(pEntity->Init(pIWsbDb, m_pWsbDbSys, RecId, JET_CURRENT_SESSION));
|
|||
|
WsbAffirmHr(pEntity->QueryInterface(riid, (void**)ppEntity));
|
|||
|
|
|||
|
} WsbCatch(hr)
|
|||
|
CATCH_ANY_EXCEPTION
|
|||
|
|
|||
|
if (pDbInfo) {
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::GetEntity"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::GetKeyInfo(
|
|||
|
IN ULONG RecType,
|
|||
|
IN USHORT nKeys,
|
|||
|
OUT COM_IDB_KEY_INFO* pKeyInfo
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDbPriv::GetKeyInfo
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = E_FAIL;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::GetKeyInfo"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
|
|||
|
WsbAssert(0 < nKeys, E_INVALIDARG);
|
|||
|
WsbAssert(0 != pKeyInfo, E_POINTER);
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
for (int i = 0; i < m_nRecTypes; i++) {
|
|||
|
if (m_RecInfo[i].Type == RecType) {
|
|||
|
USHORT n = min(nKeys, m_RecInfo[i].nKeys);
|
|||
|
|
|||
|
for (int j = 0; j < n; j++) {
|
|||
|
pKeyInfo[j].Type = m_RecInfo[i].Key[j].Type;
|
|||
|
pKeyInfo[j].Size = m_RecInfo[i].Key[j].Size;
|
|||
|
pKeyInfo[j].Flags = m_RecInfo[i].Key[j].Flags;
|
|||
|
}
|
|||
|
hr = S_OK;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::GetKeyInfo"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::GetRecInfo(
|
|||
|
IN ULONG RecType,
|
|||
|
OUT COM_IDB_REC_INFO* pRecInfo
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IWsbDbPriv::GetRecInfo
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = E_FAIL;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::GetRecInfo"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
|
|||
|
WsbAssert(0 != pRecInfo, E_POINTER);
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
for (int i = 0; i < m_nRecTypes; i++) {
|
|||
|
if (m_RecInfo[i].Type == RecType) {
|
|||
|
pRecInfo->Type = m_RecInfo[i].Type;
|
|||
|
pRecInfo->EntityClassId = m_RecInfo[i].EntityClassId;
|
|||
|
pRecInfo->Flags = m_RecInfo[i].Flags;
|
|||
|
pRecInfo->MinSize = m_RecInfo[i].MinSize;
|
|||
|
pRecInfo->MaxSize = m_RecInfo[i].MaxSize;
|
|||
|
pRecInfo->nKeys = m_RecInfo[i].nKeys;
|
|||
|
hr = S_OK;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::GetRecInfo"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// GetJetIds - for a given record type, return the session ID,
|
|||
|
// the table ID, and the data column ID
|
|||
|
HRESULT CWsbDb::GetJetIds(JET_SESID SessionId, ULONG RecType,
|
|||
|
JET_TABLEID* pTableId, ULONG* pDataColId)
|
|||
|
{
|
|||
|
HRESULT hr = WSB_E_INVALID_DATA;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::GetJetIds"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
JET_DBID DbId = 0;
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
|
|||
|
WsbAssert(0 != pTableId || 0 != pDataColId, E_POINTER);
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
WsbTrace(OLESTR("CWsbDb::GetJetIds: this = %p, pDbInfo = %p\n"),
|
|||
|
this, pDbInfo);
|
|||
|
|
|||
|
for (int index = 0; index < pDbInfo->nSessions; index++) {
|
|||
|
if (pDbInfo->SessionInfo[index].SessionId == SessionId) {
|
|||
|
DbId = pDbInfo->SessionInfo[index].DbId;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
WsbTrace(OLESTR("CWsbDb::GetJetIds: index = %d, DbId = %ld\n"), index, (LONG)DbId);
|
|||
|
WsbAffirm(index < pDbInfo->nSessions, E_FAIL);
|
|||
|
WsbAffirm(pDbInfo->SessionInfo[index].pTableInfo, WSB_E_NOT_INITIALIZED);
|
|||
|
for (int i = 0; i < m_nRecTypes; i++) {
|
|||
|
if (m_RecInfo[i].Type == RecType) {
|
|||
|
JET_ERR jstat;
|
|||
|
char table_name[20];
|
|||
|
IMP_TABLE_INFO* t_info = pDbInfo->SessionInfo[index].pTableInfo;
|
|||
|
|
|||
|
WsbAffirmHr(jet_make_table_name(m_RecInfo[i].Type,
|
|||
|
table_name, 20));
|
|||
|
WsbTrace(OLESTR("CWsbDb::GetJetIds: t_info = %p, i = %d, table_name = <%hs>\n"),
|
|||
|
t_info, i, table_name);
|
|||
|
if (0 == t_info[i].TableId && 0 == t_info[i].ColId) {
|
|||
|
|
|||
|
// Open the table for this record type
|
|||
|
WsbTrace(OLESTR("CWsbDb::GetJetIds: opening Jet table, SessionId = %lx, DbId = %ld, table_name = <%hs>, &TableId = %p\n"),
|
|||
|
(LONG)SessionId, (LONG)DbId, table_name, (&t_info[i].TableId));
|
|||
|
jstat = JetOpenTable(SessionId, DbId, table_name,
|
|||
|
NULL, 0, 0, &t_info[i].TableId);
|
|||
|
WsbTrace(OLESTR("CWsbDb::GetJetIds: TableId = %ld\n"),
|
|||
|
t_info[i].TableId);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
|
|||
|
// Get the column ID for the data column
|
|||
|
WsbAffirmHr(jet_get_column_id(SessionId, DbId, table_name,
|
|||
|
JET_DATA_COLUMN_NAME, &t_info[i].ColId));
|
|||
|
}
|
|||
|
|
|||
|
if (0 != pTableId) {
|
|||
|
*pTableId = t_info[i].TableId;
|
|||
|
}
|
|||
|
if (0 != pDataColId) {
|
|||
|
*pDataColId = t_info[i].ColId;
|
|||
|
}
|
|||
|
hr = S_OK;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::GetJetIds"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// GetJetIndexInfo - for a given record type and key type, return information
|
|||
|
// about the key/index: the key size (in bytes), the column ID,
|
|||
|
// the index name
|
|||
|
HRESULT CWsbDb::GetJetIndexInfo(JET_SESID SessionId, ULONG RecType, ULONG KeyType,
|
|||
|
ULONG* pColId, OLECHAR** pName, ULONG bufferSize)
|
|||
|
{
|
|||
|
HRESULT hr = WSB_E_INVALID_DATA;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::GetJetIndexInfo"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
JET_DBID DbId = 0;
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
|
|||
|
WsbAssert(0 != pColId || 0 != pName, E_POINTER);
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
|
|||
|
for (int index = 0; index < pDbInfo->nSessions; index++) {
|
|||
|
if (pDbInfo->SessionInfo[index].SessionId == SessionId) {
|
|||
|
DbId = pDbInfo->SessionInfo[index].DbId;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
WsbAffirm(index < pDbInfo->nSessions, E_FAIL);
|
|||
|
for (int i = 0; i < m_nRecTypes; i++) {
|
|||
|
if (m_RecInfo[i].Type == RecType) {
|
|||
|
char index_name[20];
|
|||
|
char table_name[20];
|
|||
|
|
|||
|
WsbAffirmHr(jet_make_table_name(RecType, table_name, 20));
|
|||
|
|
|||
|
if (0 == KeyType) {
|
|||
|
if (0 != pName) {
|
|||
|
WsbAffirm(bufferSize > strlen(JET_SEQNUM_COLUMN_NAME), E_FAIL);
|
|||
|
WsbAffirm(0 < mbstowcs(*pName, JET_SEQNUM_COLUMN_NAME,
|
|||
|
strlen(JET_SEQNUM_COLUMN_NAME) + 1), E_FAIL);
|
|||
|
}
|
|||
|
if (0 != pColId) {
|
|||
|
WsbAffirmHr(jet_get_column_id(SessionId, DbId, table_name,
|
|||
|
JET_SEQNUM_COLUMN_NAME, pColId));
|
|||
|
}
|
|||
|
hr = S_OK;
|
|||
|
} else {
|
|||
|
// Search for the given key type
|
|||
|
for (int j = 0; j < m_RecInfo[i].nKeys; j++) {
|
|||
|
if (m_RecInfo[i].Key[j].Type == KeyType) {
|
|||
|
WsbAffirmHr(jet_make_index_name(KeyType, index_name, 20));
|
|||
|
if (0 != pName) {
|
|||
|
WsbAffirm(bufferSize > strlen(index_name), E_FAIL);
|
|||
|
WsbAffirm(0 < mbstowcs(*pName, index_name,
|
|||
|
strlen(index_name) + 1), E_FAIL);
|
|||
|
}
|
|||
|
if (0 != pColId) {
|
|||
|
WsbAffirmHr(jet_get_column_id(SessionId, DbId, table_name,
|
|||
|
index_name, pColId));
|
|||
|
}
|
|||
|
hr = S_OK;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::GetJetIndexInfo"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::FinalConstruct(
|
|||
|
void
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
CComObjectRoot::FinalConstruct
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::FinalConstruct"), OLESTR("") );
|
|||
|
|
|||
|
try {
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
|
|||
|
WsbAffirmHr(CWsbPersistable::FinalConstruct());
|
|||
|
m_nRecTypes = 0;
|
|||
|
m_version = 0;
|
|||
|
m_pImp = NULL;
|
|||
|
m_RecInfo = NULL;
|
|||
|
|
|||
|
m_SessionIndex = 0;
|
|||
|
|
|||
|
// Allocate space for DB info & set
|
|||
|
pDbInfo = (IMP_DB_INFO*)WsbAlloc(sizeof(IMP_DB_INFO));
|
|||
|
m_pImp = pDbInfo;
|
|||
|
WsbAffirm(pDbInfo, E_OUTOFMEMORY);
|
|||
|
ZeroMemory(pDbInfo, sizeof(IMP_DB_INFO));
|
|||
|
pDbInfo->IsLoaded = FALSE;
|
|||
|
pDbInfo->OpenCount = 0;
|
|||
|
|
|||
|
pDbInfo->nSessions = 0;
|
|||
|
pDbInfo->SessionInfo = NULL;
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::FinalConstruct"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
CWsbDb::FinalRelease(
|
|||
|
void
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
CComObjectRoot::FinalRelease
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::FinalRelease"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
if (m_pImp) {
|
|||
|
int i;
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
|
|||
|
if (pDbInfo->RecInfo) {
|
|||
|
for (i = 0; i < m_nRecTypes; i++) {
|
|||
|
|
|||
|
if (pDbInfo->RecInfo[i].Key) {
|
|||
|
WsbFree(pDbInfo->RecInfo[i].Key);
|
|||
|
}
|
|||
|
}
|
|||
|
WsbFree(pDbInfo->RecInfo);
|
|||
|
}
|
|||
|
|
|||
|
// Make sure Jet resources are released
|
|||
|
if (NULL != pDbInfo->SessionInfo) {
|
|||
|
IMP_SESSION_INFO* s_info = pDbInfo->SessionInfo;
|
|||
|
|
|||
|
for (i = 0; i < pDbInfo->nSessions; i++) {
|
|||
|
if (0 != s_info[i].SessionId && 0 != s_info[i].DbId) {
|
|||
|
JET_ERR jstat;
|
|||
|
|
|||
|
jstat = JetCloseDatabase(s_info[i].SessionId,
|
|||
|
s_info[i].DbId, 0);
|
|||
|
WsbTrace(OLESTR("CWsbDb::FinalRelease: JetCloseDatabase[%d] = %ld\n"),
|
|||
|
i, (LONG)jstat);
|
|||
|
}
|
|||
|
if (s_info[i].pTableInfo) {
|
|||
|
WsbFree(s_info[i].pTableInfo);
|
|||
|
s_info[i].pTableInfo = NULL;
|
|||
|
}
|
|||
|
s_info[i].SessionId = 0;
|
|||
|
s_info[i].DbId = 0;
|
|||
|
}
|
|||
|
|
|||
|
WsbFree(pDbInfo->SessionInfo);
|
|||
|
pDbInfo->SessionInfo = NULL;
|
|||
|
}
|
|||
|
pDbInfo->nSessions = 0;
|
|||
|
|
|||
|
WsbFree(pDbInfo);
|
|||
|
m_pImp = NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (m_RecInfo) {
|
|||
|
for (int i = 0; i < m_nRecTypes; i++) {
|
|||
|
if (m_RecInfo[i].Key) {
|
|||
|
WsbFree(m_RecInfo[i].Key);
|
|||
|
}
|
|||
|
}
|
|||
|
WsbFree(m_RecInfo);
|
|||
|
m_RecInfo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
CWsbPersistable::FinalRelease();
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::FinalRelease"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::GetClassID(
|
|||
|
OUT CLSID* pClsid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IPersist::GetClassID().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::GetClassID"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
WsbAssert(0 != pClsid, E_POINTER);
|
|||
|
*pClsid = CLSID_CWsbDb;
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::GetClassID"), OLESTR("hr = <%ls>, CLSID = <%ls>"), WsbHrAsString(hr), WsbGuidAsString(*pClsid));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::Load(
|
|||
|
IN IStream* pStream
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IPersistStream::Load().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
OLECHAR* name = NULL;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::Load"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
ULONG Bytes;
|
|||
|
ULONG len = 0;
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
FILE_DB_INFO db_file_block; // Used to move info to/from file
|
|||
|
CComQIPtr<IWsbDb, &IID_IWsbDb> pIWsbDb = (IWsbDbPriv*)this;
|
|||
|
|
|||
|
WsbAssert(0 != pStream, E_POINTER);
|
|||
|
WsbAssert(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
|
|||
|
// Don't allow loading into an already open DB
|
|||
|
WsbAffirm(pDbInfo->OpenCount == 0, WSB_E_INVALID_DATA);
|
|||
|
|
|||
|
// Read the DB file name
|
|||
|
WsbAffirmHr(WsbLoadFromStream(pStream, &name, 0));
|
|||
|
if (name) {
|
|||
|
len = wcslen(name);
|
|||
|
}
|
|||
|
|
|||
|
// If the DB name is empty, there is no more info
|
|||
|
if (0 < len) {
|
|||
|
// Alloc space and read DB info
|
|||
|
WsbAffirmHr(pStream->Read((void*)&db_file_block, sizeof(FILE_DB_INFO), &Bytes));
|
|||
|
WsbAffirm(Bytes == sizeof(FILE_DB_INFO), WSB_E_STREAM_ERROR);
|
|||
|
|
|||
|
// Check DB version for match
|
|||
|
WsbAffirm(db_file_block.version == m_version, WSB_E_IDB_WRONG_VERSION);
|
|||
|
|
|||
|
// Locate the DB
|
|||
|
WsbAffirmHr(db_info_from_file_block(&db_file_block));
|
|||
|
hr = Locate(name);
|
|||
|
if (S_OK != hr) {
|
|||
|
if (pDbInfo->RecInfo) {
|
|||
|
WsbFree(pDbInfo->RecInfo);
|
|||
|
pDbInfo->RecInfo = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
hr = S_FALSE;
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbFree(name);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::Load"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
HRESULT
|
|||
|
CWsbDb::Save(
|
|||
|
IN IStream* pStream,
|
|||
|
IN BOOL clearDirty
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Implements:
|
|||
|
|
|||
|
IPersistStream::Save().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::Save"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
ULONG Bytes;
|
|||
|
ULONG len = 0;
|
|||
|
FILE_DB_INFO db_file_block; // Used to move info to/from file
|
|||
|
|
|||
|
WsbAssert(0 != pStream, E_POINTER);
|
|||
|
WsbAssert(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
|
|||
|
// Save the DB name
|
|||
|
WsbAffirmHr(WsbSaveToStream(pStream, m_path));
|
|||
|
WsbAffirmHr(m_path.GetLen(&len));
|
|||
|
|
|||
|
// If the name is empty, none of the other information is likely
|
|||
|
// to be useful
|
|||
|
if (0 < len) {
|
|||
|
// Write some DB info
|
|||
|
WsbAffirm(m_nRecTypes, WSB_E_NOT_INITIALIZED);
|
|||
|
WsbAffirmHr(db_info_to_file_block(&db_file_block));
|
|||
|
WsbAffirmHr(pStream->Write((void*)&db_file_block, sizeof(FILE_DB_INFO), &Bytes));
|
|||
|
WsbAffirm(Bytes == sizeof(FILE_DB_INFO), WSB_E_STREAM_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
if (clearDirty) {
|
|||
|
SetIsDirty(FALSE);
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::Save"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Private functions
|
|||
|
//
|
|||
|
|
|||
|
// db_info_from_file_block - copy data from DB info file block
|
|||
|
HRESULT
|
|||
|
CWsbDb::db_info_from_file_block(void* block)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
try {
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
ULONG Size;
|
|||
|
FILE_DB_INFO *pDbFileBlock = (FILE_DB_INFO*)block;
|
|||
|
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
|
|||
|
WsbAssert(0 != pDbFileBlock, E_POINTER);
|
|||
|
m_version = pDbFileBlock->version;
|
|||
|
m_nRecTypes = pDbFileBlock->nRecTypes;
|
|||
|
|
|||
|
// Allocate record arrays
|
|||
|
if (NULL == m_RecInfo) {
|
|||
|
Size = m_nRecTypes * sizeof(IDB_REC_INFO);
|
|||
|
m_RecInfo = (IDB_REC_INFO*)WsbAlloc(Size);
|
|||
|
WsbAffirm(m_RecInfo, E_OUTOFMEMORY);
|
|||
|
ZeroMemory(m_RecInfo, Size);
|
|||
|
}
|
|||
|
if (NULL == pDbInfo->RecInfo) {
|
|||
|
Size = m_nRecTypes * sizeof(IMP_REC_INFO);
|
|||
|
pDbInfo->RecInfo = (IMP_REC_INFO*)WsbAlloc(Size);
|
|||
|
WsbAffirm(pDbInfo->RecInfo, E_OUTOFMEMORY);
|
|||
|
ZeroMemory(pDbInfo->RecInfo, Size);
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// db_info_to_file_block - copy data to DB info file block
|
|||
|
HRESULT
|
|||
|
CWsbDb::db_info_to_file_block(void* block)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
try {
|
|||
|
FILE_DB_INFO *pDbFileBlock = (FILE_DB_INFO*)block;
|
|||
|
|
|||
|
WsbAssert (0 != pDbFileBlock, E_POINTER);
|
|||
|
pDbFileBlock->version = m_version;
|
|||
|
pDbFileBlock->nRecTypes = m_nRecTypes;
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
return hr;
|
|||
|
}
|
|||
|
|
|||
|
// rec_info_from_file_block - copy record data from rec info file block
|
|||
|
HRESULT
|
|||
|
CWsbDb::rec_info_from_file_block(int index, void* block)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
try {
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
ULONG Size;
|
|||
|
FILE_REC_INFO *pRecFileBlock = (FILE_REC_INFO*)block;
|
|||
|
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
|
|||
|
WsbAssert (0 != pRecFileBlock, E_POINTER);
|
|||
|
m_RecInfo[index].Type = pRecFileBlock->Type;
|
|||
|
m_RecInfo[index].EntityClassId = pRecFileBlock->EntityClassId;
|
|||
|
m_RecInfo[index].Flags = pRecFileBlock->Flags;
|
|||
|
m_RecInfo[index].MinSize = pRecFileBlock->MinSize;
|
|||
|
m_RecInfo[index].MaxSize = pRecFileBlock->MaxSize;
|
|||
|
m_RecInfo[index].nKeys = pRecFileBlock->nKeys;
|
|||
|
|
|||
|
// Allocate Key arrays
|
|||
|
if (NULL == m_RecInfo[index].Key) {
|
|||
|
Size = m_RecInfo[index].nKeys * sizeof(IDB_KEY_INFO);
|
|||
|
m_RecInfo[index].Key = (IDB_KEY_INFO*)WsbAlloc(Size);
|
|||
|
WsbAffirm(m_RecInfo[index].Key, E_OUTOFMEMORY);
|
|||
|
ZeroMemory(m_RecInfo[index].Key, Size);
|
|||
|
}
|
|||
|
if (NULL == pDbInfo->RecInfo[index].Key) {
|
|||
|
Size = m_RecInfo[index].nKeys * sizeof(IMP_KEY_INFO);
|
|||
|
pDbInfo->RecInfo[index].Key = (IMP_KEY_INFO*)WsbAlloc(Size);
|
|||
|
WsbAffirm(pDbInfo->RecInfo[index].Key, E_OUTOFMEMORY);
|
|||
|
ZeroMemory(pDbInfo->RecInfo[index].Key, Size);
|
|||
|
}
|
|||
|
|
|||
|
for (int j = 0; j < pRecFileBlock->nKeys; j++) {
|
|||
|
m_RecInfo[index].Key[j].Type = pRecFileBlock->Key[j].Type;
|
|||
|
m_RecInfo[index].Key[j].Size = pRecFileBlock->Key[j].Size;
|
|||
|
m_RecInfo[index].Key[j].Flags = pRecFileBlock->Key[j].Flags;
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// rec_info_to_file_block - copy record data to rec info file block
|
|||
|
HRESULT
|
|||
|
CWsbDb::rec_info_to_file_block(int index, void* block)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
try {
|
|||
|
FILE_REC_INFO *pRecFileBlock = (FILE_REC_INFO*)block;
|
|||
|
|
|||
|
WsbAssert (0 != pRecFileBlock, E_POINTER);
|
|||
|
|
|||
|
// pRecFileBlock->SeqNum = m_RecInfo[index].SeqNum;
|
|||
|
pRecFileBlock->Type = m_RecInfo[index].Type;
|
|||
|
pRecFileBlock->EntityClassId = m_RecInfo[index].EntityClassId;
|
|||
|
pRecFileBlock->Flags = m_RecInfo[index].Flags;
|
|||
|
pRecFileBlock->MinSize = m_RecInfo[index].MinSize;
|
|||
|
pRecFileBlock->MaxSize = m_RecInfo[index].MaxSize;
|
|||
|
pRecFileBlock->nKeys = m_RecInfo[index].nKeys;
|
|||
|
for (int j = 0; j < pRecFileBlock->nKeys; j++) {
|
|||
|
pRecFileBlock->Key[j].Type = m_RecInfo[index].Key[j].Type;
|
|||
|
pRecFileBlock->Key[j].Size = m_RecInfo[index].Key[j].Size;
|
|||
|
pRecFileBlock->Key[j].Flags = m_RecInfo[index].Key[j].Flags;
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
return hr;
|
|||
|
}
|
|||
|
|
|||
|
// session_current_index - find the index into the session info array.
|
|||
|
// Sets m_SessionIndex if it's OK
|
|||
|
HRESULT
|
|||
|
CWsbDb::session_current_index(IWsbDbSession* pSession)
|
|||
|
{
|
|||
|
HRESULT hr = WSB_E_INVALID_DATA;
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = pSession;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDB::session_current_index"), OLESTR(""));
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
if (NULL != pDbInfo && NULL != pDbInfo->SessionInfo && pSessionPriv) {
|
|||
|
JET_SESID SessionId;
|
|||
|
|
|||
|
if (S_OK == pSessionPriv->GetJetId(&SessionId)) {
|
|||
|
for (int index = 0; index < pDbInfo->nSessions; index++) {
|
|||
|
if (pDbInfo->SessionInfo[index].SessionId == SessionId) {
|
|||
|
hr = S_OK;
|
|||
|
m_SessionIndex = index;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDB::session_current_index"), OLESTR("sessionID[%ld] = %lx"),
|
|||
|
m_SessionIndex, pDbInfo->SessionInfo[m_SessionIndex].SessionId);
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// jet_init - make sure this IDB object is initialized for JET
|
|||
|
HRESULT
|
|||
|
CWsbDb::jet_init(void)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::jet_init"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
IMP_DB_INFO* pDbInfo;
|
|||
|
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
WsbTrace(OLESTR("CWsbDB::jet_init, nSessions = %d\n"),
|
|||
|
(int)pDbInfo->nSessions);
|
|||
|
if (0 == pDbInfo->nSessions) {
|
|||
|
ULONG memSize;
|
|||
|
|
|||
|
// Allocate the thread info array
|
|||
|
WsbAffirm(m_pWsbDbSys, E_FAIL);
|
|||
|
memSize = SESSION_INFO_INITIAL_SIZE * sizeof(IMP_SESSION_INFO);
|
|||
|
pDbInfo->SessionInfo = (IMP_SESSION_INFO*)WsbAlloc(memSize);
|
|||
|
WsbAffirm(pDbInfo->SessionInfo, E_OUTOFMEMORY);
|
|||
|
pDbInfo->nSessions = SESSION_INFO_INITIAL_SIZE;
|
|||
|
ZeroMemory(pDbInfo->SessionInfo, memSize);
|
|||
|
WsbTrace(OLESTR("CWsbDB::jet_init, SessionInfo(%ld bytes) allocated & zeroed\n"),
|
|||
|
memSize);
|
|||
|
|
|||
|
// Begin a JET session for the IDB
|
|||
|
m_SessionIndex = 0;
|
|||
|
|
|||
|
JET_SESID sid;
|
|||
|
CComPtr<IWsbDbSession> pSession;
|
|||
|
WsbAffirmHr(m_pWsbDbSys->GetGlobalSession(&pSession));
|
|||
|
|
|||
|
CComQIPtr<IWsbDbSessionPriv, &IID_IWsbDbSessionPriv> pSessionPriv = pSession;
|
|||
|
WsbAffirmPointer(pSessionPriv);
|
|||
|
WsbAffirmHr(pSessionPriv->GetJetId(&sid));
|
|||
|
pDbInfo->SessionInfo[m_SessionIndex].SessionId = sid;
|
|||
|
WsbTrace(OLESTR("CWsbDB::jet_init, SessionId[0] = %lx\n"),
|
|||
|
pDbInfo->SessionInfo[m_SessionIndex].SessionId);
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::jet_init"), OLESTR("hr =<%ls>"), WsbHrAsString(hr));
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// jet_load_info - load DB info from database
|
|||
|
HRESULT
|
|||
|
CWsbDb::jet_load_info(void)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
JET_TABLEID table_id = 0;
|
|||
|
JET_ERR jstat;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("CWsbDb::jet_load_info"), OLESTR(""));
|
|||
|
|
|||
|
try {
|
|||
|
JET_COLUMNID col_id_data;
|
|||
|
ULONG size;
|
|||
|
FILE_DB_INFO db_file_block; // Used to move info to/from file
|
|||
|
FILE_REC_INFO rec_file_block; // Used to move record info
|
|||
|
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
|
|||
|
// Open the info table
|
|||
|
jstat = JetOpenTable(JET_CURRENT_SESSION, JET_CURRENT_DB, JET_INFO_TABLE_NAME,
|
|||
|
NULL, 0, 0, &table_id);
|
|||
|
if (jstat != JET_errSuccess) {
|
|||
|
table_id = 0;
|
|||
|
}
|
|||
|
WsbTrace(OLESTR("CWsbDb::jet_load_info: open TableId = %ld\n"),
|
|||
|
table_id);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
WsbAffirmHr(jet_get_column_id(JET_CURRENT_SESSION, JET_CURRENT_DB, JET_INFO_TABLE_NAME,
|
|||
|
JET_DATA_COLUMN_NAME, &col_id_data));
|
|||
|
jstat = JetSetCurrentIndex(JET_CURRENT_SESSION, table_id, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
|
|||
|
// Get the DB info and check for match
|
|||
|
jstat = JetRetrieveColumn(JET_CURRENT_SESSION, table_id, col_id_data,
|
|||
|
&db_file_block, sizeof(FILE_DB_INFO), &size, 0, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
WsbAffirm(db_file_block.nRecTypes > 0, WSB_E_INVALID_DATA);
|
|||
|
WsbAffirm(db_file_block.version == m_version, WSB_E_IDB_WRONG_VERSION);
|
|||
|
WsbAffirmHr(db_info_from_file_block(&db_file_block));
|
|||
|
|
|||
|
// Get the record/key info
|
|||
|
for (int i = 0; i < m_nRecTypes; i++) {
|
|||
|
jstat = JetMove(JET_CURRENT_SESSION, table_id, JET_MoveNext, 0);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
jstat = JetRetrieveColumn(JET_CURRENT_SESSION, table_id, col_id_data,
|
|||
|
&rec_file_block, sizeof(FILE_REC_INFO), &size, 0, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
WsbAffirmHr(rec_info_from_file_block(i, &rec_file_block));
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
if (table_id) {
|
|||
|
jstat = JetCloseTable(JET_CURRENT_SESSION, table_id);
|
|||
|
WsbTrace(OLESTR("CWsbDb::jet_load_info: close TableId = %ld, jstat = %ld\n"),
|
|||
|
table_id, jstat);
|
|||
|
}
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("CWsbDb::jet_load_info"), OLESTR("hr =<%ls>"),
|
|||
|
WsbHrAsString(hr));
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// jet_make_index_name - convert key type to index name
|
|||
|
HRESULT
|
|||
|
CWsbDb::jet_make_index_name(ULONG key_type, char* pName, ULONG bufsize)
|
|||
|
{
|
|||
|
HRESULT hr = E_FAIL;
|
|||
|
char lbuf[20];
|
|||
|
|
|||
|
if (pName != NULL) {
|
|||
|
sprintf(lbuf, "%ld", key_type);
|
|||
|
if (bufsize > strlen(lbuf)) {
|
|||
|
strcpy(pName, lbuf);
|
|||
|
hr = S_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// jet_make_table_name - convert record type to table name
|
|||
|
HRESULT
|
|||
|
CWsbDb::jet_make_table_name(ULONG rec_type, char* pName, ULONG bufsize)
|
|||
|
{
|
|||
|
HRESULT hr = E_FAIL;
|
|||
|
char lbuf[20];
|
|||
|
|
|||
|
if (pName != NULL) {
|
|||
|
sprintf(lbuf, "%ld", rec_type);
|
|||
|
if (bufsize > strlen(lbuf)) {
|
|||
|
strcpy(pName, lbuf);
|
|||
|
hr = S_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// jet_save_info - save DB info to database
|
|||
|
HRESULT
|
|||
|
CWsbDb::jet_save_info()
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
IMP_DB_INFO* pDbInfo = NULL;
|
|||
|
JET_TABLEID table_id = 0;
|
|||
|
JET_ERR jstat;
|
|||
|
|
|||
|
try {
|
|||
|
JET_COLUMNID col_id_data;
|
|||
|
JET_COLUMNID col_id_index;
|
|||
|
SHORT data_number;
|
|||
|
FILE_DB_INFO db_file_block; // Used to move info to/from file
|
|||
|
FILE_REC_INFO rec_file_block; // Used to move record info
|
|||
|
|
|||
|
WsbAffirm(m_pImp, WSB_E_NOT_INITIALIZED);
|
|||
|
pDbInfo = (IMP_DB_INFO*)m_pImp;
|
|||
|
|
|||
|
// Open the table
|
|||
|
jstat = JetOpenTable(JET_CURRENT_SESSION, JET_CURRENT_DB, JET_INFO_TABLE_NAME,
|
|||
|
NULL, 0, 0, &table_id);
|
|||
|
WsbTrace(OLESTR("CWsbDb::jet_save_info: open TableId = %ld\n"),
|
|||
|
table_id);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
WsbAffirmHr(jet_get_column_id(JET_CURRENT_SESSION, JET_CURRENT_DB, JET_INFO_TABLE_NAME,
|
|||
|
JET_INDEX_COLUMN_NAME, &col_id_index));
|
|||
|
WsbAffirmHr(jet_get_column_id(JET_CURRENT_SESSION, JET_CURRENT_DB, JET_INFO_TABLE_NAME,
|
|||
|
JET_DATA_COLUMN_NAME, &col_id_data));
|
|||
|
|
|||
|
// Put the DB info
|
|||
|
jstat = JetPrepareUpdate(JET_CURRENT_SESSION, table_id, JET_prepInsert);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
WsbAffirmHr(db_info_to_file_block(&db_file_block));
|
|||
|
data_number = 0;
|
|||
|
jstat = JetSetColumn(JET_CURRENT_SESSION, table_id, col_id_index, &data_number,
|
|||
|
sizeof(data_number), 0, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
jstat = JetSetColumn(JET_CURRENT_SESSION, table_id, col_id_data, &db_file_block,
|
|||
|
sizeof(FILE_DB_INFO), 0, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
jstat = JetUpdate(JET_CURRENT_SESSION, table_id, NULL, 0, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
|
|||
|
// Put the record/key info
|
|||
|
for (int i = 0; i < m_nRecTypes; i++) {
|
|||
|
jstat = JetPrepareUpdate(JET_CURRENT_SESSION, table_id, JET_prepInsert);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
WsbAffirmHr(rec_info_to_file_block(i, &rec_file_block));
|
|||
|
data_number = (SHORT) ( i + 1 );
|
|||
|
jstat = JetSetColumn(JET_CURRENT_SESSION, table_id, col_id_index, &data_number,
|
|||
|
sizeof(data_number), 0, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
jstat = JetSetColumn(JET_CURRENT_SESSION, table_id, col_id_data, &rec_file_block,
|
|||
|
sizeof(FILE_REC_INFO), 0, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
jstat = JetUpdate(JET_CURRENT_SESSION, table_id, NULL, 0, NULL);
|
|||
|
WsbAffirmHr(jet_error(jstat));
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
if (table_id) {
|
|||
|
jstat = JetCloseTable(JET_CURRENT_SESSION, table_id);
|
|||
|
WsbTrace(OLESTR("CWsbDb::jet_save_info: close TableId = %ld, jstat = %ld\n"),
|
|||
|
table_id, jstat);
|
|||
|
}
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// Local functions
|
|||
|
|
|||
|
// jet_get_column_id - convert a column name to a column ID
|
|||
|
static HRESULT jet_get_column_id(JET_SESID jet_session, JET_DBID DbId,
|
|||
|
char* pTableName, char* pColumnName, JET_COLUMNID* pColId)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
WsbTraceIn(OLESTR("jet_get_column_id"), OLESTR("SessId = %lx, DbId = %lx"),
|
|||
|
(ULONG)jet_session, (ULONG)DbId);
|
|||
|
|
|||
|
try {
|
|||
|
JET_COLUMNBASE col_base;
|
|||
|
JET_ERR jstat;
|
|||
|
|
|||
|
WsbAssert(NULL != pColId, E_POINTER);
|
|||
|
jstat = JetGetColumnInfo(jet_session, DbId, pTableName, pColumnName,
|
|||
|
&col_base, sizeof(col_base), JET_ColInfoBase);
|
|||
|
WsbAssertHr(jet_error(jstat));
|
|||
|
*pColId = col_base.columnid;
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
WsbTraceOut(OLESTR("jet_get_column_id"), OLESTR("col_id = %ld"),
|
|||
|
(LONG)*pColId);
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|