简体   繁体   English

IPC:UWP C# 管道客户端在连接 C++ 服务器时失败

[英]IPC: UWP C# pipe client fails on connect C++ server

I am trying to use NamedPipe to communicate between app and service in Win10.我正在尝试使用 NamedPipe 在 Win10 中的应用程序和服务之间进行通信。 APP is developed with C#(UWP),running foreground as Pipe Client. APP是用C#(UWP)开发的,前台作为Pipe Client运行。 And service is C++ running background as Pipe Server.服务是 C++ 运行后台作为管道服务器。 Now the problem is that the APP couldn't connect the service.现在的问题是APP无法连接服务。 I know that MSFT doc said Pipes are only supported within an app-container.我知道 MSFT 文档说管道仅在应用程序容器中受支持。 But I have tried the following cases: my uwp app VS C#(nonUWP) Server(not in an app-container);但我尝试过以下情况:我的 uwp 应用程序 VS C#(nonUWP) 服务器(不在应用程序容器中); C++ Client VS C++ server(same code with service except running in foreground). C++ 客户端 VS C++ 服务器(与服务相同的代码,除了在前台运行)。 Both cases works fine.两种情况都可以正常工作。 So I think it may have something wrong with Security privilege.所以我认为它的安全权限可能有问题。 But I couldn't find somthing abnormal, Could someone help me out?但是我找不到任何异常,有人可以帮助我吗?

Client(UWP/C#):客户端(UWP/C#):

_namedPipeClientHandle[index] = CreateFileW(@"\\.\pipe\starpipe",
                            DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE,
                            SHAREMODE.FILE_SHARE_READ | SHAREMODE.FILE_SHARE_WRITE,
                            0,
                            CREATIONDISPOSITION.OPEN_EXISTING,
                            FLAGSANDATTRIBUTES.FILE_FLAG_OVERLAPPED,
                            0);
                    if (_namedPipeClientHandle[index] != null && _namedPipeClientHandle[index].IsInvalid == false)
                    {
                        _namedPipeClientStream[index] = new FileStream(_namedPipeClientHandle[index], FileAccess.ReadWrite, 2048, true);
                        isConnected[index] = true;
                        break;
                    }

Server(C++):服务器(C++):

    EXPLICIT_ACCESS ea[2];
    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
    SECURITY_ATTRIBUTES sa;

    if (!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID)) return false;

    SecureZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
    ea[0].grfAccessPermissions = FILE_ALL_ACCESS | GENERIC_WRITE | GENERIC_READ;
    ea[0].grfAccessMode = SET_ACCESS;
    ea[0].grfInheritance = NO_INHERITANCE;
    ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea[0].Trustee.ptstrName = (LPTSTR)pEveryoneSID;

    if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID)) return false;
    ea[1].grfAccessPermissions = FILE_ALL_ACCESS | GENERIC_WRITE | GENERIC_READ;
    ea[1].grfAccessMode = SET_ACCESS;
    ea[1].grfInheritance = NO_INHERITANCE;
    ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
    ea[1].Trustee.ptstrName = (LPTSTR)pAdminSID;

    DWORD dwRes = SetEntriesInAclW(2, ea, NULL, &pACL);
    if (ERROR_SUCCESS != dwRes) return false;

    auto secDesc = std::vector<unsigned char>(SECURITY_DESCRIPTOR_MIN_LENGTH);
    PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)(&secDesc[0]);
    if (nullptr == pSD) return false;

    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) return false;
    if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) return false;

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = pSD;
    sa.bInheritHandle = FALSE;

    const char* pStrPipeName = "\\\\.\\pipe\\starpipe";
    m_hPipe = CreateNamedPipeA(
        pStrPipeName,
        PIPE_ACCESS_DUPLEX,
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_TYPE_BYTE | PIPE_WAIT,
        PIPE_UNLIMITED_INSTANCES,
        2048,
        2048,
        0,
        &sa);

    if (m_hPipe == INVALID_HANDLE_VALUE) return false;

    if (::ConnectNamedPipe(m_hPipe, NULL)) return true;

when named pipe created from UWP process (appcontainer) name must have form从 UWP 进程(应用程序容器)创建的命名管道名称必须具有形式

"\\\\\\\\?\\\\pipe\\\\local\\\\SomeName"

system convert this name to系统将此名称转换为

"\\\\\\\\?\\\\pipe\\\\Sessions\\\\<SessionId>\\\\AppContainerNamedObjects\\\\<AppContainerSid>\\\\SomeName"

so desktop application must create pipe with such name format for UWP be able open it.所以桌面应用程序必须创建具有这种名称格式的管道,以便 UWP 能够打开它。 but for this need know appcontainer-sid for uwp app and it session id.但为此需要知道 uwp 应用程序的 appcontainer-sid 及其会话 ID。 ok, appcontainer-sid is permanent for concrete UWP - never changed, but SessionId can be different (usually 1 but can be and another).好的,appcontainer-sid 对于具体的 UWP 是永久性的 - 永远不会改变,但 SessionId 可以不同(通常是 1,但可以是另一个)。 also need set special security descriptor on pipe, which grant access for SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW .还需要在管道上设置特殊的安全描述符,授予对SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW访问权限。 for example例如

"D:(A;;GA;;;WD)(A;;GA;;;AC)S:(ML;;;;;LW)"

example of create such pipe in desktop在桌面上创建此类管道的示例

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)

in client (UWP) app simply在客户端 (UWP) 应用程序中

CreateFileW(L"\\\\?\\pipe\\local\\MyPipe", 
        FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

but for this server need know UWP session id (or process id) or assume that UWP run in same session with server.但是对于此服务器,需要知道 UWP 会话 ID(或进程 ID)或假设 UWP 与服务器在同一会话中运行。 in general this is not nice.一般来说,这不好。

i be advice use RPC instead named pipes.我建议使用 RPC 代替命名管道。 here no any problems.这里没有任何问题。

desktop need use RpcServerRegisterIf3 and set SecurityDescriptor for interface for let UWP access it.桌面需要使用RpcServerRegisterIf3并为接口设置 SecurityDescriptor 以便让 UWP 访问它。 say "D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)" ."D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)" and this fine work with UWP, for example server code和 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;
}

and client only need和客户只需要

RpcBindingFromStringBinding((RPC_WSTR)L"ncalrpc:[myname]", &IDL_handle)

According to this page根据这个页面

Pipes are only supported within an app-container;管道仅在应用程序容器中受支持; ie, from one UWP process to another UWP process that's part of the same app.即,从一个 UWP 进程到属于同一应用程序的另一个 UWP 进程。 Also, named pipes must use the syntax ".\\pipe\\LOCAL" for the pipe name.此外,命名管道必须使用语法“.\\pipe\\LOCAL”作为管道名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM