为什么 CreateFileA 仅在可执行文件为 Visual Studio 下的 运行 时失败?
Why is CreateFileA failing only when the executable is run under Visual Studio?
我使用 WinAPI 编写了一个简单的 check_file_ref
函数来检查两个路径是否引用同一个文件。代码很好。它是用 Visual Studio 2017 在 C 中编译的(标志 /TC
)。
奇怪的是CreateFileA
(winapi)在Visual Studio下的可执行文件运行时总是失败(即执行不调试),返回一个无效的文件句柄。否则,当可执行文件位于 Visual Studio.
之外的 运行 时,它会按预期工作
工作目录在任何情况下都是相同的。
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
/**
* Check whether two paths reference the same file.
* @param p1 File path 1.
* @param p2 File path 2.
* @return 0 same file, 1 different files, < 0 on failure.
*/
int
check_file_ref(char const * p1, char const * p2)
{
if ( !p1 || !p2 )
return -1;
if ( p1 == p2 )
return 0;
if ( strcmp(p1, p2) == 0 )
return 0;
int ret;
DWORD share, flags;
HANDLE f1, f2;
BY_HANDLE_FILE_INFORMATION s1, s2;
ret = -1;
share = FILE_SHARE_READ | FILE_SHARE_WRITE;
flags = FILE_ATTRIBUTE_NORMAL;
f1 = CreateFileA(p1, GENERIC_READ, share, NULL, OPEN_EXISTING, flags, NULL);
f2 = CreateFileA(p2, GENERIC_READ, share, NULL, OPEN_EXISTING, flags, NULL);
if ( f1 == INVALID_HANDLE_VALUE ) {
fprintf(stderr, "Could not open %s file, error %d\n", p1, GetLastError());
goto cleanup;
}
if ( f2 == INVALID_HANDLE_VALUE ) {
fprintf(stderr, "Could not open %s file, error %d\n", p2, GetLastError());
goto cleanup;
}
if ( GetFileInformationByHandle(f1, &s1) == 0 ) {
fprintf(stderr, "Could not get %s file information, error %d\n", p1, GetLastError());
goto cleanup;
}
if ( GetFileInformationByHandle(f2, &s2) == 0 ) {
fprintf(stderr, "Could not get %s file information, error %d\n", p2, GetLastError());
goto cleanup;
}
/*
The identifier (low and high parts) and the volume serial number uniquely
identify a file on a single computer. To determine whether two open handles
represent the same file, combine the identifier and the volume serial number
for each file and compare them.
See
https://docs.microsoft.com/fr-fr/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle
https://docs.microsoft.com/fr-fr/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information
*/
ret = !(s1.dwVolumeSerialNumber == s2.dwVolumeSerialNumber
&& s1.nFileIndexLow == s2.nFileIndexLow
&& s1.nFileIndexHigh == s2.nFileIndexHigh);
cleanup:
CloseHandle(f2);
CloseHandle(f1);
return ret;
}
int main()
{
int ret;
char workingDir[256];
/* Both paths reference the same file.
One is relative, the other absolute. */
char * p1 = "hello.txt";
char * p2 = "C:/Users/bro/source/repos/tests/x64/Debug/hello.txt";
ret = GetModuleFileNameA(NULL, workingDir, sizeof(workingDir));
printf("working dir: %s\n", (ret == 0 ? "err" : workingDir));
/* Should return 0. */
ret = check_file_ref(p1, p2);
printf("p1: %s\n", p1);
printf("p2: %s\n", p2);
printf("check_file_ret ret %d ", ret);
if ( ret == 0 ) printf("(same file)\n");
else if ( ret == 1 ) printf("(different files)\n");
else printf("(error)\n");
return 0;
}
可视化下可执行运行:
CreateFileA fails
可直接从命令行 运行 执行:
CreateFileA works
为什么 CreateFileA
仅当可执行文件是 Visual Studio 下的 运行 时才会失败?
当您 运行 来自 VisualStudio 的应用程序时,默认工作目录是项目位置,它由 VS 宏 $(ProjectDir)
定义。您可以通过打开项目属性来更改它:从上下文菜单中右键单击 Solution Explorer 和 select Properties 中的项目。有selectDebugging和更改Working Directory 属性。例如,在您的情况下,解决方案可能是 $(OutDir)
或所需位置的绝对路径。
我使用 WinAPI 编写了一个简单的 check_file_ref
函数来检查两个路径是否引用同一个文件。代码很好。它是用 Visual Studio 2017 在 C 中编译的(标志 /TC
)。
奇怪的是CreateFileA
(winapi)在Visual Studio下的可执行文件运行时总是失败(即执行不调试),返回一个无效的文件句柄。否则,当可执行文件位于 Visual Studio.
工作目录在任何情况下都是相同的。
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
/**
* Check whether two paths reference the same file.
* @param p1 File path 1.
* @param p2 File path 2.
* @return 0 same file, 1 different files, < 0 on failure.
*/
int
check_file_ref(char const * p1, char const * p2)
{
if ( !p1 || !p2 )
return -1;
if ( p1 == p2 )
return 0;
if ( strcmp(p1, p2) == 0 )
return 0;
int ret;
DWORD share, flags;
HANDLE f1, f2;
BY_HANDLE_FILE_INFORMATION s1, s2;
ret = -1;
share = FILE_SHARE_READ | FILE_SHARE_WRITE;
flags = FILE_ATTRIBUTE_NORMAL;
f1 = CreateFileA(p1, GENERIC_READ, share, NULL, OPEN_EXISTING, flags, NULL);
f2 = CreateFileA(p2, GENERIC_READ, share, NULL, OPEN_EXISTING, flags, NULL);
if ( f1 == INVALID_HANDLE_VALUE ) {
fprintf(stderr, "Could not open %s file, error %d\n", p1, GetLastError());
goto cleanup;
}
if ( f2 == INVALID_HANDLE_VALUE ) {
fprintf(stderr, "Could not open %s file, error %d\n", p2, GetLastError());
goto cleanup;
}
if ( GetFileInformationByHandle(f1, &s1) == 0 ) {
fprintf(stderr, "Could not get %s file information, error %d\n", p1, GetLastError());
goto cleanup;
}
if ( GetFileInformationByHandle(f2, &s2) == 0 ) {
fprintf(stderr, "Could not get %s file information, error %d\n", p2, GetLastError());
goto cleanup;
}
/*
The identifier (low and high parts) and the volume serial number uniquely
identify a file on a single computer. To determine whether two open handles
represent the same file, combine the identifier and the volume serial number
for each file and compare them.
See
https://docs.microsoft.com/fr-fr/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle
https://docs.microsoft.com/fr-fr/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information
*/
ret = !(s1.dwVolumeSerialNumber == s2.dwVolumeSerialNumber
&& s1.nFileIndexLow == s2.nFileIndexLow
&& s1.nFileIndexHigh == s2.nFileIndexHigh);
cleanup:
CloseHandle(f2);
CloseHandle(f1);
return ret;
}
int main()
{
int ret;
char workingDir[256];
/* Both paths reference the same file.
One is relative, the other absolute. */
char * p1 = "hello.txt";
char * p2 = "C:/Users/bro/source/repos/tests/x64/Debug/hello.txt";
ret = GetModuleFileNameA(NULL, workingDir, sizeof(workingDir));
printf("working dir: %s\n", (ret == 0 ? "err" : workingDir));
/* Should return 0. */
ret = check_file_ref(p1, p2);
printf("p1: %s\n", p1);
printf("p2: %s\n", p2);
printf("check_file_ret ret %d ", ret);
if ( ret == 0 ) printf("(same file)\n");
else if ( ret == 1 ) printf("(different files)\n");
else printf("(error)\n");
return 0;
}
可视化下可执行运行:
CreateFileA fails
可直接从命令行 运行 执行:
CreateFileA works
为什么 CreateFileA
仅当可执行文件是 Visual Studio 下的 运行 时才会失败?
当您 运行 来自 VisualStudio 的应用程序时,默认工作目录是项目位置,它由 VS 宏 $(ProjectDir)
定义。您可以通过打开项目属性来更改它:从上下文菜单中右键单击 Solution Explorer 和 select Properties 中的项目。有selectDebugging和更改Working Directory 属性。例如,在您的情况下,解决方案可能是 $(OutDir)
或所需位置的绝对路径。