UWP使用命名管道与桌面程序通信 (C#)

关于UWP的历史,其起源是Microsoft在Windows 8中引入的Metro apps。(后来又被称作Modern apps, Windows apps, Universal Windows Apps等)无论是从目的还是从效果上看,这一类应用模型都与iOS/Android比较相似,是为了更有利于移动平台生态的发展设计的。
然而UWP目前面向的最大的用户群体是Windows桌面用户,只用UWP实现一个程序就会出现很多feature无法实现的问题,因此这种情况下,让用户安装并运行一个普通权限的后台进程,使用UWP做UI与之通信就成为了一种选择,毕竟UWP的C#/XAML性能比WPF好得多,分发/支付上也比桌面程序方便不少,虽说原则上微软并不允许商店应用与桌面应用互相通信。

除了命名管道(named pipe),进程间通信的方式还有很多,比如Socket,还有微软给Runtime Broker用的COM RPC(rpcrt4)等。对于前者,如果是AppContainer默认的防火墙配置,与本机其它程序不允许通过127.0.0.1通信,而调节这个防火墙配置需要管理员权限。而对于COM RPC,由于其API比较复杂,并且rpc.h里大部分的API是desktop only,因此调用起来也不方便。(在Store apps里有办法使用desktop only API,但是如果数目多了会比较麻烦)

相比之下,命名管道只需要在app中获取一个desktop only的API(即CreateFile)就能使用了,并且用户运行桌面程序时不需要管理员权限,大概是最简单的方式。

在AppContainer中不能创建命名管道,只能连接到有权限访问的命名管道。因此我们使用桌面程序创建。

桌面程序:

using (var pipe = new NamedPipeServerStream("mypipe", PipeDirection.InOut, -1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0, null, HandleInheritability.None, PipeAccessRights.ChangePermissions))  
{
    PipeSecurity ps = pipe.GetAccessControl();
    PipeAccessRule clientRule = new PipeAccessRule(
        new SecurityIdentifier("S-1-15-2-1"), // All application packages
        PipeAccessRights.ReadWrite,
        AccessControlType.Allow);
    PipeAccessRule ownerRule = new PipeAccessRule(
        WindowsIdentity.GetCurrent().Owner,
        PipeAccessRights.FullControl,
        AccessControlType.Allow);
    ps.AddAccessRule(clientRule);
    ps.AddAccessRule(ownerRule);
    pipe.SetAccessControl(ps);
    pipe.WaitForConnection();
    using (var sr = new StreamReader(pipe, Encoding.UTF8))
    {
        while (true)
        {
            string message = sr.ReadLine();
            //在此处处理App写入命名管道的内容
            pipe.WaitForPipeDrain();
        }
    }
}

其中”mypipe”是命名管道的名称,SID S-1-15-2-1 是All application packages的SID,如果只想让一个AppContainer访问,可以通过获取进程信息来获取它的SID。

App程序:

var handle = CreateFileW(@"\\.\pipe\mypipe", FileAccess.ReadWrite, FileShare.None, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);  
if (handle.IsInvalid)  
{
    var err = Marshal.GetLastWin32Error();
    //handle error
}
sw = new StreamWriter(new FileStream(handle, FileAccess.Write), Encoding.UTF8);  
sw.WriteLine("Hello from AppContainer");  
sw.Flush(); 

其中”mypipe”是命名管道的名称。
由于UWP的.NET标准库并没有System.IO.Pipes,必须手动调用CreateFile。UWP允许使用的CreateFile2是不能打开命名管道的,即使有权限也会返回ERROR_NOT_SUPPORTED_IN_APPCONTAINERCreateFileW并不能直接使用DllImport("kernel32"),否则无法通过WACK。

posted @ 2021-01-20 20:44  MaxBruce  阅读(428)  评论(0编辑  收藏  举报