Event事件的访问问题
做个本地UDP通讯, 涉及到大数据分包传送, 使用Event来通知数据包收到状态
测试程序很正常, 发送端CreateEvent, 接收端SetEvent
可是到了实际项目里却出现无法访问, 错误代码2(找不到文件), 查看资料发现因为项目发送端是Service, 接收端是桌面程序, 不再同一用户范围, 导致找不到事件对象
MSDN关于CreateEvent描述: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa
关于命名空间的描述: https://docs.microsoft.com/zh-cn/windows/win32/termserv/kernel-object-namespaces
根据资料描述, 要在事件名前面加"Global\"以定义事件是全局对象
var lEventHandle: THandle; lEventName: string; begin lEventName := TGUID.NewGuid.ToString; lEventHandle := CreateEvent(nil, False, False, PChar('Global\' + lEventName)); ... WaitForSingleObject(lEventHandle, 1000) CloseHandle(lEventHandle); end;
改完以后测试, 发现错误2解决了, 但是出现另一个问题, 错误代码5, 拒绝访问, 继续查资料, 发现还是不同用户的问题, 由于Service运行于system账户, 权限高于用户账户, 所以用户账户进程无法访问
关于权限说明资料: https://docs.microsoft.com/zh-cn/previous-versions/windows/desktop/legacy/aa379560(v=vs.85)
权限设置(SetSecurityDescriptorDacl)资料: https://docs.microsoft.com/zh-cn/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptordacl?redirectedfrom=MSDN
于是设置CreateEvent第一个参数, 降低权限:
var lEventHandle: THandle; lSA: TSecurityAttributes; lSD: TSecurityDescriptor; lEventName: string; begin lEventName := TGUID.NewGuid.ToString; InitializeSecurityDescriptor(@lSD, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(@lSD, True, nil{这里设置为nil, 不限制访问}, False); lSA.nLength := SizeOf(lSA); lSA.lpSecurityDescriptor := @lSD; lSA.bInheritHandle := False; lEventHandle := CreateEvent(@lSA, False, False, PChar('Global\' + lEventName)); ... WaitForSingleObject(lEventHandle, 1000) CloseHandle(lEventHandle); end;
终于能访问了....
PS:
期间遇到了个坑, 项目机制是先由客户端发包, 然后服务端接收再返回, 在没设置权限时, 测试时是写死EventName, 导致客户端先CreateEvent后服务端open
此时又有个BUG: 服务端Open完了忘了close, 导致后服务端返回时的CreateEvent实际是沿用了客户端创建的Event
所以客户端也能正常访问
结果改为动态EventName以后, 由于服务返回时EventName变了, 所以就出现了拒绝访问
搞的我调试发现使用固定值的EventName就能正常, 改为动态GUID就不正常, 还以为创建GUID会影响权限, 查了好久...心累...