Bug di CDatabase in MFC in VS2012

 C Programming >> Programmazione C >  >> Tags >> MFC
Bug di CDatabase in MFC in VS2012

Dopo aver migrato un'applicazione MFC da Visual Studio 2008 a Visual Studio 2012, mi imbatto in un errore imprevisto:l'applicazione aveva problemi a recuperare i dati dal database di SQL Server. Dopo il debug si è scoperto che la funzione CDatabase::GetConnect che stavo usando per recuperare la stringa di connessione dopo aver aperto il database (per scopi diversi) stava improvvisamente restituendo una stringa vuota. Si è scoperto che si trattava di un bug MFC noto, segnalato su Microsoft Connect. La causa del problema è che CDatabase crittografa la stringa di connessione e quindi la svuota. Ecco uno snippet di OpenEx:

BOOL CDatabase::OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions)
{
	ENSURE_VALID(this);
	ENSURE_ARG(lpszConnectString == NULL || AfxIsValidString(lpszConnectString));
	ENSURE_ARG(!(dwOptions & noOdbcDialog && dwOptions & forceOdbcDialog));

	// Exclusive access not supported.
	ASSERT(!(dwOptions & openExclusive));

	m_bUpdatable = !(dwOptions & openReadOnly);

	TRY
	{
		m_strConnect = lpszConnectString;

		DATA_BLOB connectBlob;
		connectBlob.pbData = (BYTE *)(LPCTSTR)m_strConnect;
		connectBlob.cbData = (DWORD)(AtlStrLen(m_strConnect) + 1) * sizeof(TCHAR);
		if (CryptProtectData(&connectBlob, NULL, NULL, NULL, NULL, 0, &m_blobConnect))
		{
			SecureZeroMemory((BYTE *)(LPCTSTR)m_strConnect, m_strConnect.GetLength() * sizeof(TCHAR));
			m_strConnect.Empty();
		}

Sebbene Microsoft avesse promesso di risolvere il bug nella prossima versione principale, avevo bisogno di una correzione ora. Quindi ecco la mia soluzione.

CDatabase ha un membro protetto m_strConnect che dovrebbe mantenere la stringa di connessione in testo normale e uno chiamato m_blobConnect che rappresenta la stringa di connessione crittografata. Tuttavia, non esiste alcun metodo in CDatabase per restituire questo BLOB. Ottenere il BLOB ti consentirebbe di decrittografarlo e ottenere la stringa di connessione effettiva. Quindi la soluzione è derivare CDatabase, fornire un metodo per restituire la stringa di connessione e nel codice sostituire CDatabase con questa classe derivata e la chiamata a GetConnect() a questo nuovo metodo.

class CDatabaseEx : public CDatabase
{
public:
   CString GetConnectEx()
   {
      CString strConnect = m_strConnect;

      if (strConnect.GetLength() == 0)
      {
         DATA_BLOB connectBlob;
         if (CryptUnprotectData(&m_blobConnect, NULL, NULL, NULL, NULL, 0, &connectBlob))
         {
            strConnect = (LPTSTR)connectBlob.pbData;
            LocalFree(connectBlob.pbData);
         }
      }

      return strConnect;
   }
};