Se vogliamo riuscire a creare il nostro OleDB Provider in ATL occorre sapere come ci si connette ad un server MySQL in linguaggio C. Per compiere questa operazione occorre innanzitutto scaricare le librerie necessarie per il linguaggio C dal sito di MySQL (libmysql). All’interno di Visual Studio occorre quindi creare un progetto ATL ed inserire un controllo OleDB Provider, successivamente inseriamo la cartella dei file di inclusione e delle librerie che abbiamo scaricato precedentemente all’interno del nostro progetto.
Una connessione ad un server MySQL avviene su una porta standard (3306), ma ovviamente può avvenire anche su altre, abbiamo bisogno del nome del server e del nome dell’utente e la password, per ultimo il nome del database ( archivio ) su cui agire. L’utilizzo della libreria C di MySQL è molto semplice, occorre creare un oggetto MYSQL e su questo fare tutte le operazioni, al termine chiudere la connessione e rilasciare l’oggetto. Nel progetto che abbiamo creato in Visual Studio ci sono 4 file su cui andremo a scrivere ( i nomi variano a seconda del nome dato al progetto ): uno per i nostri dati, uno per la gestione del command e del rowset, infine uno per la sessione. Ho inserito la dichiarazione a livello globale dell’oggetto MYSQL in modo da poter operare sul database da ogni file.
#include <mysql.h> #include <ATLComTime.h> #include <atlcur.h> #pragma warning (disable : 4244) #pragma warning (disable : 4996) MYSQL* m_pMySQL;
Questo è l’inizio del file dove risiede il mio rowset, la prima inclusione è riferita alla libreria MySQL che ho configurato a livello globale, mentre la seconda è stata inserita per la gestione dei campi DATETIME.
Il primo oggetto da costruire è il Datasource che conterrà le informazioni per la connessione, che però verrà fatta praticamente nell’oggetto sessione. Ecco il codice del mio oggetto Datasource:
// MyDS.h : Dichiarazione di CMyData #pragma once #include "resource.h" // simboli principali #include "MyRS.h" #include "MySess.h" // CMyData class ATL_NO_VTABLE CMyData : public CComObjectRootEx<CComMultiThreadModel>, public CComCoClass<CMyData, &CLSID_MyVikProv>, public IDBCreateSessionImpl<CMyData, CMySession>, public IDBInitializeImpl<CMyData>, public IDBPropertiesImpl<CMyData>, public IPersistImpl<CMyData>, public IInternalConnectionImpl<CMyData> { public: DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return FInit(); } void FinalRelease() { } DECLARE_REGISTRY_RESOURCEID(IDR_MYVIKPROV) BEGIN_COM_MAP(CMyData) COM_INTERFACE_ENTRY(IDBCreateSession) COM_INTERFACE_ENTRY(IDBInitialize) COM_INTERFACE_ENTRY(IDBProperties) COM_INTERFACE_ENTRY(IPersist) COM_INTERFACE_ENTRY(IInternalConnection) END_COM_MAP() BEGIN_PROPSET_MAP(CMyData) BEGIN_PROPERTY_SET(DBPROPSET_DATASOURCEINFO) PROPERTY_INFO_ENTRY(ACTIVESESSIONS) PROPERTY_INFO_ENTRY(DATASOURCEREADONLY) PROPERTY_INFO_ENTRY(BYREFACCESSORS) PROPERTY_INFO_ENTRY(OUTPUTPARAMETERAVAILABILITY) PROPERTY_INFO_ENTRY(PROVIDEROLEDBVER) PROPERTY_INFO_ENTRY(DSOTHREADMODEL) PROPERTY_INFO_ENTRY(SUPPORTEDTXNISOLEVELS) PROPERTY_INFO_ENTRY(USERNAME) PROPERTY_INFO_ENTRY_VALUE(DBMSNAME, L"MySQL Server") PROPERTY_INFO_ENTRY_VALUE(DBMSVER, L"5.00.00") PROPERTY_INFO_ENTRY_VALUE(PROVIDERFRIENDLYNAME, L"RGPSoft MySQL Provider") PROPERTY_INFO_ENTRY_VALUE(PROVIDERVER, L"1.00.00") END_PROPERTY_SET(DBPROPSET_DATASOURCEINFO) BEGIN_PROPERTY_SET(DBPROPSET_DBINIT) PROPERTY_INFO_ENTRY(AUTH_PASSWORD) PROPERTY_INFO_ENTRY(AUTH_PERSIST_SENSITIVE_AUTHINFO) PROPERTY_INFO_ENTRY(AUTH_USERID) PROPERTY_INFO_ENTRY(INIT_DATASOURCE) PROPERTY_INFO_ENTRY(INIT_HWND) PROPERTY_INFO_ENTRY_VALUE(INIT_LCID, 1040) PROPERTY_INFO_ENTRY_VALUE(INIT_LOCATION, L"3306") PROPERTY_INFO_ENTRY_EX(INIT_MODE, VT_I4, DBPROPFLAGS_NOTSUPPORTED, DB_MODE_SHARE_DENY_NONE, DBPROPOPTIONS_SETIFCHEAP) PROPERTY_INFO_ENTRY(INIT_PROMPT) PROPERTY_INFO_ENTRY(INIT_PROVIDERSTRING) PROPERTY_INFO_ENTRY(INIT_CATALOG) END_PROPERTY_SET(DBPROPSET_DBINIT) CHAIN_PROPERTY_SET(CMySession) CHAIN_PROPERTY_SET(CMyCommand) END_PROPSET_MAP() public: }; OBJECT_ENTRY_AUTO(__uuidof(MyVikProv), CMyData)
Il mio oggetto si chiama MyData ed il progetto si chiama MyVik perché è il provider per MySQL del software Vikings. Ora vediamo l’oggetto sessione che effettua la connessione reale al server MySQL.
// CMySession class ATL_NO_VTABLE CMySession : public CComObjectRootEx<CComMultiThreadModel>, public IGetDataSourceImpl<CMySession>, public IOpenRowsetImpl<CMySession>, public ISessionPropertiesImpl<CMySession>, public IObjectWithSiteSessionImpl<CMySession>, public IDBSchemaRowsetImpl<CMySession>, public IDBCreateCommandImpl<CMySession, CMyCommand>, public ITransactionLocalImpl<CMySession>, public ITableDefinitionImpl<CMySession> { public: CMySession() { m_pMySQL = NULL; } DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return FInit(); } void FinalRelease() { if (m_pMySQL != NULL) { mysql_close(m_pMySQL); m_pMySQL = NULL; } } STDMETHOD(SetSite)(IUnknown* pCreator) { HRESULT hr = __super::SetSite(pCreator); if (FAILED(hr)) return hr; USES_CONVERSION; // Prendo i dati dal datasource IDBProperties* pProp = NULL; hr = pCreator->QueryInterface(__uuidof(IDBProperties), (void**)&pProp); if (FAILED(hr)) return hr; CString szCatalog, szDataSource, szPort, szUser, szPassword; CDBPropIDSet set0; set0.SetGUID(DBPROPSET_DBINIT); set0.AddPropertyID(DBPROP_INIT_DATASOURCE); DBPROPSET* pPropSet = NULL; ULONG ulPropSet = 0; hr = pProp->GetProperties(1, &set0, &ulPropSet, &pPropSet); if (FAILED(hr)) return hr; if (pPropSet != NULL) { szDataSource = OLE2T(pPropSet->rgProperties[0]. vValue.bstrVal); ::CoTaskMemFree(pPropSet->rgProperties); ::CoTaskMemFree(pPropSet); } else return E_FAIL; CDBPropIDSet set1; set1.SetGUID(DBPROPSET_DBINIT); set1.AddPropertyID(DBPROP_INIT_LOCATION); ulPropSet = 0; hr = pProp->GetProperties(1, &set1, &ulPropSet, &pPropSet); if (FAILED(hr)) return hr; if (pPropSet != NULL) { szPort = OLE2T(pPropSet->rgProperties[0].vValue. bstrVal); ::CoTaskMemFree(pPropSet->rgProperties); ::CoTaskMemFree(pPropSet); } else return E_FAIL; CDBPropIDSet set2; set2.SetGUID(DBPROPSET_DBINIT); set2.AddPropertyID(DBPROP_AUTH_USERID); ulPropSet = 0; hr = pProp->GetProperties(1, &set2, &ulPropSet, &pPropSet); if (FAILED(hr)) return hr; if (pPropSet != NULL) { szUser = OLE2T(pPropSet->rgProperties[0].vValue. bstrVal); ::CoTaskMemFree(pPropSet->rgProperties); ::CoTaskMemFree(pPropSet); } else return E_FAIL; CDBPropIDSet set3; set3.SetGUID(DBPROPSET_DBINIT); set3.AddPropertyID(DBPROP_AUTH_PASSWORD); ulPropSet = 0; hr = pProp->GetProperties(1, &set3, &ulPropSet, &pPropSet); if (FAILED(hr)) return hr; if (pPropSet != NULL) { szPassword = OLE2T(pPropSet->rgProperties[0].vValue. bstrVal); ::CoTaskMemFree(pPropSet->rgProperties); ::CoTaskMemFree(pPropSet); } else return E_FAIL; CDBPropIDSet set4; set4.SetGUID(DBPROPSET_DBINIT); set4.AddPropertyID(DBPROP_INIT_CATALOG); ulPropSet = 0; hr = pProp->GetProperties(1, &set4, &ulPropSet, &pPropSet); if (FAILED(hr)) return hr; if (pPropSet != NULL) { szCatalog = OLE2T(pPropSet->rgProperties[0].vValue. bstrVal); ::CoTaskMemFree(pPropSet->rgProperties); ::CoTaskMemFree(pPropSet); } else return E_FAIL; unsigned int nPort = _wtoi(szPort); if (!(m_pMySQL = mysql_init(NULL))) { return E_FAIL; } if (!mysql_real_connect(m_pMySQL, W2A(szDataSource), W2A(szUser), W2A(szPassword), W2A(szCatalog), nPort, NULL, 0)) { return E_FAIL; } pProp->Release(); return hr; } STDMETHOD(OpenRowset)(IUnknown *pUnk, DBID *pTID, DBID *pInID, REFIID riid, ULONG cSets, DBPROPSET rgSets[], IUnknown **ppRowset) { CMyRowset* pRowset; return CreateRowset(pUnk, pTID, pInID, riid, cSets, rgSets, ppRowset, pRowset); } void SetRestrictions(ULONG cRestrictions, GUID* rguidSchema, ULONG* rgRestrictions) { for (ULONG l=0; l<cRestrictions; l++) { // Sono supportate solo le restrizioni sul nome di tabella if (InlineIsEqualGUID(rguidSchema[l], DBSCHEMA_TABLES)) rgRestrictions[l] = 1101; else if (InlineIsEqualGUID(rguidSchema[l], DBSCHEMA_COLUMNS)) rgRestrictions[l] = 1101; else if (InlineIsEqualGUID(rguidSchema[l], DBSCHEMA_CATALOGS)) rgRestrictions[l] = 0; } } BEGIN_PROPSET_MAP(CMySession) BEGIN_PROPERTY_SET(DBPROPSET_SESSION) PROPERTY_INFO_ENTRY(SESS_AUTOCOMMITISOLEVELS) END_PROPERTY_SET(DBPROPSET_SESSION) END_PROPSET_MAP() BEGIN_COM_MAP(CMySession) COM_INTERFACE_ENTRY(IGetDataSource) COM_INTERFACE_ENTRY(IOpenRowset) COM_INTERFACE_ENTRY(ISessionProperties) COM_INTERFACE_ENTRY(IObjectWithSite) COM_INTERFACE_ENTRY(IDBCreateCommand) COM_INTERFACE_ENTRY(IDBSchemaRowset) COM_INTERFACE_ENTRY(ITransactionLocal) COM_INTERFACE_ENTRY(ITransaction) COM_INTERFACE_ENTRY(ITableDefinition) END_COM_MAP() BEGIN_SCHEMA_MAP(CMySession) SCHEMA_ENTRY(DBSCHEMA_TABLES, CMySessionTRSchemaRowset) SCHEMA_ENTRY(DBSCHEMA_COLUMNS, CMySessionColSchemaRowset) SCHEMA_ENTRY(DBSCHEMA_CATALOGS, CMySessionCTSchemaRowset) END_SCHEMA_MAP() };
Dopo aver richiesto i dati alle proprietà del Datasource, possiamo utilizzarle per effettuare la connessione con la funzione mysql_real_connect, al termine inseriamo le restrizioni che sono le caratteristiche supportate dal nostro provider. Le restrizioni che abbiamo abilitato sono la scelta del catalogo, la modifica delle colonne e dei campi direttamente con l’utilizzo del provider.
Per ora ci fermiamo qui, ma nelle prossime lezioni vedremo come implementare la creazione dell’oggetto rowset con l’utilizzo del command, non prima di gestire le transazioni come scritto nelle interfacce dell’oggetto sessione. Se avete bisogno di farmi delle domande o avere maggiori informazioni su un determinato argomento potete farlo dal nostro forum di informatica