Given:
spDBInitialize.CreateInstance()
does the following:
LoadLibrary()
on the DLLGetProcAddress
for "DllGetClassObject"DllGetClassObject
to get an IClassFactory
IClassFactory
to handle your CreateInstance(NULL, IID_IDBInitialize, (void**) &spIDBInitialize)
requestIn your scenario, you do cannot get pass the first step because your DLL isn't registered in the Windows Registry.
However, because you know where the SQL Server CE DLLs are you can get around this by making your code just implement 3, 4, 5 and 6.
Here's a C++ console application that opens a SDF using CoCreateInstance replacement:
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <oleauto.h>
#include <atlbase.h>
#include "C:\Program Files (x86)\Microsoft SQL Server Compact Edition\v3.5\Include\sqlce_oledb.h"
#include "C:\Program Files (x86)\Microsoft SQL Server Compact Edition\v3.5\Include\sqlce_err.h"
//----------------------------------------------------------------------
// Creates a COM object using an HMODULE instead of the Windows Registry
//----------------------------------------------------------------------
HRESULT DllCoCreateInstance(HMODULE hModule, REFCLSID rclsid, LPUNKNOWN pUnkOuter, REFIID riid, LPVOID FAR* ppv)
{
HRESULT hr = S_OK;
if (hModule == NULL)
{
return E_INVALIDARG;
}
BOOL (WINAPI*DllGetClassObject)(REFCLSID, REFIID, LPVOID) = NULL;
(FARPROC&) DllGetClassObject = GetProcAddress(hModule, "DllGetClassObject");
if (DllGetClassObject == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
CComPtr<IClassFactory> spIClassFactory;
hr = DllGetClassObject(rclsid, IID_IClassFactory, &spIClassFactory);
if (FAILED(hr))
{
return hr;
}
return spIClassFactory->CreateInstance(pUnkOuter, riid, ppv);
}
//----------------------------------------------------------------------
// Open a close a SDF file
//----------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
// Need a loaded library so we can CoCreateInstance without the Windows Registry.
HMODULE hModule = LoadLibrary(L"C:\\Program Files (x86)\\Microsoft SQL Server Compact Edition\\v3.5\\sqlceoledb35.dll");
// Open a SQL Server CE 3.5 database without using Windows Registry.
CComPtr<IDBInitialize> spIDBInitialize;
//hr = spIDBInitialize.CoCreateInstance(CLSID_SQLSERVERCE_3_5);
hr = DllCoCreateInstance(hModule, CLSID_SQLSERVERCE_3_5, NULL, IID_IDBInitialize, (void**) &spIDBInitialize);
CComPtr<IDBProperties> spIDBProperties;
hr = spIDBInitialize->QueryInterface(IID_IDBProperties, (void**) &spIDBProperties);
CComVariant varDataSource(OLESTR("InsertYourSampleDatabase.SDF"));
DBPROP prop = { DBPROP_INIT_DATASOURCE, DBPROPOPTIONS_REQUIRED, 0, DB_NULLID, varDataSource };
DBPROPSET propSet = {&prop, 1, DBPROPSET_DBINIT};
hr = spIDBProperties->SetProperties(1, &propSet);
spIDBProperties = NULL;
hr = spIDBInitialize->Initialize();
// @@TODO: Do your regular OLEDB code with the opened database.
//...
// Close COM objects
spIDBInitialize = NULL;
CoUninitialize();
return 0;
}
Some things that's missing from the code snippet:
FreeLibrary()
when you're completely finished with the library (usually prior to program termination)To get meaningful error messages for SQL Server CE operations you should consult Microsoft MSDN article Using OLE DB Error Objects (SQL Server Compact Edition). I usually start with a condense version of it here:
if (FAILED(hr))
{
CComPtr<IErrorInfo> spIErrorInfo;
GetErrorInfo(0, &spIErrorInfo);
if (spIErrorInfo != NULL)
{
CComBSTR bstrError;
spIErrorInfo->GetDescription(&bstrError);
// @@TODO: Do stuff with bstrError
wprintf("%s\r\n", (BSTR) bstrError);
}
}
It's easier and safer to just deploy the entire SQL Server CE 3.5 folder, but, if you want a minimal set, I believe the following files are the important ones for your scenario:
For reference, the files I believe you don't need are: