2025-11-28 00:35:46 +09:00

735 lines
16 KiB
C++

//--------------------------------------------------------------------
// Microsoft OLE DB Sample OLEDB Simple Provider
// (C) Copyright 1991 - 1999 Microsoft Corporation. All Rights Reserved.
//
// module MyOSPObject.cpp | MyOSPObject (OLEDBSimpleProvider) object implementation
//
//
////////////////////////////////////////////////////////
// Includes
//
////////////////////////////////////////////////////////
#include "Common.h"
#include "MyOSPObject.h"
#include <fstream>
using namespace std;
#include <stdio.h>
#include <tchar.h>
////////////////////////////////////////////////////////
// MyOSPObject
//
////////////////////////////////////////////////////////
MyOSPObject::MyOSPObject()
{
//IUnknown
m_cRef = 0;
//Member variables
m_rvTable = NULL;
m_cRows = 0;
m_cColumns = 0;
m_szFilePath = NULL;
}
MyOSPObject::~MyOSPObject()
{
DBROWCOUNT i, j;
// Save the internal table to the data file:
if (m_szFilePath != NULL)
saveData();
// Walk through the internal table. Free any
// variants, each row, and finally the table.
if (m_rvTable != NULL)
{
for (i = 0; i <= m_cRows; i++)
{
for (j = 1; j <= m_cColumns; j++)
{
if (m_rvTable[i][j].vt == VT_BSTR)
SysFreeString(V_BSTR(&m_rvTable[i][j]));
}
CoTaskMemFree(m_rvTable[i]);
}
CoTaskMemFree(m_rvTable);
}
//Remove all Listeners:
while(!m_listListeners.IsEmpty())
m_listListeners.RemoveHead()->Release();
}
HRESULT MyOSPObject::Init(WCHAR* pwszFilePath)
{
// Store the name of the data file:
m_szFilePath = (LPSTR)CoTaskMemAlloc(FILENAME_MAX);
if (!m_szFilePath)
return E_OUTOFMEMORY;
size_t cbConverted;
wcstombs_s(&cbConverted, m_szFilePath, FILENAME_MAX, pwszFilePath, _TRUNCATE);
// Load the file into a table:
return loadData();
}
HRESULT MyOSPObject::QueryInterface(REFIID riid, void** ppv)
{
if (ppv == NULL)
return E_INVALIDARG;
// IUnknown:
if (riid == IID_IUnknown)
*ppv = this;
// OLEDBSimpleProvider:
else if (riid == IID_OLEDBSimpleProvider)
*ppv = this;
// Unsupported:
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
SAFE_ADDREF((IUnknown*)*ppv);
return S_OK;
}
////////////////////////////////////////////////////////
// Routines to load and save table to file
//
////////////////////////////////////////////////////////
HRESULT MyOSPObject::loadData()
{
HRESULT hr = E_OUTOFMEMORY;
ifstream myOSPFile;
size_t cChar;
BSTR pwszSeps; // separator char in file
BSTR pwszBuffer = NULL; // -> wide character buffer
BSTR pwszRow; // -> wide character buffer (position)
BSTR pwszToken;
LPTSTR pvInput = NULL; // -> input buffer
LPSTR pszPos;
DBROWCOUNT i, j;
// Build the internal array by reading the file:
myOSPFile.open(m_szFilePath, ios::in | ios::_Nocreate );
if (!(myOSPFile.is_open()))
return E_FAIL;
// Line 1 contains cRows ; cColumns:
pvInput = new char[MAX_INPUT_BUFFER ];
CHECK_MEMORY(pvInput);
myOSPFile.getline(pvInput, MAX_INPUT_BUFFER);
pszPos = pvInput;
m_cRows = strtol(pszPos, &pszPos, 10);
pszPos++; // consume delimiter
m_cColumns = strtol(pszPos, &pszPos, 10);
// allocate array of size cRows X cColumns:
m_rvTable = (VARIANT**)CoTaskMemAlloc((m_cRows+1) * sizeof(VARIANT*));
CHECK_MEMORY(m_rvTable);
ZeroMemory(m_rvTable, (m_cRows+1) * sizeof(VARIANT*));
for (i = 0; i <= m_cRows; i++)
{
m_rvTable[i] = (VARIANT*)CoTaskMemAlloc((m_cColumns+1) * sizeof(VARIANT));
CHECK_MEMORY(m_rvTable[i]);
}
// read the rest of the file and stuff each line into the next row:
i = 0;
j = 0;
pwszSeps = L";";
pwszBuffer = (BSTR)CoTaskMemAlloc(MAX_WIDE_BUFFER);
CHECK_MEMORY(pwszBuffer);
do
{
myOSPFile.getline(pvInput, MAX_INPUT_BUFFER);
pwszRow = pwszBuffer;
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pvInput, -1, pwszRow,
MAX_WIDE_BUFFER);
for (j = 1; j <= m_cColumns; j++)
{
if (pwszRow == NULL)
{
m_rvTable[i][j].vt = VT_NULL;
continue;
}
cChar = wcscspn(pwszRow, pwszSeps);
if (cChar)
{
LPWSTR pNextToken;
pwszToken = wcstok_s(pwszRow, pwszSeps, &pNextToken);
m_rvTable[i][j].vt = VT_BSTR;
V_BSTR(&m_rvTable[i][j]) = SysAllocString(pwszToken);
pwszRow += (cChar+1);
}
else
{
m_rvTable[i][j].vt = VT_NULL;
pwszRow += 1;
}
}
i++;
if (i > m_cRows)
break;
}
while (!(myOSPFile.eof()));
hr = S_OK;
CLEANUP:
SAFE_FREE(pwszBuffer);
if ( FAILED(hr) )
{
if (m_rvTable)
{
// Since m_rvTable is valid, m_cRows is valid
for (int i = 0; i <= m_cRows; i++)
{
SAFE_FREE(m_rvTable[i]);
}
SAFE_FREE(m_rvTable);
}
}
if(pvInput)
delete [] pvInput;
myOSPFile.close();
return hr;
}
HRESULT MyOSPObject::saveData()
{
HRESULT hr = S_OK;
ofstream myOSPFile;
int nCnt;
BSTR pbstrCell;
DBROWCOUNT i, j;
LPTSTR pvOutput; // -> output buffer
// Open the file for output:
myOSPFile.open(m_szFilePath, ios::out | ios::_Nocreate);
if (!(myOSPFile.is_open()))
return E_FAIL;
pvOutput = new char[MAX_OUTPUT_BUFFER+1];
if (!pvOutput)
{
hr = E_OUTOFMEMORY;
goto EXIT;
}
// Write the first line containing "rows ; columns":
wsprintf(pvOutput, "%li;%li\n", m_cRows, m_cColumns);
myOSPFile.write(pvOutput, lstrlen(pvOutput));
myOSPFile.flush();
// Write the columns of each row on a line:
for (i = 0; i <= m_cRows && hr == S_OK; i++)
{
nCnt = 0;
for (j = 1; j <= m_cColumns; j++)
{
if (m_rvTable[i][j].vt == VT_NULL)
{
if ((nCnt + lstrlen(";")) >= MAX_OUTPUT_BUFFER)
{
hr = DB_E_DATAOVERFLOW;
break;
}
nCnt += wsprintf(pvOutput+nCnt, ";");
}
else
{
pbstrCell = V_BSTR(&m_rvTable[i][j]);
if ((nCnt + SysStringByteLen(pbstrCell)) >= MAX_OUTPUT_BUFFER)
{
hr = DB_E_DATAOVERFLOW;
break;
}
nCnt += wsprintf(pvOutput+nCnt, "%ws;", pbstrCell);
}
}
if ((nCnt + lstrlen("\n")) >= MAX_OUTPUT_BUFFER)
{
hr = DB_E_DATAOVERFLOW;
break;
}
nCnt += wsprintf(pvOutput+nCnt, "\n");
myOSPFile.write(pvOutput, nCnt);
myOSPFile.flush();
}
EXIT:
// close the file
myOSPFile.close();
delete [] pvOutput;
return hr;
}
////////////////////////////////////////////////////////
// Common notification routine: send event to each
// Listener in the list.
////////////////////////////////////////////////////////
HRESULT MyOSPObject::Notify(EOSPEVENTS eEvent, DBROWCOUNT iRow, DB_LORDINAL iColumn, DBROWCOUNT cRows)
{
//no-op case
if(m_listListeners.GetCount()==0)
return S_OK;
//obtain first element
HRESULT hr = S_OK;
POS pos = m_listListeners.GetHeadPosition();
//otherwise notify for reason/phase
switch(eEvent)
{
//Multi-Cast
case CHANGECELL_ABOUTTODO:
while(pos)
hr = m_listListeners.GetNext(pos)->aboutToChangeCell(iRow, iColumn);
break;
//Multi-Cast
case CHANGECELL_DIDEVENT:
while(pos)
hr = m_listListeners.GetNext(pos)->cellChanged(iRow, iColumn);
break;
//Multi-Cast
case DELETEROWS_ABOUTTODO:
while(pos)
hr = m_listListeners.GetNext(pos)->aboutToDeleteRows(iRow, cRows);
break;
//Multi-Cast
case DELETEROWS_DIDEVENT:
while(pos)
hr = m_listListeners.GetNext(pos)->deletedRows(iRow, cRows);
break;
//Multi-Cast
case INSERTROWS_ABOUTTODO:
while(pos)
hr = m_listListeners.GetNext(pos)->aboutToInsertRows(iRow, cRows);
break;
//Multi-Cast
case INSERTROWS_DIDEVENT:
while(pos)
hr = m_listListeners.GetNext(pos)->insertedRows(iRow, cRows);
break;
//Multi-Cast
case ROWSAVAILABLE_DIDEVENT:
while(pos)
hr = m_listListeners.GetNext(pos)->rowsAvailable(iRow, cRows);
break;
//Multi-Cast
case TRANSFERCOMPLETE_DIDEVENT:
while(pos)
hr = m_listListeners.GetNext(pos)->transferComplete(OSPXFER_COMPLETE);
break;
//Multi-Cast
case TRANSFERCOMPLETE_FAILEDTODO:
while(pos)
hr = m_listListeners.GetNext(pos)->transferComplete(OSPXFER_ABORT);
break;
default:
//Unknown event?
break;
};
return hr;
}
////////////////////////////////////////////////////////
// OSPSimpleProvider implementation
//
////////////////////////////////////////////////////////
HRESULT MyOSPObject::addOLEDBSimpleProviderListener(OLEDBSimpleProviderListener* pospIListener)
{
if(pospIListener==NULL)
return E_INVALIDARG;
//Add the Listener:
pospIListener->AddRef();
m_listListeners.AddTail(pospIListener);
return S_OK;
}
HRESULT MyOSPObject::deleteRows(DBROWCOUNT iRow, DBROWCOUNT cRows, DBROWCOUNT* pcRowsDeleted)
{
DBROWCOUNT cNewRows, i, j;
VARIANT** rvNewTable;
// Doesn't handle label or wildcard rows
if (iRow <= 0)
return E_FAIL;
// InvalidArg
if (cRows < 0)
return E_INVALIDARG;
// Is starting row within range?
if (iRow > m_cRows)
return E_INVALIDARG;
// Adjust row count to stay within range:
if (iRow + cRows > m_cRows + 1)
cRows = m_cRows - iRow + 1;
// Notify our Listeners:
Notify(DELETEROWS_ABOUTTODO, iRow, -1, cRows);
// Allocate new array with reduced rows:
cNewRows = m_cRows - cRows;
rvNewTable = (VARIANT**)CoTaskMemAlloc((cNewRows+1) * sizeof(VARIANT*));
if (!rvNewTable)
return E_OUTOFMEMORY;
j = 0;
for (i = 0; i <= m_cRows; i++)
{
if (i < iRow || i > (iRow + cRows - 1))
rvNewTable[j++] = m_rvTable[i]; // retain rows
else
CoTaskMemFree(m_rvTable[i]); // free deleted row
}
// Free old table, point to new table and adjust count:
CoTaskMemFree(m_rvTable);
m_rvTable = rvNewTable;
m_cRows = cNewRows;
// Save the internal table to the data file:
if (m_szFilePath != NULL)
saveData();
// Notify our Listeners:
Notify(DELETEROWS_DIDEVENT, iRow, -1, cRows);
// Return the count of rows deleted:
*pcRowsDeleted = cRows;
return S_OK;
}
HRESULT MyOSPObject::find(DBROWCOUNT iRowStart, DB_LORDINAL iColumn, VARIANT val, OSPFIND findFlags, OSPCOMP compType, DBROWCOUNT* piRowFound)
{
// Check arguments:
if (piRowFound == NULL)
return E_INVALIDARG;
if (iRowStart <= 0 || iRowStart > m_cRows)
return E_FAIL;
if (iColumn <= 0 || iColumn > m_cColumns)
return E_FAIL;
// Determine search order:
DBROWCOUNT iRowStop = (findFlags == OSPFIND_UP || findFlags == OSPFIND_UPCASESENSITIVE) ? 0 : m_cRows+1;
BOOL fCaseSensitive = (findFlags == OSPFIND_CASESENSITIVE || findFlags == OSPFIND_UPCASESENSITIVE);
// Linear search through all rows:
while (iRowStart != iRowStop)
{
// Although CompareVariant deosn't work for integers relationship(<, ==, >),
// it works for VT_BSTR. That suffices for this program.
LONG iCompVal = CompareVariant(&m_rvTable[iRowStart][iColumn], &val, fCaseSensitive);
switch(compType)
{
case OSPCOMP_EQ:
if (iCompVal == 0)
{
*piRowFound = iRowStart;
return S_OK;
}
break;
case OSPCOMP_LT:
if (iCompVal < 0)
{
*piRowFound = iRowStart;
return S_OK;
}
break;
case OSPCOMP_LE:
if (iCompVal <= 0)
{
*piRowFound = iRowStart;
return S_OK;
}
break;
case OSPCOMP_GE:
if (iCompVal >= 0)
{
*piRowFound = iRowStart;
return S_OK;
}
break;
case OSPCOMP_GT:
if (iCompVal > 0)
{
*piRowFound = iRowStart;
return S_OK;
}
break;
case OSPCOMP_NE:
if (iCompVal != 0)
{
*piRowFound = iRowStart;
return S_OK;
}
break;
default:
break;
};
if (findFlags == OSPFIND_UP || findFlags == OSPFIND_UPCASESENSITIVE)
iRowStart--;
else
iRowStart++;
}
*piRowFound = -1;
return S_FALSE;
}
HRESULT MyOSPObject::insertRows(DBROWCOUNT iRow, DBROWCOUNT cRows, DBROWCOUNT* pcRowsInserted)
{
DBROWCOUNT cNewRows, i, j, k;
VARIANT** rvNewTable;
// Doesn't handle label or wildcard rows
if (iRow <= 0)
return E_FAIL;
// InvalidArg
if (cRows < 0)
return E_INVALIDARG;
// If called to insert after the end, adjust to insert at the end:
if (iRow > m_cRows)
iRow = m_cRows + 1;
// Notify our Listeners:
Notify(INSERTROWS_ABOUTTODO, iRow, -1, cRows);
// Allocate new array with more rows:
cNewRows = m_cRows + cRows;
rvNewTable = (VARIANT**)CoTaskMemAlloc((cNewRows+1) * sizeof(VARIANT*));
if (rvNewTable)
return E_OUTOFMEMORY;
j = 0;
for (i = 0; i <= cNewRows; i++)
{
if (i < iRow || i > (iRow + cRows - 1))
rvNewTable[i] = m_rvTable[j++]; // retain rows
else
{ // insert new empty row
rvNewTable[i] = (VARIANT*)CoTaskMemAlloc((m_cColumns+1) * sizeof(VARIANT));
if (!rvNewTable[i])
{
for (k = iRow; k < i; k++)
{
SAFE_FREE(rvNewTable[k]);
}
SAFE_FREE(rvNewTable);
return E_OUTOFMEMORY;
}
for (k = 0; k <= m_cColumns; k++) // initialize new columns
rvNewTable[i][k].vt = VT_NULL;
}
}
// Free old table, point to new table and adjust count:
CoTaskMemFree(m_rvTable);
m_rvTable = rvNewTable;
m_cRows = cNewRows;
// Save the internal table to the data file:
if (m_szFilePath != NULL)
saveData();
// Notify our Listeners:
Notify(INSERTROWS_DIDEVENT, iRow, -1, cRows);
// Return the count of rows inserted:
*pcRowsInserted = cRows;
return S_OK;
}
HRESULT MyOSPObject::getColumnCount(DB_LORDINAL* pcColumns)
{
if (pcColumns == NULL)
return E_INVALIDARG;
*pcColumns = m_cColumns;
return S_OK;
}
HRESULT MyOSPObject::getEstimatedRows(DBROWCOUNT* piRows)
{
return getRowCount(piRows);
}
HRESULT MyOSPObject::getLocale(BSTR* pbstrLocale)
{
if (pbstrLocale == NULL)
return E_INVALIDARG;
*pbstrLocale = NULL;
// Return default locale:
TCHAR* pszLocale;
int cchData = GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SLANGUAGE, NULL, 0);
pszLocale = (TCHAR*)CoTaskMemAlloc(cchData * sizeof(TCHAR));
if (!pszLocale)
return E_OUTOFMEMORY;
BSTR bstr = SysAllocStringLen(NULL, cchData);
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SLANGUAGE, pszLocale, cchData);
#if !defined(_UNICODE)
bstr = SysAllocStringLen(NULL, cchData);
size_t cchConverted;
mbstowcs_s(&cchConverted, bstr, cchData+1, pszLocale, cchData);
#else
bstr = SysAllocStringLen(pszLocale, cchData);
#endif
*pbstrLocale = bstr;
return S_OK;
}
HRESULT MyOSPObject::getRowCount(DBROWCOUNT* pcRows)
{
if (pcRows == NULL)
return E_INVALIDARG;
*pcRows = m_cRows;
return S_OK;
}
HRESULT MyOSPObject::getRWStatus(DBROWCOUNT iRow, DB_LORDINAL iColumn, OSPRW* prwStatus)
{
if (prwStatus == NULL)
return E_INVALIDARG;
// In this sample, column 1 is read-only. All other columns may be changed:
if (iColumn == 1)
*prwStatus = OSPRW_READONLY;
else
*prwStatus = OSPRW_READWRITE;
return S_OK;
}
HRESULT MyOSPObject::getVariant(DBROWCOUNT iRow, DB_LORDINAL iColumn, OSPFORMAT format, VARIANT* pVar)
{
//INVALIDARG
if (pVar == NULL)
return E_INVALIDARG;
//Doesn't handle wildcards
if (iRow < 0 || iColumn < 0)
return E_FAIL;
//Get Column Value
VariantCopy(pVar, &m_rvTable[iRow][iColumn]);
return S_OK;
}
HRESULT MyOSPObject::isAsync(BOOL* pbAsynch)
{
if(pbAsynch==NULL)
return E_INVALIDARG;
*pbAsynch = FALSE;
return S_OK;
}
HRESULT MyOSPObject::removeOLEDBSimpleProviderListener(OLEDBSimpleProviderListener* pospIListener)
{
if(pospIListener==NULL)
return E_INVALIDARG;
//Find the Listener in the list:
POS pos = m_listListeners.Find(pospIListener);
if (pos == NULL)
return E_FAIL;
//Remove the Listener from the list and release it:
SAFE_RELEASE(pospIListener);
m_listListeners.RemoveAt(pos);
return S_OK;
}
HRESULT MyOSPObject::setVariant(DBROWCOUNT iRow, DB_LORDINAL iColumn, OSPFORMAT format, VARIANT Var)
{
// Doesn't handle wildcards
if (iRow < 0 || iColumn < 0)
return E_FAIL;
// Notify our Listeners:
Notify(CHANGECELL_ABOUTTODO, iRow, iColumn, 1);
// Set Column Value
VariantCopy(&m_rvTable[iRow][iColumn], &Var);
// Save the internal table to the data file:
if (m_szFilePath != NULL)
saveData();
// Notify our Listeners:
Notify(CHANGECELL_DIDEVENT, iRow, iColumn, 1);
return S_OK;
}
HRESULT MyOSPObject::stopTransfer()
{
// Do nothing because we are already populated
return S_OK;
}