您犯的最大错误是将 ANSI 和 Unicode 混合在一起。您正在使用char[] 缓冲区并将它们类型转换为LPWSTR 指针,以便将它们分配给OPENFILENAME 字段。由于您使用的是 API 的TCHAR 版本,这意味着您的项目正在为 Unicode 而不是 ANSI 编译。因此,API 需要 Unicode 缓冲区,并将输出 Unicode 字符串。这也意味着您告诉 API 两次 您的缓冲区分配的空间可用于接收字符,因为您将 ofn.nMaxFile 和 ofn.nMaxFileTitle 字段设置为 byte计数而不是 字符 计数。所以你可能会导致缓冲区溢出。
您不能仅仅将 8 位缓冲区类型转换为 16 位数据类型。您必须首先为缓冲区使用正确的数据类型,并摆脱类型转换。在这种情况下,这意味着使用WCHAR/wchar_t(或至少TCHAR)缓冲区而不是char 缓冲区。但是,由于您在函数参数中使用 char,因此您应该使用 API 的 ANSI 版本而不是 TCHAR/Unicode 版本。
在选择多个文件时,尤其是具有长文件名的文件,结果字符数据可以容易地增长超出固定长度缓冲区的大小。正如OPENFILENAME documentation 所说:
lpstr文件
类型:LPTSTR
用于初始化文件名编辑控件的文件名。如果不需要初始化,此缓冲区的第一个字符必须为 NULL。当GetOpenFileName 或GetSaveFileName 函数成功返回时,此缓冲区包含所选文件的驱动器标识符、路径、文件名和扩展名。
如果设置了OFN_ALLOWMULTISELECT 标志并且用户选择了多个文件,则缓冲区包含当前目录,后跟所选文件的文件名。对于资源管理器样式的对话框,目录和文件名字符串以 NULL 分隔,最后一个文件名后有一个额外的 NULL 字符。对于旧式对话框,字符串以空格分隔,函数使用短文件名作为带空格的文件名。您可以使用FindFirstFile 函数在长文件名和短文件名之间进行转换。如果用户只选择一个文件,lpstrFile 字符串在路径和文件名之间没有分隔符。
如果缓冲区太小,函数返回FALSE,CommDlgExtendedError 函数返回FNERR_BUFFERTOOSMALL。在这种情况下,lpstrFile 缓冲区的前两个字节包含所需的大小,以字节或字符为单位。
nMaxFile
类型:DWORD
lpstrFile 指向的缓冲区的大小(以字符为单位)。缓冲区必须足够大以存储路径和文件名字符串或字符串,包括终止 NULL 字符。 如果缓冲区太小而无法包含文件信息,GetOpenFileName 和 GetSaveFileName 函数将返回 FALSE。缓冲区的长度应至少为 256 个字符。
你没有考虑到这一点。 256(最好使用 260,又名MAX_PATH)可以用于选择单个文件,但可能不适用于选择多个文件。如果GetOpenFileName() 失败并返回FNERR_BUFFERTOOSMALL,您将不得不重新分配缓冲区并再次调用GetOpenFileName()。
话虽如此,请尝试更多类似的东西:
BOOL OpenBmpFiles(char **filePath, char** fileNames, HWND hwnd)
{
*filePath = NULL;
*fileNames = NULL;
size_t iMaxFileSize = MAX_PATH;
char *lpFileBuffer = (char*) malloc(iMaxFileSize);
if (!lpFileBuffer)
return FALSE;
char szFileTitle[MAX_PATH];
BOOL bResult = FALSE;
OPENFILENAMEA ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = "Bitmap Files (*.bmp)\0*.bmp\0\0";
ofn.nFilterIndex = 1;
ofn.lpstrFile = lpFileBuffer;
ofn.nMaxFile = iMaxFileSize;
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrTitle = "Open BMP File";
ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_LONGNAMES | OFN_ALLOWMULTISELECT | OFN_EXPLORER;
do
{
//
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
// use the contents of lpstrFile to initialize itself.
//
ofn.lpstrFile[0] = '\0';
// show the common dialog "Open BMP File"
if (GetOpenFileNameA(&ofn))
break;
// if cancel, exit
if (CommDlgExtendedError() != FNERR_BUFFERTOOSMALL)
goto cleanup;
// reallocate the buffer and try again
iMaxFileSize = * (WORD*) lpFileBuffer;
char *lpNewFileBuffer = (char*) realloc(lpFileBuffer, iMaxFileSize);
if (!lpNewFileBuffer)
goto cleanup;
lpFileBuffer = lpNewFileBuffer;
ofn.lpstrFile = lpFileBuffer;
ofn.nMaxFile = iMaxFileSize;
}
while (true);
if (lpFileBuffer[ofn.nFileOffset-1] != '\0')
{
MessageBox(hwnd, TEXT("Single Selection"), TEXT("Open Debug 1"), MB_OK);
// copy the single filename and make sure it is double-null terminated
size_t len = strlen(&lpFileBuffer[ofn.nFileOffset]) + 2;
*fileNames = (char*) malloc(len);
if (!*fileNames)
goto cleanup;
strncpy(*fileNames, &lpFileBuffer[ofn.nFileOffset], len);
// copy the directory path and make sure it is null terminated
lpFileBuffer[ofn.nFileOffset] = '\0';
*filePath = strdup(lpFileBuffer);
if (!*filePath)
{
free(*fileNames);
*fileNames = NULL;
goto cleanup;
}
}
else
{
MessageBox(hwnd, TEXT("Multiple Selection"), TEXT("Open Debug 2"), MB_OK);
// copy the directory path, it is already null terminated
*filePath = strdup(lpFileBuffer);
if (!*filePath)
goto cleanup;
// copy the multiple filenames, they are already double-null terminated
size_t len = (ofn.nMaxFile - ofn.nFileOffset);
*fileNames = (char*) malloc(len);
if (!*fileNames)
{
free(*filePath);
*filePath = NULL;
goto cleanup;
}
// have to use memcpy() since the filenames are null-separated
memcpy(*fileNames, &lpFileBuffer[ofn.nFileOffset], len);
}
bResult = TRUE;
cleanup:
free(lpFileBuffer);
return bResult;
}
那么你可以这样使用它:
char *path, *filenames;
if (OpenBmpFiles(&path, &filenames, hwnd))
{
char *filename = filenames;
do
{
// use path + filename as needed...
/*
char *fullpath = (char*) malloc(strlen(path)+strlen(filename)+1);
PathCombineA(fullpath, path, filename);
doSomethingWith(fullpath);
free(fullpath);
*/
filename += (strlen(filename) + 1);
}
while (*filename != '\0');
free(path);
free(filenames);
}
更新:或者,为了简化返回文件名的使用,您可以执行更多类似的操作:
BOOL OpenBmpFiles(char** fileNames, HWND hwnd)
{
*fileNames = NULL;
size_t iMaxFileSize = MAX_PATH;
char *lpFileBuffer = (char*) malloc(iMaxFileSize);
if (!lpFileBuffer)
return FALSE;
char szFileTitle[MAX_PATH];
BOOL bResult = FALSE;
OPENFILENAMEA ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = "Bitmap Files (*.bmp)\0*.bmp\0\0";
ofn.nFilterIndex = 1;
ofn.lpstrFile = lpFileBuffer;
ofn.nMaxFile = iMaxFileSize;
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrTitle = "Open BMP File";
ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_LONGNAMES | OFN_ALLOWMULTISELECT | OFN_EXPLORER;
do
{
//
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
// use the contents of lpstrFile to initialize itself.
//
ofn.lpstrFile[0] = '\0';
// show the common dialog "Open BMP File"
if (GetOpenFileNameA(&ofn))
break;
// if cancel, exit
if (CommDlgExtendedError() != FNERR_BUFFERTOOSMALL)
goto cleanup;
// reallocate the buffer and try again
iMaxFileSize = * (WORD*) lpFileBuffer;
char *lpNewFileBuffer = (char*) realloc(lpFileBuffer, iMaxFileSize);
if (!lpNewFileBuffer)
goto cleanup;
lpFileBuffer = lpNewFileBuffer;
ofn.lpstrFile = lpFileBuffer;
ofn.nMaxFile = iMaxFileSize;
}
while (true);
if (lpFileBuffer[ofn.nFileOffset-1] != '\0')
{
MessageBox(hwnd, TEXT("Single Selection"), TEXT("Open Debug 1"), MB_OK);
// copy the single filename and make sure it is double-null terminated
size_t len = strlen(lpFileBuffer) + 2;
*fileNames = (char*) malloc(len);
if (!*fileNames)
goto cleanup;
strncpy(*fileNames, lpFileBuffer, len);
}
else
{
MessageBox(hwnd, TEXT("Multiple Selection"), TEXT("Open Debug 2"), MB_OK);
// calculate the output buffer size
char *path = lpFileBuffer;
size_t pathLen = strlen(path);
bool slashNeeded = ((path[pathLen-1] != '\\') && (path[pathLen-1] != '/'));
size_t len = 1;
char *filename = &lpFileBuffer[ofn.nFileOffset];
while (*filename != '\0')
{
int filenameLen = strlen(filename);
len += (pathLen + filenameLen + 1);
if (slashNeeded) ++len;
filename += (filenameLen + 1);
}
// copy the filenames and make sure they are double-null terminated
*fileNames = (char*) malloc(len);
if (!*fileNames)
goto cleanup;
char *out = *fileNames;
filename = &lpFileBuffer[ofn.nFileOffset];
while (*filename != '\0')
{
strncpy(out, path, pathLen);
out += pathLen;
if (slashNeeded) *out++ = '\\';
int filenameLen = strlen(filename);
strncpy(out, filename, filenameLen);
out += filenameLen;
*out++ = '\0';
filename += (filenameLen + 1);
}
*out = '\0';
}
bResult = TRUE;
cleanup:
free(lpFileBuffer);
return bResult;
}
char *filenames;
if (OpenBmpFiles(&filenames, hwnd))
{
char *filename = filenames;
do
{
// use filename as needed...
/*
doSomethingWith(filename);
*/
filename += (strlen(filename) + 1);
}
while (*filename != '\0');
free(filenames);
}