Using CRecordset::Open and determining if no records returned? - visual-c++

CString strSQL = _T("");
rAryStrMemo.RemoveAll(); // Type CStringArray
strSQL.Format(_T("SELECT memo1, memo2, memo3, memo4, memo5 FROM [Congregation Speakers] WHERE Congregation='%s' AND Speaker='%s'"),
theApp.FormatDBText(strCongregation),
theApp.FormatDBText(strSpeaker));
CRecordset *pRecords = new CRecordset(theApp.GetDatabase());
if (pRecords->Open(CRecordset::snapshot, (LPCTSTR)strSQL, CRecordset::readOnly))
{
CString strMemo = _T("");
GetFieldValue(pRecords, _T("memo1"), strMemo); rAryStrMemo.Add(strMemo);
GetFieldValue(pRecords, _T("memo2"), strMemo); rAryStrMemo.Add(strMemo);
GetFieldValue(pRecords, _T("memo3"), strMemo); rAryStrMemo.Add(strMemo);
GetFieldValue(pRecords, _T("memo4"), strMemo); rAryStrMemo.Add(strMemo);
GetFieldValue(pRecords, _T("memo5"), strMemo); rAryStrMemo.Add(strMemo);
}
}
The function it calls:
inline void GetFieldValue(CRecordset *pRS, LPCTSTR lpszFieldName, CString &rstrValue)
{
rstrValue = _T(""); // Fallback.
if (pRS != nullptr)
{
CString strTemp ;
pRS->GetFieldValue(lpszFieldName, strTemp); // This will be not right.
rstrValue = (LPCTSTR)strTemp ; // Forcing this conversion corrects it.
}
}
I learnt something new today. If the resulting CRecordset is empty it causes an "Invalid cursor state" exception. I mistakenly thought that if Open returned true that it had one or more records. Obviously not.
So, what is the right way to know that atleast 1 record was returned?

Related

dynamic_cast of CWnd to CComboBox is yeilding a null pointer in OnSize function

Code:
void CChristianLifeMinistryEditorDlg::OnSize(UINT nType, int cx, int cy)
{
CResizingDialog::OnSize(nType, cx, cy);
const CWnd* pFocus = GetFocus();
CComboBox* pFocusCombo = nullptr;
if (pFocus != nullptr)
{
if (pFocus->GetParent()->IsKindOf(RUNTIME_CLASS(CComboBox)))
{
pFocusCombo = dynamic_cast<CComboBox*>(GetFocus()->GetParent());
}
}
for (CWnd* pWnd = GetWindow(GW_CHILD); pWnd != nullptr; pWnd = pWnd->GetNextWindow(GW_HWNDNEXT))
{
if (pWnd == pFocusCombo)
{
// TODO: Sadly, by now, the control has already got all the text selected.
//pFocusCombo->SetEditSel(LOWORD(dwEditSel), HIWORD(dwEditSel));
}
else if (pWnd->IsKindOf(RUNTIME_CLASS(CComboBox)))
{
// This only works for combo boxes that are bound to controls
auto* pCombo = dynamic_cast<CComboBox*>(pWnd);
pCombo->SetEditSel(-1, -1);
}
else
{
CString strClassName;
if (::GetClassName(pWnd->GetSafeHwnd(), strClassName.GetBuffer(_MAX_PATH), _MAX_PATH))
{
if (strClassName == _T("ComboBox"))
{
auto* pCombo = (CComboBox*)pWnd;
//auto* pCombo = dynamic_cast<CComboBox*>(pWnd);
pCombo->SetEditSel(-1, -1);
}
}
strClassName.ReleaseBuffer();
}
}
if (m_pHtmlPreview != nullptr)
{
m_lblHtmlPreview.GetWindowRect(m_rctHtmlPreview);
ScreenToClient(m_rctHtmlPreview);
m_pHtmlPreview->MoveWindow(m_rctHtmlPreview);
}
}
I display the whole function for context. But I am specifically interested in this bit:
CString strClassName;
if (::GetClassName(pWnd->GetSafeHwnd(), strClassName.GetBuffer(_MAX_PATH), _MAX_PATH))
{
if (strClassName == _T("ComboBox"))
{
auto* pCombo = (CComboBox*)pWnd;
//auto* pCombo = dynamic_cast<CComboBox*>(pWnd);
pCombo->SetEditSel(-1, -1);
}
}
strClassName.ReleaseBuffer();
During code analysis updates I had many situations where I had to update C-Style casts. A lot of the time I was able to use static_cast, but, in some instances the compiler would then tell me I should use dynamic_cast.
I then found that my application was not working correctly and in debug mode isolated it to this bit:
//auto* pCombo = (CComboBox*)pWnd;
auto* pCombo = dynamic_cast<CComboBox*>(pWnd);
It turned out that the cast pointer pCombo was null. Yet, this never happens when I use the C-Style cast. As a result I have reverted to the C-Style cast. I saw this discussion (MFC Classes and C++ style casts) but I can't see that this is the reason (temporary pointers).
What cast should I be using that I can rely on in this situation?
C-style casting will try different c++ casting, it may choose reinterpret_cast static_cast. This is converting CWnd* to CComboBox*, for example:
CWnd* wnd = GetDlgItem(IDC_COMBO1);
CComboBox* combo = (CComboBox*)wnd;
In general, parent class can't "always" be converted to child. It depends if CComboBox m_combobox; for that ID was created.
A) m_combobox does exist:
In this case our wnd can be a reference to m_combobox.
CWnd::GetDlgItem etc. cast &m_combobox to CWnd*, they pass it around.
dynamic_cast checks it and converts back to CComboBox*.
MFC's IsKindOf will confirm if m_combobox was created.
B) m_combobox doesn't exist:
In this case our wnd is CWnd* object. MFC never created CComboBox for that control.
dynamic_cast tests it, can't convert to CComboBox*
static_cast works if wnd's classname is "ComboBox"
The code below should be okay. In this case you can skip dynamic_cast if you want, rely on static_cast. But it's better to use dynamic_cast if possible.
CComboBox* ptr = nullptr;
if (wnd->IsKindOf(RUNTIME_CLASS(CComboBox)))
ptr = dynamic_cast<CComboBox*>(wnd);
if(!ptr)
{
CString classname;
::GetClassName(wnd->m_hWnd, classname.GetBuffer(_MAX_PATH), _MAX_PATH);
classname.ReleaseBuffer();
if(classname == L"ComboBox")
ptr = static_cast<CComboBox*>(wnd);
}

