.NET5 开发手机提词应用,基于内嵌Web服务器及PowerPoint自动化
項(xiàng)目說(shuō)明
我使用電腦錄制視頻教程的時(shí)候,會(huì)展示PPT給觀眾,同時(shí)也有一些提示性的文字給我自己看。這就類似于很多電視節(jié)目錄制現(xiàn)場(chǎng)的“提詞器”。
節(jié)目錄制現(xiàn)場(chǎng)的提詞器
?
?????? 在PC環(huán)境下,PowerPoint也具有提詞器功能,在編輯PPT的時(shí)候,把每一頁(yè)的備注中寫上提示詞即可。投影到屏幕上的給觀眾看的畫面沒(méi)有提示詞,而演講者的電腦屏幕的畫面中有提示詞。但是這要求使用投影儀或者使用雙屏幕。而我的視頻錄制環(huán)境是我和觀眾是看的同一塊屏幕,因此無(wú)法使用PowerPoint的提詞功能。所以我只能自己開(kāi)發(fā)一個(gè)應(yīng)用。
?????? 既然我和觀眾是看的同一塊屏幕,如果想達(dá)到“觀眾看不到提示詞,而我能看到”的效果,就只能把提示詞展示到額外的顯示設(shè)備上。我們每個(gè)人都有智能手機(jī),因此我就想到了把智能手機(jī)做為顯示提示詞的設(shè)備。基于這個(gè)想法,我開(kāi)發(fā)出了一個(gè)桌面應(yīng)用,這個(gè)應(yīng)用提供了一個(gè)內(nèi)嵌的Web服務(wù)器,提供了“獲取當(dāng)前PPT頁(yè)面?zhèn)渥⑽淖帧币约啊胺?yè)”等功能的接口,并且提供了一個(gè)調(diào)用這些接口的網(wǎng)頁(yè);這樣,只要在手機(jī)上訪問(wèn)這個(gè)網(wǎng)頁(yè),就可以通過(guò)手機(jī)來(lái)獲取提示詞,也可以通過(guò)手機(jī)來(lái)切換PPT的翻頁(yè)。下圖是我使用這個(gè)提詞應(yīng)用實(shí)際工作的場(chǎng)景:
我的提詞器實(shí)際工作場(chǎng)景
?
?????? 這個(gè)應(yīng)用使用.NET 5/.NET Core開(kāi)發(fā),但是思路是不局限于語(yǔ)言的,其他編程語(yǔ)言的開(kāi)發(fā)者也可以使用你習(xí)慣的語(yǔ)言來(lái)開(kāi)發(fā)。
我的應(yīng)用主要使用了兩個(gè)技術(shù),一個(gè)是在WinForm程序中內(nèi)嵌Web服務(wù)器,另一個(gè)就是通過(guò)代碼控制PowerPoint文檔。我下面將對(duì)它們分別做講解。
?
.NET 內(nèi)嵌Web服務(wù)器技術(shù)
.NET中可以使用Kestrel實(shí)現(xiàn)內(nèi)嵌Web服務(wù)器,而Kestrel就是ASP.NET Core項(xiàng)目默認(rèn)的Web服務(wù)器。由于Kestrel只是一個(gè)NuGet包而已,因此可以把它裝到任何.NET項(xiàng)目上,比如控制臺(tái)、WinForm、WPF、Xamarin等。其實(shí)所謂的ASP.NETCore項(xiàng)目本質(zhì)上也只是一個(gè)裝了Kestrel等相關(guān)包的控制臺(tái)程序而已。
這里演示在WinForm項(xiàng)目中的用法,其他類型項(xiàng)目操作步驟都差不多:
1、首先創(chuàng)建一個(gè)WinForm項(xiàng)目,然后在項(xiàng)目根目錄下創(chuàng)建名字為wwwroot的文件夾,這個(gè)文件夾用來(lái)放html、js、css等靜態(tài)文件。
2、在項(xiàng)目的csproj文件中增加如下配置:
<None Update="wwwroot\**"><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None>這段配置的作用是:項(xiàng)目構(gòu)建的時(shí)候,會(huì)把wwwroot目錄下所有的內(nèi)容都復(fù)制到輸出目錄下。
3、在wwwroot目錄下創(chuàng)建index.html文件。
4、通過(guò)Nuget安裝:Microsoft.AspNetCore.Owin
?
Install-Package?Microsoft.AspNetCore.Owin?
5、在主窗口中聲明
private?IWebHost?host;在窗口的構(gòu)造函數(shù)中添加如下內(nèi)容:
其中"http://*:80" 表示網(wǎng)站監(jiān)聽(tīng)所有網(wǎng)卡(這樣就可以通過(guò)其他設(shè)備訪問(wèn)內(nèi)嵌的網(wǎng)站了),并且通過(guò)80端口提供服務(wù)。要確保計(jì)算機(jī)中的防火墻中開(kāi)啟了對(duì)應(yīng)端口的訪問(wèn)權(quán)限。
?
在需要關(guān)閉服務(wù)器或者窗口關(guān)閉的時(shí)候執(zhí)行如下代碼,否則程序不會(huì)正常退出:
host.StopAsync(); host.WaitForShutdown();?
然后聲明如下方法:
public void Configure(IApplicationBuilderapp) {app.UseDefaultFiles();app.UseStaticFiles();app.Run(async(context) =>{varrequest = context.Request;varresponse = context.Response;stringpath = request.Path.Value;if(path == "/report"){response.StatusCode= 200;awaitresponse.WriteAsync("OK");}else{response.StatusCode= 404;}}); }其中app.UseDefaultFiles()表示啟用對(duì)于index.html等默認(rèn)文檔的支持;app.UseStaticFiles()表示把wwwroot提供為靜態(tài)文件夾。app.Run()中的代碼意思為:如果用戶訪問(wèn)了/report這個(gè)路徑,則輸出OK,否則就響應(yīng)碼為404。
?
代碼控制PowerPoint文檔
代碼需要實(shí)現(xiàn)讀取PowerPoint頁(yè)面的備注文字以及翻頁(yè)等功能,這需要使用Office Automation技術(shù),也就是通過(guò)代碼調(diào)用Office的COM接口,當(dāng)前前提條件就是計(jì)算機(jī)上必須安裝PowerPoint軟件。
微軟官方推薦的在.NET中訪問(wèn)Office的方法就是在Visual Studio中通過(guò)COM引用生成Office的Interop程序集,也就是所謂的“Early Binding”。這種方式的優(yōu)點(diǎn)就是一切對(duì)象都是強(qiáng)類型的,所以代碼編寫比較方便。而缺點(diǎn)就是和特定Office版本綁定,必須注意開(kāi)發(fā)的時(shí)候的Office綁定,必須用盡可能低的版本的Office進(jìn)行開(kāi)發(fā)。不知道是我本地環(huán)境的原因還是.NET 5對(duì)于這種方式支持不成熟,我在.NET 5項(xiàng)目通過(guò)COM引用方式使用的時(shí)候,一直出現(xiàn)“MsoTriState在未被引用的程序集中定義。必須添加對(duì)程序集office的引用”的編譯錯(cuò)誤,如下圖:
Figure3編譯錯(cuò)誤
?
?????? 也可以使用Late Binding方式操作,也就是通過(guò)dynamic這種方式進(jìn)行COM接口的訪問(wèn)。這種方式的優(yōu)點(diǎn)是不依賴于特性O(shè)ffice版本,缺點(diǎn)就是全都是弱類型調(diào)用,因此需要查詢文檔,開(kāi)發(fā)效率比較低。
?????? 我發(fā)現(xiàn)一個(gè)開(kāi)源項(xiàng)目NetOffice(https://netoffice.io/),它仍然是強(qiáng)類型的,但是不依賴于特定的Office版本。最大的遺憾就是它目前的.NET Standard版本的開(kāi)發(fā)正在進(jìn)行,所以目前的版本仍然不支持.NET Core。
?????? 經(jīng)過(guò)比較,我只能選擇Late Binding這種方式來(lái)進(jìn)行了。
由于Com的復(fù)雜性,特別是“引用計(jì)數(shù)”這種比較古老的資源管理技術(shù)的復(fù)雜性,導(dǎo)致晚綁定的對(duì)象回收要十分注意,否則會(huì)導(dǎo)致Office無(wú)法退出。我封裝了一個(gè)簡(jiǎn)單的庫(kù)Zack.ComObjectHelpers,可以簡(jiǎn)化Com對(duì)象資源的回收。
這個(gè)庫(kù)的Nuget安裝命令是:
Install-PackageZack.ComObjectHelpers? ? ??
?????? 然后使用其中的COMReferenceTracker類進(jìn)行COM引用的管理:打開(kāi)文檔創(chuàng)建一個(gè)COMReferenceTracker對(duì)象,在每一步可能返回Com對(duì)象的地方,都用T方法進(jìn)行資源回收,操作完成后調(diào)用Dispose。
?????? 如下的代碼就是打開(kāi)一個(gè)PPT文檔,然后進(jìn)入演示模式的代碼:
private dynamic presentation; private COMReferenceTracker comRefTracker =new COMReferenceTracker();private void Form1_Closed(object sender,EventArgs e) {this.comRefTracker.Dispose(); }private dynamic T(dynamic comObj) {returnthis.comRefTracker.T(comObj); }private void MiOpen_Click(object sender,System.EventArgs e) {stringfilename = "d:/1.pptx";dynamicpptApp = T(PowerPointHelper.CreatePowerPointApplication());pptApp.Visible= true;dynamicpresentations = T(pptApp.Presentations);this.presentation= T(presentations.Open(filename));T(this.presentation.SlideShowSettings).Run(); }?
?????? C#操作Office Automation的文檔、資料比較少,不過(guò)由于COM對(duì)象本身是跨語(yǔ)言的,而VBA操作Office Automation的資料非常多,因此完全參考VBA操作的資料即可。比如下面的代碼就是我仿照網(wǎng)上搜索“VBA 讀取PowerPoint備注”的代碼改造成C#語(yǔ)法而成的“讀取當(dāng)前PowerPoint頁(yè)面的備注”代碼:
dynamic notesPage =T(T(T(T(presentation.SlideShowWindow).View).Slide).NotesPage); notesText = GetInnerText(notesPage);private string GetInnerText(dynamic part) {StringBuildersb = new StringBuilder();dynamicshapes = T(T(part).Shapes);intshapesCount = shapes.Count;for(int i = 0; i < shapesCount; i++){dynamicshape = T(shapes[i + 1]);vartextFrame = T(shape.TextFrame);if(textFrame.HasText == -1)//MsoTriState.msoTrue==-1{stringtext = T(textFrame.TextRange).Text;sb.AppendLine(text);}sb.AppendLine();}returnsb.ToString(); }?
手機(jī)提詞器應(yīng)用代碼
?
上面已經(jīng)把整個(gè)應(yīng)用的最核心代碼介紹了,想了解整個(gè)項(xiàng)目的代碼請(qǐng)?jiān)L問(wèn)項(xiàng)目的github頁(yè)面https://github.com/yangzhongke/PhoneAsPrompter
?
手機(jī)控制電腦中視頻播放
?
用“應(yīng)用中內(nèi)嵌Web服務(wù)器”技術(shù),我還實(shí)現(xiàn)了一個(gè)“手機(jī)控制電腦中視頻播放”的應(yīng)用,可以通過(guò)手機(jī)控制電腦中的視頻播放器進(jìn)行暫停、播放、調(diào)節(jié)音量、快進(jìn)快退等功能,甚至可以進(jìn)一步開(kāi)發(fā)完成切換直播電視頻道等功能。代碼也放到了上面github頁(yè)面的VideoRemoteController項(xiàng)目中。
?
視頻教程
????除了這篇文章,我還錄制了大約2個(gè)小時(shí)的視頻教程來(lái)更詳細(xì)的講解代碼,視頻都是免費(fèi)的,可以訪問(wèn)我推送的這條公眾號(hào)消息中的其他視頻。也可以訪問(wèn)我的嗶哩嗶哩、頭條、微博等平臺(tái)中同步發(fā)布的賬號(hào)中的視頻,賬號(hào)名都是“楊中科”。
?
點(diǎn)擊閱讀原文訪問(wèn)開(kāi)源項(xiàng)目
總結(jié)
以上是生活随笔為你收集整理的.NET5 开发手机提词应用,基于内嵌Web服务器及PowerPoint自动化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 使用 Source Generator
- 下一篇: 应对「高并发」的思路