.NET Core 程序的打包与分发 (Linux 篇 Part. II)

前文讲述了如何在 Microsoft 支持的发行版上使用 Self-contained 方式部署 .NET Core Console App。这种部署方式非常简单,但也有不少问题,主要是以下几个方面:

  1. Side by side 部署非常浪费空间。
    每个 app 自带完整的运行环境与基础库,大约占空间 50MB。如果是部署自己编写的服务器软件,那么这个开销还可以接受,但实际上 Console App 除了服务器程序,还有可能是分发给最终用户的客户端程序。如果用户使用的每个 .NET Core 程序都是以这种形式分发,会造成不可忽视的空间浪费。
    另外,虽说这种部署方式有着不同 app 之间环境互不干扰的优点,但是实际上在 .NET Core 环境里,这个优点并不明显,因为 .NET Core 的共享安装方式本身就支持 side by side,安装新版 .NET Core 完全不干扰旧版 .NET Core 程序。
  2. 只适用于 ABI 稳定,且被 Microsoft 支持的发行版。
    Self-contained 的 .NET Core App 并非像 golang 程序那样静态链接了所有依赖,而是依赖发行版的很多软件包,因此并非随便扔哪个发行版都能直接跑起来。
    比如 .NET Core 1.x 的 binary 直接链接到带版本号的库文件,换一个发行版就有大概率跑不起来;.NET Core 2.0 改为运行时动态载入库,一定程度上解决了这个问题,但在 Arch Linux 等比较激进的发行版上仍然无法正常运行,总有各种大大小小的问题,比如 OpenSSL 缺少 SSLv3 等。
  3. runtime 更新不方便
    每个仍然在支持周期内的 .NET Core runtime 都会定期更新, 这些更新通常并不改变功能或兼容性,但可能包含 bug 修复和安全更新。如果采用 Self-contained 部署方式,意味着 runtime 更新要由应用程序维护者来进行,这样不仅会造成重复工作,也有可能造成更新不及时等问题。

本文以 Arch Linux 和 Ubuntu 为例,介绍 shared runtime 部署方式,用于解决这些问题。选择 Ubuntu 是因为其 dotnet 包支持非常完善,而选择 Arch Linux 则是因为有 .NET Core Runtime.NET Core SDK 的 AUR 包,同时它也是典型的简单粗暴的滚动发行版。 继续阅读

.NET Core 程序的打包与分发 (Linux 篇 Part. I)

.NET Core 从 runtime 发布 1.0 RTM 版本至今已经过去一年多了,由于其主要针对跨平台 Console App 设计(ASP.NET Core 的实际部署方式也是 Console App),在 Windows 和 macOS 上的应用都比较有限:对于 Windows 平台,由于有兼容性较好的完整 .NET Framework,几乎不需要使用 .NET Core;而 macOS 程序主要以桌面 GUI 为主,.NET Core 除了用在服务器程序开发中,也很难有什么别的用途,而 macOS 上 mono 程序开发/调试的体验也还说得过去。

但 Linux 的情况就不同。首先 Linux 的应用主要以 Console/Web Server 为主,而 .NET Core 主要就是针对这些;其次在很多发行版上,mono runtime 的版本非常旧,API 实现不全,并且调试不方便。这种情况下,合理使用 .NET Core 能显著简化 .NET 应用程序的部署。

本文主要针对将 Console App 使用 Self-contained 方式(自带完整 runtime)部署到 .NET Core 支持的发行版的应用场景。这些发行版的特征是存在版本号(非滚动更新),且同一版本内二进制 ABI 稳定,如 Ubuntu, Debian, CentOS/RHEL, Fedora 等;而对于滚动更新的发行版如 Arch Linux 等,我将会在下一篇文章中讲述如何分发 .NET Core 程序。 继续阅读

三种方式轻松绕过Windows App Certification Kit (WACK)的API检测(UWP, C++)

微软在UWP中提供了一组丰富的API能够满足99%的应用的需求,然而在那剩下1%的情况下找不到桌面API的替代品是很棘手的,正如我上一篇文章里提到的CreateFile,就不属于微软在UWP中允许使用的API。
实际上WACK的API检测是个没什么用的东西,因为它是通过读取PE导入表,以及 .NET程序的P/Invoke签名的方法来判断一个App是否使用了不允许使用的API。因此只要调用LoadLibrary就能轻松绕过。
问题在于LoadLibrary(Ex)本身不被允许调用,微软表示替代品是LoadPackagedLibrary,而这个API在调用时会检测路径是否在appx内,如果不在就直接报错。(太愚蠢了)因此首先我们要设法获得LoadLibraryEx的地址,在此之前先获取kernel32.dll或kernelbase.dll的地址。

继续阅读

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好得多,分发/支付上也比桌面程序方便不少,虽说原则上微软并不允许商店应用与桌面应用互相通信。

继续阅读