Access violation reading location 0xCCCCCCCC in linked list

i was trying to make the linked list with the following code
typedef of string is char*
void insert_at_front(CourseList* self, String *course) {//inserting to the front
void insert_at_front(CourseList* self, String *course) {//inserting to the front
CourseNodePtr new = malloc(sizeof * new);
new->course=malloc(sizeof course+1);
strncpy_s(new->course, sizeof course+1, course, sizeof course+1);
new->students = new_bst();
new->next = self->head;
self->head = new;
}
how ever i got error Exception thrown at 0x66C5FF80 (ucrtbased.dll) in Project.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC when i am trying to enter any character or string in course. Just don't know what is wrong with the code.
update:
i have edited the code to this,it runs but nothing stored in course if i type in numbers. If i type in any characters, it loops and loops again.
void insert_at_front(CourseList* self, String* course) {//inserting to the front
CourseNodePtr new_node;
if (!(new_node = malloc(sizeof * new_node))) {
perror("malloc");
exit(1);
if (!(new_node->course = malloc(strlen(course) + 1))) {
perror("malloc");
exit(1);
}
strncpy_s(new_node->course, strlen(course) + 1, course, strlen(course) + 1);
new_node->students = new_bst();
new_node->next = self->head;
self->head = new_node;
}

How to get installed application path for executable in COM

I am trying to get the installed location of all application using COM. I am able to get the display name of each application. But I am not able to get installed path of each application.
MY Code:
CComPtr<IShellItem> spPrinters;
CoInitialize(nullptr);
HRESULT hresult = ::SHCreateItemFromParsingName(L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\8\\"
L"::{7B81BE6A-CE2B-4676-A29E-EB907A5126C5}", nullptr, IID_PPV_ARGS(&spPrinters));
CComPtr<IEnumShellItems> spEnum;
spPrinters->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&spEnum));
for (CComPtr<IShellItem> spProgram; spEnum->Next(1, &spProgram, nullptr) == S_OK; spProgram.Release())
{
CComHeapPtr<wchar_t> spszName;
spProgram->GetDisplayName(SIGDN_NORMALDISPLAY, &spszName);
CString cDisplayName = spszName;
}
Any idea how to get installed path from IEnumShellItems?
Here is a piece of code that will dump this out. The child's IPropertyStore does not return these, I don't know why, so we have to use the old
IShellFolder2::GetDetailsEx method with a special column id (which is the same as a PROPERTYKEY).
CComPtr<IShellItem> cpl;
CComPtr<IShellFolder2> folder;
CComPtr<IEnumShellItems> enumerator;
PROPERTYKEY pkLocation;
SHCreateItemFromParsingName(L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\8\\::{7B81BE6A-CE2B-4676-A29E-EB907A5126C5}", nullptr, IID_PPV_ARGS(&cpl));
// bind to IShellFolder
cpl->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARGS(&folder));
// bind to IEnumShellItems
cpl->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&enumerator));
// get this property key's value
PSGetPropertyKeyFromName(L"System.Software.InstallLocation", &pkLocation);
for (CComPtr<IShellItem> child; enumerator->Next(1, &child, nullptr) == S_OK; child.Release())
{
// get child's display name
CComHeapPtr<wchar_t> name;
child->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
wprintf(L"%s\n", name);
// get child's PIDL
CComHeapPtr<ITEMIDLIST> pidl;
SHGetIDListFromObject(child, &pidl);
// the PIDL is absolute, we need the relative one (the last itemId in the list)
// get it's install location
CComVariant v;
if (SUCCEEDED(folder->GetDetailsEx(ILFindLastID(pidl), &pkLocation, &v)))
{
// it's a VT_BSTR
wprintf(L" %s\n", v.bstrVal);
}
}
Note it's using an undocumented System.Software.InstallLocation PROPERTYKEY. To find it I just dumped all columns with a code like this for each child:
int iCol = 0;
do
{
SHCOLUMNID colId;
if (FAILED(folder->MapColumnToSCID(iCol, &colId)))
break; // last column
CComHeapPtr<wchar_t> name;
PSGetNameFromPropertyKey(colId, &name);
CComVariant v;
if (SUCCEEDED(folder->GetDetailsEx(ILFindLastID(pidl), &colId, &v)))
{
if (v.vt == VT_BSTR)
{
wprintf(L" %s: %s\n", name, v.bstrVal);
}
else
{
wprintf(L" %s vt: %i\n", name, v.vt);
}
}
iCol++;
} while (true);
}
PS: I've not added much error checking, but you should.

