c++ - 无需安装即可使用 SqlServer CE

标签 c++ com

给定:

  • 干净的机器,没有 SQL Server CE。
  • 一组 *.sdf 文件(Sql Server CE 数据库),不管它们是如何到达那里的
  • 相关Sql Server CE的DLLs (sqlceca35.dll, sqlcecompact35.dll, sqlceer35EN.dll, sqlceme35.dll, sqlceoledb35.dll, sqlceqp35.dll, sqlcese35.dll)

问题:

  • 如何提供由上述 DLL 实现的 Sql Server CE OLEDB 提供程序。当以受限帐户运行时,我正在寻找一种编程方式来执行此操作。

换句话说,假设我们谈论的是Sql Server CE 3.5,如何让下面的代码成功:

IDBInitializePtr spDBInitialize;
HRESULT hr = spDBInitialize.CreateInstance(CLSID_SQLSERVERCE35, NULL);

请注意,机器是干净的,代码是作为受限帐户运行的。

编辑

还有一个问题。代码是 C++,我不能使用 Ado.NET

最佳答案

spDBInitialize.CreateInstance() 执行以下操作:

  1. 在 HKEY_CLASSES_ROOT\CLSID 下的 Windows 注册表中查找您的 CLSID
  2. 确定来自 InProcServer 的 DLL
  3. 在 DLL 上调用 LoadLibrary()
  4. 为“DllGetClassObject”调用 GetProcAddress
  5. 调用 DllGetClassObject 获取 IClassFactory
  6. 使用返回的 IClassFactory 来处理您的 CreateInstance(NULL, IID_IDBInitialize, (void**) &spIDBInitialize) 请求

在您的情况下,您确实无法通过第一步,因为您的 DLL 未在 Windows 注册表中注册。

但是,因为您知道 SQL Server CE DLL 的位置,所以您可以通过让您的代码仅实现 3、4、5 和 6 来解决这个问题。

这是一个使用 CoCreateInstance 替换打开 SDF 的 C++ 控制台应用程序:

#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;
}

代码片段中缺少的一些东西:

  • 当您完全完成库(通常在程序终止之前)时调用 FreeLibrary()
  • 处理错误的 HRESULT hr 返回码
  • 处理 LoadLibrary() 失败

要获得有意义的 SQL Server CE 操作错误消息,您应该查阅 Microsoft MSDN 文章 Using OLE DB Error Objects (SQL Server Compact Edition) .我通常从这里的精简版开始:

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);
    }
}

只部署整个 SQL Server CE 3.5 文件夹更容易和更安全,但是,如果您想要一个最小的集合,我相信以下文件对您的方案很重要:

  • sqlceoledb35.dll - SQLCE OLEDB 提供程序
  • sqlceqp35.dll - SQLCE 查询处理器
  • sqlcese35.dll - SQLCE 存储引擎
  • sqlceer35EN.dll - SQLCE native 错误字符串和资源
  • sqlcecompact35.dll - SQLCE 数据库修复工具

作为引用,我认为您不需要的文件是:

  • sqlceca35.dll - SQLCE 客户端代理(用于合并复制到 SQL Server)
  • sqlceme35.dll - SQLCE 托管扩展
  • System.Data.SqlServerCe.Entity.dll - 托管程序集

关于c++ - 无需安装即可使用 SqlServer CE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9102471/

相关文章:

c++ - 无法链接到共享库

java - "Feature Oriented Programming"(FOP) 在 C++ 中有什么意义,它在 Java 和 C# 中有意义吗?

c++ - 如何减少对大量纹理的绘图调用次数?

c++ - 如何将 C++ COM 连接到 WCF 服务

c# - 如何将整数数组从经典 asp 传递到 c# COM 对象?

c++ - 指针变量名返回的值是多少?

c++ - 可调用(回调)模板作为类模板参数

c++ - 在没有 Wrapper 类的情况下调用 ActiveX 接口(interface)上的方法

c++ - OLE bstr 不以 null 终止?

.net - 如何在自动化服务器列表中安装和注册用 VB.NET 编写的 COM Server for Excel?