使用AspNet Core或純靜態HTML文件的跨平臺桌面應用程序

目錄

介紹

背景

解決方案的說明

A  C#類來控制Chrome瀏覽器

將Chrome與.Net Core控制檯應用程序(靜態Web應用程序)一起使用

將Chrome與AspNet Core MVC應用程序結合使用

Chrome類的Launch()方法

“普通” HTML演示應用程序

AspNet Core演示應用程序

發佈AspNet Core演示應用程序

結論


一種使用C#和Chrome開發跨平臺桌面GUI應用程序的方法,它是ElectronElectron.NET庫的非常輕巧的替代方案。

源代碼可以在github上找到

介紹

該項目使用現有的Chrome安裝程序將AspNet Core應用程序或由靜態文件(htmljavascriptcss)組成的純HTML應用程序呈現爲桌面應用程序。

不需要ChromiumNodeJS,也不需要Chromium嵌入式框架(CEFCefSharp

背景

這個想法很古老:爲什麼不使用htmljavascriptcss構建可被瀏覽器執行從而跨平臺的桌面應用程序?

這個問題有很多答案。其中一些如下:

解決方案的說明

此解決方案基於以下想法:在運行MS WindowsLinuxMacOS的計算機中,有很大一部分安裝了GoogleChrome瀏覽器。同時,.Net CoreAspNet Core在所有這些OS上運行。

所需要的是一種首先創建Chrome瀏覽器實例,然後指示其導航到主頁” URL的方法。

已經有一個使用NodeJS的方式做以上說的內容:是 Mathias Bynens優秀 PuppeteerNodeJS庫。Google提供了一個有關Puppeteer 門戶,其中包含許多有價值的信息和示例。

再有是PuppeteerC#端口Darío Kondratiuk Puppeteer-Sharp

這是來自githubPuppeteer的描述。

Puppeteer是一個Node庫,它提供了高級API來通過DevTools協議控制ChromeChromium Puppeteer 默認情況下headless運行,但可以配置爲運行完整(non-headless)的ChromeChromium

此解決方案不是基於Headless Chrome瀏覽器的。相反,它使用的是普通的Chrome窗口,其中只有一個標籤頁,根本沒有地址欄。Puppeteer,當然還有Puppeteer-Sharp都可以通過這種方式運行Chrome瀏覽器。

A  C#類來控制Chrome瀏覽器

該項目包含一個名爲Chrome的靜態類,其中包含不到400行代碼,該類用於啓動Chrome並導航到第一個HTML頁面。爲此,Chrome 類提供Launch()方法

static public void Launch(ChromeStartOptions Options, Action Closed = null)

當瀏覽器關閉時,它接受一個Options對象和一個回調來調用。這是ChromeStartOptions類。

public class ChromeStartOptions
{
    public ChromeStartOptions(bool IsAspNetCoreApp = true)
    {
        this.IsAspNetCoreApp = IsAspNetCoreApp;
    }

    public bool IsAspNetCoreApp { get; set; } = true;
    public string ChromePath { get; set; } = "";
    public string HomeUrl { get; set; } = @"Index.html";
    public string ContentFolder { get; set; } = "wwwroot";
    public int Left { get; set; } = 300;
    public int Top { get; set; } = 150;
    public int Width { get; set; } = 1024;
    public int Height { get; set; } = 768;
}

HomeUrlContentFolder特性用於靜態HTML應用程序,而不是ASPNET Core應用程序。

Chrome.Net Core控制檯應用程序(靜態Web應用程序)一起使用

這是在.Net Core控制檯應用程序中使用它的方法,以便將純HTML應用程序呈現爲桌面應用程序。

static void Main(string[] args)
{
    ManualResetEvent CloseEvent = new ManualResetEvent(false);            
    Chrome.Launch(new ChromeStartOptions(false), () => {
        CloseEvent.Set();
    });            
    CloseEvent.WaitOne();
}

ChromeAspNet Core MVC應用程序結合使用

這裏是如何從AspNet Core Startup類的Configure()方法內部調用它的方法,以便將AspNet Core MVC應用程序呈現爲桌面應用程序。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // code here ....
    
    
    // call Chrome as the last thing in the Configure method
    Chrome.Launch(new ChromeStartOptions(true), () => {
        IHostApplicationLifetime LifeTime = app.ApplicationServices.GetService(typeof(IHostApplicationLifetime)) as IHostApplicationLifetime;
        LifeTime.StopApplication();
    });
}

Chrome類的Launch()方法

通過ChromeStartOptions實例傳遞給Launch()方法的標誌指示應用程序的類型。True表示AspNet Core,而false表示純靜態HTML應用程序。

調用者可以通過ChromeStartOptions類的實例將更多信息傳遞給Launch()方法。ChromePath就是這樣一點信息,表示可以找到Chrome的路徑。

目前,該Crome類已盡力尋找在WindowsLinux上安裝Chrome瀏覽器的位置。我計劃研究Chrome啓動器項目的代碼,並嘗試更好地解決此問題。