Waitformultipleobjects returns invalid handle

The code is below and it is part of a thread. pFileChange->m_hDirectory is of type HANDLE and pFileChange->m_eventFileChange if of type CEvent. CreateFile and ReadDirectoryChangesW return with a success. I am not able to figure out why i am getting an invalid handle status, please help!
UINT CFileChange::FileMontiorThread(LPVOID pArgs)
{
CFileChange* pFileChange = NULL;
pFileChange = (CFileChange*)pArgs;
pFileChange = (CFileChange*)pArgs;
CString str = pFileChange->m_strDirectory;
LPSTR strDirectory;
strDirectory = str.GetBuffer(str.GetLength());
PathRemoveFileSpec(strDirectory);
DWORD dwBytes = 0;
vector<BYTE> m_Buffer;
BOOL m_bChildren;
OVERLAPPED m_Overlapped;
HANDLE arrHandles[2] = { pFileChange->m_hDirectory, pFileChange->m_eventFileChange };
::ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
DWORD dwBufferSize = 16384;
m_Buffer.resize(dwBufferSize);
m_bChildren = false;
pFileChange->m_hDirectory = ::CreateFile(
(LPCSTR)(LPCTSTR)strDirectory,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED,
NULL);
if (pFileChange->m_hDirectory == INVALID_HANDLE_VALUE)
{
return false;
}
BOOL success = ::ReadDirectoryChangesW(
pFileChange->m_hDirectory, // handle to directory
&m_Buffer[0], // read results buffer
m_Buffer.size(), // length of buffer
m_bChildren, // monitoring option
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions
&dwBytes, // bytes returned
&m_Overlapped, // overlapped buffer
NULL); // no completion routine
DWORD dwWaitStatus;
while (!pFileChange->m_bKillThread)
{
dwWaitStatus = WaitForMultipleObjects(2, arrHandles, FALSE, INFINITE);
Switch(dwWaitStatus)
{
case WAIT_FAILED:
{
ULONG rc = 0;
rc = ::GetLastError();
LPVOID lpMsgBuf = NULL;
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
rc,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf,
0,
NULL);
CString strErrMsg;
strErrMsg.Format(_T("%s, %s, Reason:%s"), "", "", (LPTSTR)lpMsgBuf);
break;
}
}
}
return 0;
}
Note that, as it specifies in the documentation, you can't wait on any type of handle.
Waiting on a directory handle isn't going to do what you think it should. Read this related question and its answers for more information and background reading.
As it seems you're trying to create a folder monitor, perhaps read this blog post for the correct way to use ReadDirectoryChangesW.
The answer was provided by Hans Passant in a comment:
You copy the handles into arrHandles too soon, before they are created. That cannot work of course.

Check if Sections/Options exist when using `CWinApp::GetProfileString()`

Unlike the WriteProfileString method, the GetProfileString method does not offer a way to check if a section or an option even exists in the profile. All I can get is a default string. I am trying to implement a class representing my profile and I would like to have a
BOOL HasSection(CString sSection)
and a
BOOL HasOption(CString sSection, CString sOption)
method in it.
It is very easy to do. Please read the MSDN Article carefully. Notice that when you pass NULL to first or a second parameter, the function returns a list of all section names (in the former case) or keys (in the latter case).
So for you to implement HaseOption() method you would have a code similar to this one:
BOOL HasSection(CString sSection)
{
DWORD dwSize = 100, dwRequired;
LPTSTR lpBuffer = new TCHAR[dwSize];
BOOL bExists = FALSE;
dwRequired = ::GetProfileString(NULL, NULL, _T(""), lpBuffer, dwSize);
while(dwRequired == dwSize - 2)
{
// buffer is too small
delete [] lpBuffer;
dwSize = dwRequired + 100;
lpBuffer = new TCHAR[dwSize];
dwRequired = ::GetProfileString(NULL, NULL, _T(""), lpBuffer, dwSize);
}
if(dwRequired)
{
LPTSTR lpszFound = lpBuffer;
do
{
if(sSection.CompareNoCase(lpszFound) == 0)
{
bExists = TRUE; break;
}
} while(*(lpszFound = _tcsninc(lpszFound, _tcsnbcnt(lpszFound, dwRequired)+1)));
}
delete [] lpBuffer;
return bExists;
}
I have not tested the code, just cut-n-pasted from my program and modified a bit. Please test the code yourself

Resources