本主题介绍如何在打包的桌面应用(包括使用 MSIX 打包的Windows 应用 SDK应用)和未打包的 Win32 应用程序之间共享命名对象。
打包应用程序中的命名对象
命名对象 为进程提供共享对象句柄的简单方法。 进程创建命名对象后,其他进程可以使用该名称调用相应的函数来打开对象的句柄。 命名对象通常用于 线程同步 和 进程间通信。
默认情况下,在 AppContainer(如 UWP 应用)中运行的应用程序具有独立的命名对象命名空间 ,它们只能访问他们创建的命名对象。 完全信任打包的桌面应用(包括Windows 应用 SDK应用)没有这种隔离,并在全局命名空间中创建命名对象。 有关详细信息,请参阅Windows 应用 SDK注意事项。
若要与 AppContainer 应用共享命名对象,必须在创建对象时设置权限,并在打开对象时必须限定名称。 当你想要限制任意进程之间的访问时,无论是否打包,本文介绍的技术也同样适用。
创建命名对象
命名对象是使用相应的 Create API 创建的:
所有这些 API 共享一个 LPSECURITY_ATTRIBUTES 参数,使调用方能够指定 访问控制列表(ACL), 以控制哪些进程可以访问该对象。 若要与打包的应用程序共享命名对象,必须在创建命名对象时在 ACL 中授予权限。
安全标识符(SID)表示 ACL 中的标识。 每个打包的应用程序都有自己的 SID,具体取决于其包系列名称。 可以通过将其包系列名称传递给 DeriveAppContainerSidFromAppContainerName,为打包的应用程序生成 SID。
注释
包系列名称可通过开发期间Visual Studio中的包清单编辑器、通过合作伙伴中心查找通过 Microsoft Store 发布的应用程序,或通过已安装应用程序的 Get-AppxPackage PowerShell 命令找到。
此示例 演示了 ACL 命名对象所需的基本模式。 若要与打包的应用程序共享命名对象,请为每个应用程序生成 EXPLICIT_ACCESS 结构:
grfAccessMode = GRANT_ACCESS-
grfAccessPermissions =根据对象和预期用途设置适当的权限 grfInheritance = NO_INHERITANCETrustee.TrusteeForm = TRUSTEE_IS_SIDTrustee.TrusteeType = TRUSTEE_IS_USER-
Trustee.ptstrName =从 DeriveAppContainerSidFromAppContainerName 获取的 SID
通过在 Create 调用中使用适用于打包应用程序的 EXPLICIT_ACCESS 规则填充 LPSECURITY_ATTRIBUTES 参数,可以授予这些应用程序打开该命名对象的访问权限。
注释
如果该对象的 DACL 允许此类访问,则 Win32 应用程序可以在 打开这些对象 时通过限定对象名称来访问由 AppContainer 应用创建的命名对象。 大多数命名对象的默认 DACL 授予对创建者和管理员的访问权限。 如果已设置限制性 DACL,则 Win32 调用方还需要显式访问条目。
示例:使用 ACL 创建命名事件(C++)
以下示例演示如何创建命名事件并授予对打包应用程序的访问权限:
#include <windows.h>
#include <sddl.h>
#include <aclapi.h>
#include <userenv.h>
HANDLE CreateSharedEvent(PCWSTR eventName, PCWSTR packageFamilyName)
{
// Derive the SID for the target packaged application
PSID appContainerSid = nullptr;
HRESULT hr = DeriveAppContainerSidFromAppContainerName(
packageFamilyName, &appContainerSid);
if (FAILED(hr)) return nullptr;
// Build an EXPLICIT_ACCESS entry granting synchronization and signal rights
EXPLICIT_ACCESS_W explicitAccess = {};
explicitAccess.grfAccessPermissions = SYNCHRONIZE | EVENT_MODIFY_STATE;
explicitAccess.grfAccessMode = GRANT_ACCESS;
explicitAccess.grfInheritance = NO_INHERITANCE;
explicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
explicitAccess.Trustee.TrusteeType = TRUSTEE_IS_USER;
explicitAccess.Trustee.ptstrName = reinterpret_cast<LPWSTR>(appContainerSid);
// Create the ACL
PACL acl = nullptr;
DWORD result = SetEntriesInAclW(1, &explicitAccess, nullptr, &acl);
if (result != ERROR_SUCCESS)
{
FreeSid(appContainerSid);
return nullptr;
}
// Create the security descriptor
SECURITY_DESCRIPTOR sd = {};
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) ||
!SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE))
{
LocalFree(acl);
FreeSid(appContainerSid);
return nullptr;
}
SECURITY_ATTRIBUTES sa = {};
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
// Create the named event with only the rights the creator needs
HANDLE hEvent = CreateEventExW(&sa, eventName, 0,
SYNCHRONIZE | EVENT_MODIFY_STATE);
// Cleanup
LocalFree(acl);
FreeSid(appContainerSid);
return hEvent;
}
打开命名对象
命名对象通过向相应的 Open API 传递名称来打开:
打包应用程序创建的命名对象是在应用程序的命名空间中创建的,否则称为命名对象路径。 打开由打包应用程序创建的命名对象时,对象名称必须以创建应用程序的命名对象路径为前缀。
GetAppContainerNamedObjectPath 基于其 SID 返回打包应用程序的命名对象路径。 可以通过将其包系列名称传递给 DeriveAppContainerSidFromAppContainerName,为打包的应用程序生成 SID。
注释
包系列名称可通过开发期间Visual Studio中的包清单编辑器、通过合作伙伴中心查找通过 Microsoft Store 发布的应用程序,或通过已安装应用程序的 Get-AppxPackage PowerShell 命令找到。
打开由打包的应用程序创建的命名对象时,请使用以下格式 <PATH>\<NAME>:
- 用创建应用程序的命名对象路径替换
<PATH>。 - 用对象名称替换
<NAME>。
注释
仅当打包的应用程序创建了对象时,才需要为 <PATH> 对象名称添加前缀。 Win32 应用程序创建的命名对象不需要限定,但创建对象时仍必须授予访问权限。
Windows 应用 SDK 注意事项
Windows 应用 SDK使用 MSIX 打包的桌面应用以包标识的完全信任进程的形式运行, 它们不会在 AppContainer 中运行。 这意味着其命名对象默认在全局命名空间中创建,就像传统的 Win32 应用程序一样。
但是,在以下场景中,你仍然需要采用上文所述的 ACL 和路径限定流程:
-
与 UWP 应用通信 — UWP 应用在 AppContainer 中运行,并具有独立的命名对象命名空间。 使用
GetAppContainerNamedObjectPath,并通过 ACL 授予访问权限,以访问 UWP 应用的命名对象。 - 限制访问 - 即使在完全信任应用之间,你可能希望使用显式 ACL 来限制哪些进程可以打开命名对象。
- 解压缩Windows 应用 SDK应用 — 命名对象位于全局命名空间中,无需特殊处理,与传统 Win32 应用程序相同。
Remarks
默认情况下,打包应用中的命名对象会被隔离,以维护安全性,并确保支持应用程序挂起和终止等生命周期事件。 跨应用程序共享命名对象会引入严格的绑定和版本控制约束,并且要求每个应用程序对其他应用程序的生命周期具有复原能力。 出于这些原因,建议仅在同一发布服务器的应用程序之间共享命名对象。