The following is a complete source code listing for the example presented in "Handling Long Data Types." The example selects the Notes column (LONG_VARCHAR) from the Employees table of the Access Northwind sample database. The database is shipped with the OLE DB SDK and is installed as the ODBC data source OLE_DB_NWind_Jet.
To build the file using Microsoft Visual C++ 4.0 or later:
/********************************************************************
* Using ISequentialStream to retrieve LONG_VARCHAR/ LONG_VARBINARY
* (BLOB) data
********************************************************************/
#define UNICODE
#define _UNICODE
#define DBINITCONSTANTS
#define INITGUID
#include <windows.h>
#include <stdio.h>
#include <oledb.h> // OLE DB include files
#include <oledberr.h>
#include <msdasql.h>
#define BLOCK_SIZE 250
// Prototypes
HRESULT myInitDSO(IDBInitialize** ppIDBI);
HRESULT mySetInitProps(IDBInitialize* pIDBInitialize);
HRESULT myCommand(IDBInitialize* pIDBI, IRowset** ppIRowset);
void myGetBLOBData(IRowset* pIRowset);
void DumpError(LPSTR lpStr);
IMalloc* g_pIMalloc = NULL;
/********************************************************************
* OLE DB application main()
********************************************************************/
int main()
{
IDBInitialize* pIDBInitialize = NULL;
IRowset* pIRowset = NULL;
//Init OLE and set up the DLLs.
CoInitialize(NULL);
// Get the task memory allocator.
if (FAILED(CoGetMalloc(MEMCTX_TASK, &g_pIMalloc)))
goto EXIT;
// Connect to the data source.
if (FAILED(myInitDSO(&pIDBInitialize)))
goto EXIT;
// Get a session, set and execute a command.
if (FAILED(myCommand(pIDBInitialize, &pIRowset)))
goto EXIT;
myGetBLOBData(pIRowset);
EXIT:
if (pIRowset != NULL)
pIRowset->Release();
if (pIDBInitialize != NULL)
{
if (FAILED(pIDBInitialize->Uninitialize()))
{
// Uninitialize is not required, but it will fail if an
// interface has not been released. We can use it for
// debugging.
DumpError("Someone forgot to release something!");
}
pIDBInitialize->Release();
}
if (g_pIMalloc != NULL)
g_pIMalloc->Release();
CoUninitialize();
return (0);
}
/********************************************************************
* Initialize the data source.
********************************************************************/
HRESULT myInitDSO
(
IDBInitialize** ppIDBInitialize // [out]
)
{
// Create an instance of the MSDASQL (ODBC) provider.
CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
IID_IDBInitialize, (void**)ppIDBInitialize);
if (*ppIDBInitialize == NULL)
{
return (E_FAIL);
}
if (FAILED(mySetInitProps(*ppIDBInitialize)))
{
return (E_FAIL);
}
if (FAILED((*ppIDBInitialize)->Initialize()))
{
DumpError("IDBInitialze->Initialize failed.");
return (E_FAIL);
}
return (NOERROR);
}
/********************************************************************
* Set initialization properties on a data source.
********************************************************************/
HRESULT mySetInitProps
(
IDBInitialize* pIDBInitialize // [in]
)
{
const ULONG nProps = 4;
IDBProperties* pIDBProperties;
DBPROP InitProperties[nProps];
DBPROPSET rgInitPropSet;
HRESULT hr;
// Initialize common property options.
for (ULONG i = 0; i < nProps; i++ )
{
VariantInit(&InitProperties[i].vValue);
InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;
InitProperties[i].colid = DB_NULLID;
}
// Level of prompting that will be done to complete the
// connection process
InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;
InitProperties[0].vValue.vt = VT_I2;
InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;
// Data source name--see the sample source included with the OLE
// DB SDK.
InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;
InitProperties[1].vValue.vt = VT_BSTR;
InitProperties[1].vValue.bstrVal =
SysAllocString(OLESTR("OLE_DB_NWind_Jet"));
// User ID
InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;
InitProperties[2].vValue.vt = VT_BSTR;
InitProperties[2].vValue.bstrVal = SysAllocString(OLESTR(""));
// Password
InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
InitProperties[3].vValue.vt = VT_BSTR;
InitProperties[3].vValue.bstrVal = SysAllocString(OLESTR(""));
rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;
rgInitPropSet.cProperties = nProps;
rgInitPropSet.rgProperties = InitProperties;
// Set initialization properties.
pIDBInitialize->QueryInterface(IID_IDBProperties, (void**)
&pIDBProperties);
hr = pIDBProperties->SetProperties(1, &rgInitPropSet);
SysFreeString(InitProperties[1].vValue.bstrVal);
SysFreeString(InitProperties[2].vValue.bstrVal);
SysFreeString(InitProperties[3].vValue.bstrVal);
pIDBProperties->Release();
if (FAILED(hr))
{
DumpError("Set properties failed.");
}
return (hr);
}
/********************************************************************
* Execute a command selecting Notes from Employees.
********************************************************************/
HRESULT myCommand
(
IDBInitialize* pIDBInitialize, // [in]
IRowset** ppIRowset // [out]
)
{
IDBCreateSession* pIDBCreateSession;
IDBCreateCommand* pIDBCreateCommand;
IRowset* pIRowset;
ICommandText* pICommandText;
LPCTSTR wSQLString = OLESTR("SELECT Notes ")
OLESTR("FROM Employees");
LONG cRowsAffected;
HRESULT hr;
// Set for failure
*ppIRowset = NULL;
// Get the DB session object.
hr = pIDBInitialize->QueryInterface(IID_IDBCreateSession,
(void**) &pIDBCreateSession);
if (FAILED(hr))
{
DumpError("Session initialization failed.");
return (hr);
}
// Create the session.
hr = pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
(IUnknown**) &pIDBCreateCommand);
pIDBCreateSession->Release();
if (FAILED(hr))
{
DumpError("Create session failed.");
return (hr);
}
// Create the command object.
hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText,
(IUnknown**) &pICommandText);
if (FAILED(hr))
{
DumpError("Create command failed.");
return (hr);
}
pIDBCreateCommand->Release();
// The command requires the actual text as well as an indicator
// of its language and dialect.
pICommandText->SetCommandText(DBGUID_DBSQL, wSQLString);
// Execute the command.
hr = pICommandText->Execute(NULL, IID_IRowset, NULL,
&cRowsAffected, (IUnknown**) &pIRowset);
if (FAILED(hr))
{
DumpError("Command execution failed.");
}
pICommandText->Release();
*ppIRowset = pIRowset;
return (hr);
}
/********************************************************************
* Retrieve data from an ODBC LONG_VARCHAR column (Notes in
* Employees).
********************************************************************/
void myGetBLOBData
(
IRowset* pIRowset // [in]
)
{
DBOBJECT ObjectStruct; // For binding, retrieve an
// object pointer.
DBBINDING rgBinding[1]; // Bind a single column.
IAccessor* pIAccessor = NULL; // Accessor creation
HACCESSOR hAccessor = NULL;
ULONG ulErrorBinding;
void* pData; // Bound consumer buffer
HROW rghRows[1];
HROW* pRows = &rghRows[0];
ULONG cRows;
char szNotes[BLOCK_SIZE + 1];// Text data from "Notes"
ULONG cbRead; // Count of bytes read
// Set up the object structure for accessor creation. Ask the
// provider to return an ISequentialStream interface for reading.
ObjectStruct.dwFlags = STGM_READ;
ObjectStruct.iid = IID_ISequentialStream;
// Set up the binding struct for the accessor.
rgBinding[0].iOrdinal = 1; // Only one column
rgBinding[0].obValue = 0; // Offset to data
rgBinding[0].obLength = 0; // Ignore length
rgBinding[0].obStatus = sizeof(IUnknown*); // Offset to status
rgBinding[0].pTypeInfo = NULL; // Reserved
rgBinding[0].pObject = &ObjectStruct; // Our interface
// request
rgBinding[0].pBindExt = NULL; // Reserved
rgBinding[0].dwPart = DBPART_VALUE | // Get both VALUE
DBPART_STATUS; // and STATUS
// parts.
rgBinding[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBinding[0].eParamIO = DBPARAMIO_NOTPARAM;
rgBinding[0].cbMaxLen = 0; // Not applicable
rgBinding[0].dwFlags = 0; // Reserved
rgBinding[0].wType = DBTYPE_IUNKNOWN; // Type
// DBTYPE_IUNKNOWN
rgBinding[0].bPrecision = 0; // Not applicable
rgBinding[0].bScale = 0; // Not applicable
// Get the accessor interface and create the accessor.
pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);
if (FAILED(pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1,
rgBinding, sizeof(IUnknown*) + sizeof(ULONG), &hAccessor,
&ulErrorBinding)))
{
DumpError("CreateAccessor failed.");
return;
}
// Allocate memory for the returned pointer and the status field.
// The first sizeof(IUnknown*) bytes are for the pointer to the
// object; the next sizeof(ULONG) bytes are for the status.
pData = new BYTE[sizeof(IUnknown*) + sizeof(ULONG)];
while (TRUE)
{
// Get the next row.
if (FAILED(pIRowset->GetNextRows(NULL, 0, 1, &cRows,
&pRows)))
{
DumpError("GetNextRows failed.\n");
break;
}
if (cRows == 0)
{
break;
}
// Get the row data, the pointer to an ISequentialStream*.
if (FAILED(pIRowset->GetData(*pRows, hAccessor, pData)))
{
DumpError("GetData failed.\n");
break;
}
// Read and process BLOCK_SIZE bytes at a time.
if ((ULONG)((BYTE*)pData)[rgBinding[0].obStatus] ==
DBSTATUS_S_ISNULL)
{
// Process NULL data.
printf("<null>");
}
else if ((ULONG)((BYTE*)pData)[rgBinding[0].obStatus] ==
DBSTATUS_S_OK)
{
do
{
(*((ISequentialStream**) pData))->Read(szNotes,
BLOCK_SIZE, &cbRead);
if (cbRead > 0)
{
// process data
szNotes[cbRead] = (char) NULL;
printf(szNotes);
}
}
while (cbRead >= BLOCK_SIZE);
(*((ISequentialStream**) pData))->Release();
printf("\n\n");
}
pIRowset->ReleaseRows(cRows, pRows, NULL, NULL, NULL);
}
// Clean up.
pIAccessor->ReleaseAccessor(hAccessor, NULL);
pIAccessor->Release();
delete [] pData;
}
/********************************************************************
* Dump an error to the console.
********************************************************************/
void DumpError(LPSTR lpStr)
{
printf(lpStr);
printf("\n");
}