Hi, I'm using the following code to capture all files in a directory -
but when I pass the "path" var such as "C:\SomeDir" ffd.cFileName is just simply "SomeDir" and is seen as a valid folder and no more files are processed.. I'm trying to get the contents in that folder. Where am I going wrong? I'm following this example here - http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
// Prepare string for use with FindFile functions. First, copy the
// string to a buffer, then append '\*' to the directory name.
StringCchCopy(szDir, MAX_PATH, argv[1]);
StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
Theres no problem with that. i was just wondering why you cant append with a simple plus sign
Because TCHAR* is just a pointer. Adding some value to it means adding to the pointer!
If you want to use "a simple plus sign" to add some character text then you have to use some string class like std::string/wstring or MFC CString class.
but when I use it, I have a bunch of valid file names after the ListFiles call, but a few lines later, all the values are junk such as "iiiiiiiiiiiiiiiiiiiiiii", what could cause it?
Code:
if (ListFiles(dir, files)) {
for (std::vector<TCHAR *>::iterator it = files.begin();
it != files.end();
++it) {
TCHAR* text = new TCHAR[_MAX_PATH];
GetWindowText(textArea, text, _MAX_PATH);
StringCchCat(text, MAX_PATH, *it);
SetWindowText(textArea,text);
}
}
but when I use it, I have a bunch of valid file names after the ListFiles call, but a few lines later, all the values are junk such as "iiiiiiiiiiiiiiiiiiiiiii", what could cause it?
First, what is "textArea"?
Second, you changed "ListFiles". Don't you think it makes a difference we see what this new ListFiles() consists of.
Third, your code has a lot wrong with it.
1) It has memory leaks. You call "new[]" and nowhere do you call "delete[]" to release the memory.
2) Why did you even introduce "new[]" in the first place, while you're using vector? A vector is supposed to relieve you from using new[]/delete[] since it is a dynamic array container, but you're not using vector for the purpose for which it was intended for. If you're using std::vector, but at the same time using new[]/delete[], then it's time to take a step back and look carefully at what you're doing, since somewhere you're making a design mistake, misstep, or oversight (unless you're a C++ pro and you have a good reason to be using both).
Things like this:
Code:
TCHAR* text = new TCHAR[_MAX_PATH];
are totally unnecessary. Not only that, you're doing this in a loop and creating huge memory leaks without a call to delete[].
Moreover, why even use dynamically allocated memory anyway, when you know the size of the array? It isn't even dynamic. So you're needlessly slowing your code down by calling the allocator and you're creating a memory leak with the code you have now.
Code:
TCHAR text[_MAX_PATH];
All you need is a TCHAR array that has _MAX_PATH elements. You make this mistake in this new code and the original code you have at the top of the thread.
If _MAX_PATH were some huge number into the millions, then maybe that would be reason to allocate dynamically. But since _MAX_PATH is something very small, just create a normal, non-dynamic array of _MAX_PATH size. Even if you need to allocate dynamically, std::vector<> should have been used instead of new[].
Since it looks like you're using Visual C++, you could have just used CString (if VC++ is higher than 6.0, since CString is not tied to MFC in higher versions of VC++), or a level down from that, std::string/std::wstring and choose the right one depending on build type (Unicode/ANSI). Then replace all of those TCHAR* with CString or std::(w)string.
The bottom line being that you shouldn't need to write code that ties you up in knots with respect to memory allocation. The code should be immune to memory leaks even if an exception is thrown, and using things such as CString/std::(wstring) ensures this due to the destructors of these classes cleaning up themselves automatically.
Code:
#include <vector>
#include <string>
// use the right string class for the build type
#ifndef UNICODE
typedef std::string StringClass;
#else
typedef std::wstring StringClass;
#endif
typedef std::stack<StringClass> StringStack;
typedef std::vector<StringClass> StringVector;
//...
bool ListFiles(const StringClass& path, StringVector &files)
{
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
static StringStack directories;
LPTSTR szANSIString[MAX_PATH] = {0};
hFind = FindFirstFile(path, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
return false;
do {
if (_tcscmp(ffd.cFileName, _T(".")) != 0 &&
_tcscmp(ffd.cFileName, _T("..")) != 0)
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
directories.push(ffd.cFileName);
}
else {
files.push_back(ffd.cFileName);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return false;
}
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
return true;
}
//...
for (StringVector::iterator it = files.begin();
it != files.end();
++it)
{
TCHAR text[_MAX_PATH];
GetWindowText(textArea, text, _MAX_PATH);
StringCchCat(text, MAX_PATH, *it);
SetWindowText(textArea,text);
}
}
Also, hopefully you know that your first ListFiles functions is not thread-safe due to the static types used in the function.
Regards,
Paul McKenzie
Last edited by Paul McKenzie; January 10th, 2013 at 02:29 AM.
yes, sorry. I'm just trying to understand TCHAR and it's correct usage for the most part. Is it possible to make a vector containing a TCHAR[] array or is that just a no no? I tried Paul's StringClass suggestion, but how do I convert a StringVector to a LPSTR when needed?
Last edited by drwbns; January 10th, 2013 at 08:55 AM.
yes, sorry. I'm just trying to understand TCHAR and it's correct usage for the most part. Is it possible to make a vector containing a TCHAR[] array
Where in your code do you need an LPSTR? All I see in your code is to gather the filenames.
Secondly, StringVector is a vector of CString/std::(wstring). It isn't just one string, so there is no such thing as converting a StringVector to an LPSTR. What is possible is to take one of the stirngs in the StringVector and return an LPCTSTR -- that's why I asked why you need LPSTR instead of LPCTSTR.
dir is a StringVector like you suggested - but I'm getting a crash after calling buttonPush -
Code:
bool buttonPush(HWND hWnd, StringVector &dir ) {
BROWSEINFO bi;
TCHAR szDir[MAX_PATH];
LPITEMIDLIST pidl;
LPMALLOC pMalloc;
if (SUCCEEDED(SHGetMalloc(&pMalloc)))
{
ZeroMemory(&bi,sizeof(bi));
bi.hwndOwner = hWnd;
bi.pszDisplayName = 0;
bi.pidlRoot = 0;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON;
bi.pszDisplayName = szDir; // Address of a buffer to receive the display name of the folder selected by the user
bi.lpszTitle = _T("Select a folder"); // Title of the dialog
bi.lpfn = BrowseCallbackProc;
pidl = SHBrowseForFolder(&bi);
if (pidl)
{
BOOL bRet = ::SHGetPathFromIDList(pidl, const_cast<char *>(dir[0].c_str())); // dir gets a bad ptr here, how can I do this with a StringVector?
pMalloc->Free(pidl);
pMalloc->Release();
if(bRet) return true;
}
}
return false;
}
Bookmarks