когда именованный канал, созданный из имени процесса UWP (appcontainer), должен иметь форму
"\\\\?\\pipe\\local\\SomeName"
, система преобразует это имя в
"\\\\?\\pipe\\Sessions\\<SessionId>\\AppContainerNamedObjects\\<AppContainerSid>\\SomeName"
, поэтому настольное приложение должно создать канал с таким форматом имени, чтобы UWP мог его открыть. но для этого нужно знать appcontainer-sid для приложения uwp и его идентификатор сессии. Хорошо, appcontainer-sid является постоянным для конкретного UWP - никогда не менялся, но SessionId может отличаться (обычно 1, но может быть и другой). Также необходимо установить специальный дескриптор безопасности для канала, который предоставляет доступ для SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW
. например
"D:(A;;GA;;;WD)(A;;GA;;;AC)S:(ML;;;;;LW)"
пример создания такого канала в настольном компьютере
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
volatile UCHAR guz = 0;
ULONG CreatePipeforUWP(OUT PHANDLE PipeHandle, PCWSTR PipeName, HANDLE hProcess)
{
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, FALSE };
// SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
L"D:(A;;GA;;;WD)(A;;GA;;;AC)S:(ML;;;;;LW)",
SDDL_REVISION_1, &sa.lpSecurityDescriptor, 0))
{
return GetLastError();
}
HANDLE hToken;
ULONG err = BOOL_TO_ERROR(OpenProcessToken(hProcess, TOKEN_QUERY, &hToken));
if (err == NOERROR)
{
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = 128;
union {
PVOID buf;
PTOKEN_APPCONTAINER_INFORMATION AppConainer;
PWSTR sz;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
err = BOOL_TO_ERROR(GetTokenInformation(hToken, ::TokenAppContainerSid, buf, cb, &rcb));
if (err == NOERROR)
{
ULONG SessionId;
err = BOOL_TO_ERROR(GetTokenInformation(hToken, ::TokenSessionId, &SessionId, sizeof(SessionId), &rcb));
if (err == NOERROR)
{
PWSTR szSid;
err = BOOL_TO_ERROR(ConvertSidToStringSid(AppConainer->TokenAppContainer, &szSid));
if (err == NOERROR)
{
static const WCHAR fmt[] = L"\\\\?\\pipe\\Sessions\\%d\\AppContainerNamedObjects\\%s\\%s";
rcb = (1 + _scwprintf(fmt, SessionId, szSid, PipeName)) * sizeof(WCHAR);
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
_swprintf(sz, fmt, SessionId, szSid, PipeName);
HANDLE hPipe = CreateNamedPipeW(sz,
PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, 0, 0, 0, &sa);
if (hPipe == INVALID_HANDLE_VALUE)
{
err = GetLastError();
}
else
{
*PipeHandle = hPipe;
}
LocalFree(szSid);
}
}
break;
}
} while (err == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(hToken);
}
LocalFree(sa.lpSecurityDescriptor);
return err;
}
ULONG CreatePipeforUWP(OUT PHANDLE PipeHandle, PCWSTR PipeName, ULONG dwProcessId)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, dwProcessId))
{
ULONG err = CreatePipeforUWP(PipeHandle, PipeName, hProcess);
CloseHandle(hProcess);
return err;
}
return GetLastError();
}
HANDLE hPipe;
if (CreatePipeforUWP(&hPipe, L"MyPipe", *) == NOERROR)
в клиентском (UWP) приложении просто
CreateFileW(L"\\\\?\\pipe\\local\\MyPipe",
FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
, но для этого сервера необходимо знать идентификатор сеанса UWP (или идентификатор процесса) или предполагать, что UWP работает в одном сеансе с сервером. в общем, это нехорошо.
Я бы посоветовал использовать RP C вместо именованных каналов. здесь никаких проблем.
для рабочего стола нужно использовать RpcServerRegisterIf3
и установить SecurityDescriptor для интерфейса, чтобы позволить UWP получить к нему доступ. скажем "D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)"
. и это прекрасно работает с UWP, например, с кодом сервера
ULONG InitRpcServer()
{
PSECURITY_DESCRIPTOR SecurityDescriptor;
// generic all for SDDL_ALL_APP_PACKAGES + SDDL_EVERYONE
ULONG dwError = BOOL_TO_ERROR(ConvertStringSecurityDescriptorToSecurityDescriptorW(
L"D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)", SDDL_REVISION, &SecurityDescriptor, 0));
if (dwError == ERROR_SUCCESS)
{
dwError = RpcServerRegisterIf3(hello_v1_0_s_ifspec,
NULL, NULL, RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,
RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0x10000, 0, SecurityDescriptor);
if (dwError == RPC_S_OK)
{
dwError = RpcServerUseProtseqEpW(
(RPC_WSTR)L"ncalrpc",
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
(RPC_WSTR)L"myname",
SecurityDescriptor);
if (dwError == RPC_S_OK)
{
dwError = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
}
}
LocalFree(SecurityDescriptor);
}
return dwError;
}
и клиенту нужно только
RpcBindingFromStringBinding((RPC_WSTR)L"ncalrpc:[myname]", &IDL_handle)