After migrating an MFC application from Visual Studio 2008 to Visual Studio 2012 I run into an unexpected error: the application was having problem fetching data from the SQL Server database. After debugging it turned out that function CDatabase::GetConnect that I was using to retrieve the connection string after opening the database (for different purposes) was suddenly returning an empty string. It turned out this was a known MFC bug, reported on Microsoft Connect. The cause of the problem is that CDatabase encrypts the connection string, and then empties it. Here is a snippet of 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(); }
Though Microsoft promised to be solved the bug in the next major version I needed a fix now. So here is my fix.
CDatabase has a protected member m_strConnect which is supposed to keep the connection string in plain text, and one called m_blobConnect that represents the encrypted connection string. However, there is no method in CDatabase to return this blob. Getting the blob would allow you to decrypt it and get the actual connection string. So the solution is to derive CDatabase, provide a method to return the connection string, and in your code replace CDatabase with this derived class and the call to GetConnect() to this new method.
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; } };