64位Windows API结构对齐导致命名管道上出现“拒绝访问”错误
64-bit Windows API struct alignment caused Access Denied error on named pipe
我花了两天时间,但在尝试将CallNamedPipe
作为结构对齐问题时,我终于缩小了ERROR_ACCESS_DENIED
(5)错误的来源。我们有32位服务和32位应用程序,我正在尝试将服务更新为64位服务。奇怪的是,在32位模式下一切正常,但在64位模式下,32位应用程序的CallNamedPipe
报告了一个拒绝访问错误。
该服务已经在设置SECURITY_ATTRIBUTES
结构,并用正确初始化的PSECURITY_DESCRIPTOR
填充lpSecurityDescriptor
成员。当传递给CreateNamedPipe
时,它没有报告任何错误。我仍然不知道为什么它没有报告错误;也许坏的安全属性会自动返回到默认值,而不是失败。
通过多次循环(包括之前更改结构对齐的一些不完整/不正确的尝试-调试服务启动代码并不容易),我意识到将默认结构对齐设置为1字节(/Zp1)的项目设置造成了问题。当我最终在#include <windows.h>
和#pragma pack(pop)
出现之前使用#pragma pack(push,8)
时,事情就开始运转了。
我现在的问题是为什么这是必要的。我看到Windows API中有许多头文件通过包括pshpack1.h
、pshpack2.h
、pshpack4.h
、pshpack8.h
和poppack.h
来显式设置结构对齐。我如何知道Windows API何时控制自己的打包,以及何时设置正确的打包级别成为我的责任?难道每一个关心结构对齐的Windows API声明都不应该设置正确的打包,这样我就不必在系统中筛选包括Windows API头文件在内的所有代码了吗?一种替代方案是将项目设置更改为使用默认结构对齐,但我必须假设这样做是因为我们的系统中依赖1字节结构对齐的代码比依赖Windows API的代码还要多。
这就是服务器端代码的样子:
BOOL OpenMyPipe()
{
SECURITY_ATTRIBUTES sa;
PSECURITY_DESCRIPTOR pSD;
printf("sizeof(SECURITY_ATTRIBUTES) == %dn", sizeof(SECURITY_ATTRIBUTES));
pSD = (PSECURITY_DESCRIPTOR)GlobalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pSD == NULL)
return FALSE;
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
return FALSE;
if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL)NULL, FALSE))
return FALSE;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
char szPipeName[MAX_PATH];
sprintf(szPipeName, "\\.\pipe\%s%s", "__SQLTST_",
"MAINMR");
hPipe = CreateNamedPipe(szPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1, 0, 0, NMPWAIT_WAIT_FOREVER, &sa);
if (hPipe == INVALID_HANDLE_VALUE)
return FALSE;
return TRUE;
}
为了简单起见,我用一个小型VB.NET客户端验证了这一点:
Sub Main()
Dim pipes = System.IO.Directory.GetFiles("\.pipe")
Using pipe As New System.IO.Pipes.NamedPipeClientStream(".", "__SQLTST_MAINMR")
Dim message(16) As Byte
pipe.Connect(3000)
Array.Copy(BitConverter.GetBytes(Process.GetCurrentProcess().Id), message, 4)
pipe.Write(message, 0, 16)
End Using
End Sub
我相信只有当服务器端代码在不同的帐户(如SYSTEM帐户)下运行时才会出现错误。不过,我不知道如何简单地测试它。我所知道的是,当上面的代码与常规应用程序代码在同一帐户下运行时,即使没有设置SECURITY_ATTRIBUTES
,它也不会失败。当然,您还必须将服务器代码中的结构对齐设置为1字节才能看到错误。
Windows SDK预计打包为8字节。从使用Windows标头
项目应该编译为使用默认的结构打包,目前是8字节,因为最大的整型是8字节。这样做可以确保头文件中的所有结构类型都以与Windows API期望的相同对齐方式编译到应用程序中。它还确保具有8字节值的结构正确对齐,并且不会在执行数据对齐的处理器上导致对齐错误。
这对于确保数据结构按照系统预期进行调整是必要的。我怀疑没有明确这样做的原因是他们想要默认值,所以为什么要要求其他东西呢。更换包装的情况相对较少,应仅限于特定情况。如果微软将#pragma pack(push,8)
添加到每个头文件中,他们就会含蓄地说更改对齐方式是正常的。
未对齐的结构可以节省空间,但会降低性能,因为访问未对齐的成员时会生成对齐错误。
Windows SDK确实会更改结构的对齐方式,原因有很多。一种可能是需要读取32位或64位数据结构的文件格式。例如,PE文件格式可以使用IMAGE_THUNK_DATA64
或IMAGE_THUNK_DATA32
读取。前者需要8字节的填充,而后者需要4字节的填充。类似地,Wininet.h
将根据它是针对32位代码还是64位代码进行编译而对数据结构进行不同的打包。这些都是合理的包装变化,但有特定的原因。
- 访问被拒绝后,c++中的故障保护代码
- 删除目录函数访问被拒绝
- 为什么 Windows 拒绝访问某些进程的名称?
- 我在执行任何程序时被拒绝在 devcpp 中访问
- Windows C++:文件夹移动访问被拒绝错误
- AWS AMI 中的 Windows DPAPI 失败,访问被拒绝
- C++ 17 文件系统copy_file访问被拒绝
- 如何在 bash 中使用管道在 c++ 中使用 getenv() 访问文件?
- mysql c ++连接器异常:用户"root"@'localhost'的访问被拒绝(使用密码:NO)&&MySQL服务器已消失
- 任务计划程序:设置运行级别时拒绝访问
- 清除同一属性后,设置属性时拒绝访问
- 创建注册表密钥 - 拒绝访问
- 64位Windows API结构对齐导致命名管道上出现“拒绝访问”错误
- RegLoadKey给出错误代码5(拒绝访问)
- 在正常应用程序中,当信号量第一次由服务创建时,它被拒绝访问
- 当发生错误(拒绝访问)时调用Curl-headerfunction和write函数
- 在EnumProcessModules上拒绝访问- c++
- 无法启动程序:拒绝访问
- 拒绝访问nt权限本地服务
- OpenFileMapping在某些前任中被拒绝访问