1,222
社区成员




LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static HWND hComboBox;
switch (message)
{
case WM_CREATE:
hComboBox = CreateWindow(WC_COMBOBOX,
TEXT(""),
CBS_DROPDOWN | WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_SORT,
90, 23, 130, 400,
hwnd, NULL, NULL, NULL);
static TCHAR Folder[MAX_PATH];
WIN32_FIND_DATA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
DWORD dwError = 0;
HRESULT hr = SHGetFolderPathW(0, CSIDL_MYDOCUMENTS, 0, 0, Folder);
if (SUCCEEDED(hr))
{
StringCchCat(Folder, MAX_PATH, TEXT("\\work"));
if (DirectoryExists(Folder))
{
StringCchCat(Folder, MAX_PATH, TEXT("\\*"));
hFind = FindFirstFile(Folder, &ffd);
}
else
{
MessageBox(hwnd, TEXT("directory does not exist."), TEXT("error"), MB_ICONERROR);
}
if (INVALID_HANDLE_VALUE == hFind)
{
return 0;
}
do
{
if (ffd.cFileName[0] == '.') continue; // 跳过当前目录“.”、上一级目录“..”和隐藏目录
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 只要目录,不要文件
{
BOOL onlyDigit = TRUE;
for (int i = 0; i < sizeof(ffd.cFileName); i++)
{
if (ffd.cFileName[i] < '0' || ffd.cFileName[i] > '9')
{
onlyDigit = FALSE;
}
}
if (onlyDigit)
{
// 将只有数字的目录名称放到下拉列表框里面
SendMessage(hComboBox, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)ffd.cFileName);
}
}
}
while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
}
else
{
MessageBox(hwnd, TEXT("Failed to get special folder."), TEXT("ERROR"), MB_ICONERROR);
}
[其它代码片段]
if (ffd.cFileName[0] == '.') continue;
来过滤掉当前目录“.”、上一级目录“.”和隐藏目录,然后用if (ffd.cFileName[i] < '0' || ffd.cFileName[i] > '9')
遍历目录名称中的每个字符,如果含有非数字字符,就跳过该目录名称,处理下一个目录名称。 _Field_z_ WCHAR cFileName[ MAX_PATH ];
其中 MAX_PATH = 260,也就是说 ffd.cFileName 是一个长度为 260 个字符的宽字符数组,每一个宽字符都是 2 字节的 Unicode 字符,所以 sizeof(ffd.cFileName) 的值是 520。
sizeof 返回的总是括号中的总字节数,所以 Ln49 其实已经发生了数组下标越界。当时编译器报下标越界的时候我还纠结半天,不明白为什么会越界,原来是因为在做 < '\0' 和 > '\9' 这样的判断的时候总是容易按 ASCII 的方式去思考,就以为 sizeof(ffd.cFileName) 的值等于 260。虽然 VS 里面程序正常运行。
问题就发生在这个 for 循环里面。ffd.cFileName 是一个 Null-terminated string,当碰到“10086”这样的目录名称时,ffd.cFileName 的具体值如下:
ffd.cFileName[0] = '1';
ffd.cFileName[1] = '0';
ffd.cFileName[2] = '0';
ffd.cFileName[3] = '8';
ffd.cFileName[4] = '6';
ffd.cFileName[5] = '\0';
ffd.cFileName[6] = [initialize value];
...
...
...
ffd.cFileName[258] = [initialize value];
ffd.cFileName[259] = [initialize value];
当 for 循环中的 i = 5 时,Ln52 中的 onlyDigit = FALSE 就被执行了。接着,i 继续增大,for 循环继续执行,onlyDigit 的值会一直是FALSE,直到循环结束。所以这就是为什么对于纯数字的目录名称,程序也没有将其放到ComboBox里面。
当然,对于非纯数字的目录名称,上述for循环是没有问题的。
找到原因后,我重新写了代码,在 ffd.cFileName 中 加入了对 NULL 字符的处理,程序终于正常运行了:
[其它代码片段]
do
{
if (ffd.cFileName[0] == '.') continue;
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
BOOL IsNumericString = TRUE;
for (int i = 0; i < 260; i++)
{
if (ffd.cFileName[i] == '\0')
{
break;
}
else
{
if ((ffd.cFileName[i] < '0') || (ffd.cFileName[i] > '9'))
{
IsNumericString = FALSE;
break;
}
}
}
if (IsNumericString == TRUE)
{
SendMessage(hComboBox, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)ffd.cFileName);
}
}
}
while (FindNextFile(hFind, &ffd) != 0);
[其它代码片段]
在这里再次感谢大佬!