Chrome.Launch()方法調用Chrome.LaunchAsync()執行以下操作的方法:

  • 處理傳入的選項
  • 準備一個Puppeteer-SharpLaunchOptions實例。
  • 調用Puppeteer.LaunchAsync(options)方法並返回一個Browser實例。
  • 如果這是AspNet Core應用程序,則該選項實例已準備好並且已經包含初始Url。否則,此步驟將推遲。
  • 現在Chrome已啓動並運行,並顯示一個標籤Page
  • 該代碼獲得對此Page的引用。
  • 如果這不是 AspNet Core應用程序,請將事件處理程序鏈接到該Page以滿足傳如請求,因爲沒有web服務器。緊接着調用Page.GoToAsync(url)傳遞應用程序的主頁” URL
  • 在下一步中,在兩種情況下,都將另一個事件處理程序鏈接到Page,以處理瀏覽器的關閉。

這是Chrome.LaunchAsync()方法的完整代碼。

static public async Task LaunchAsync(ChromeStartOptions Options, Action Closed = null)
{
    if (Browser == null)
    {
        // prepare options
        IsAspNetCoreApp = Options.IsAspNetCoreApp;

        if (!string.IsNullOrWhiteSpace(Options.ContentFolder))
        {
            ContentFolder = Path.GetFullPath(Options.ContentFolder);
        } 

        HomeUrl = !IsAspNetCoreApp ? $@"http://{SStaticApp}/{Options.HomeUrl}" : $"http://localhost:{Port}"; 

        List<string> ArgList = new List<string>(DefaultArgs);

        string AppValue = !IsAspNetCoreApp ? "data:text/html, loading..." : Chrome.HomeUrl;

        ArgList.Add($"--app={AppValue}");   // The --app= argument opens Chrome in app mode that is no fullscreen, no url bar, just the window
        ArgList.Add($"--window-size={Options.Width},{Options.Height}");
        ArgList.Add($"--window-position={Options.Left},{Options.Top}");

        LaunchOptions LaunchOptions = new LaunchOptions
        {
            Devtools = false,
            Headless = false,
            Args = ArgList.ToArray(),
            ExecutablePath = !string.IsNullOrWhiteSpace(Options.ChromePath) ? Options.ChromePath : FindChromPath(),
            DefaultViewport = null
        };

        // launch Chrome
        Browser = await Puppeteer.LaunchAsync(LaunchOptions);

        // get the main tab page
        Page[] Pages = await Browser.PagesAsync().ConfigureAwait(false);
        TabPage = Pages[0];

        // event handler for static files
        if (!IsAspNetCoreApp)
        {
            await TabPage.SetRequestInterceptionAsync(true);
            TabPage.Request += StaticRequestHandler;
            await TabPage.GoToAsync(Chrome.HomeUrl, WaitUntilNavigation.DOMContentLoaded);
        }

        // event handler for close
        TabPage.Close += (sender, ea) => {
            Closed?.Invoke();
            Closed = null;
            TabPage = null;
            if (!Browser.IsClosed)
                Browser.CloseAsync();
            Browser = null;
        };

    }
}

普通” HTML演示應用程序

簡單的情況。這是一個.Net Core 3.0控制檯應用程序。

輸出類型設置爲Windows應用程序只是爲了在運行時隱藏控制檯框。

該應用程序碰巧包含一個名爲wwwroot的文件夾,這是該ChromeStartOptions類的ContentFolder屬性的默認值。

public string ContentFolder { get; set; } = "wwwroot";

ContentFolder屬性指示放置靜態文件(htmljscss)的根目錄文件夾。

wwwroot文件夾中包含index.html這又恰好是默認值文件ChromeStartOptions類中的HomeUrl屬性的默認值。

public string HomeUrl { get; set; } = @"Index.html";

這是正在運行的應用程序。

AspNet Core演示應用程序

它是一個AspNet Core 3.0 MVC應用程序,沒有比Visual Studio 2019預覽版模板生成的代碼更多的代碼。

爲了使該應用程序正常工作,需要做一些事情。

項目文件的第一個PropertyGroup應如下所示。

<PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
    <ApplicationIcon />
    <OutputType>WinExe</OutputType>
    <StartupObject />
</PropertyGroup>

上面的代碼使應用程序處於進程外狀態,並在運行時隱藏了控制檯。

隨後是在Properties文件夾中找到的lauchSettings.json文件。

{
    "profiles": {
        "PuppetMvc": {
            "commandName": "Project",
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "applicationUrl": "http://localhost:5000"
        }
    }
}

這就是所有文件內容。只需一個配置文件,完全沒有有關IIS Express的設置。該5000端口實際上未被應用程序使用。該Chrome類的發現和使用第一個自由端口。

Program類應該是如下這樣。

public class Program
{

    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    } 

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {                    
                webBuilder.ConfigureKestrel(o =>
                {
                    o.Listen(IPAddress.Loopback, Chrome.Port);  
                })
                .UseStartup<Startup>();
            });

}

與模板代碼的唯一區別在於,它配置了Kestrel以偵聽所選端口。

這是正在運行的應用程序。

發佈AspNet Core演示應用程序

這是發佈設置。

修剪所有未使用的程序集之後,以上內容在單個文件中創建了一個自包含部署。

這是發佈文件夾的內容。* .exe大小爲43 MB,包含所有內容,包括AspNet Core。只有靜態文件位於wwwroot文件夾中。

最棒的是:雙擊* .exeChrome瀏覽器中運行該應用程序。

結論

多虧了ChromePuppeteer-Sharp,創建了使用Web技術構建的跨平臺桌面應用程序的另一種可能性。這不需要ChromiumNodeJS或其他任何東西。它唯一需要知道的是Chrome的安裝位置。

發佈了69 篇原創文章 · 獲贊 139 · 訪問量 45萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章