使用C#实现一个PPT遥控器

  • A+
所属分类:.NET技术
摘要

本项目参考了 https://github.com/yangzhongke/PhoneAsPrompter 项目来完成实现,并对其进行了一些修改完善。


说明

本项目参考了 https://github.com/yangzhongke/PhoneAsPrompter 项目来完成实现,并对其进行了一些修改完善。

完整代码可以到 https://github.com/PuZhiweizuishuai/PPT-Remote-controlhttps://gitee.com/puzhiweizuishuai/PPT-Remote-control 查看。

软件下载地址: https://gitee.com/puzhiweizuishuai/PPT-Remote-control/releases/v1.0.0

另外,由于程序启动后会创建一个WEB服务器,用来显示PPT的操控界面,所以某些安全软件可能会报毒。但是程序本身是没有问题的。

截图

使用C#实现一个PPT遥控器

具体实现

通过在Win Form项目中内嵌一个Kestrel Web服务器,我们就可以通过浏览器向web服务器发送请求来接收远程操作指令。之后通过Late Binding的方式去操作PPT。

1、在 Win Form项目中内嵌HTTP服务器

在Form窗口启动时,我们新建一个Kestrel服务器

            this.webHost = new WebHostBuilder()                 .UseKestrel()                 .Configure(ConfigureWebApp)                 .UseUrls("http://*:" + port)                 .Build();              // 异步运行服务器             this.webHost.RunAsync(); 

然后对其进行配置

private void ConfigureWebApp(IApplicationBuilder app)         {             app.UseDefaultFiles();             app.UseStaticFiles();             app.Run(async (context) =>             {                 // 处理非静态请求                  var request = context.Request;                 var response = context.Response;                 string path = request.Path.Value;                 response.ContentType = "application/json; charset=UTF-8";                 bool hasRun = true;                 if (path == "/report")                 {                     string value = request.Query["value"];                     this.BeginInvoke(new Action(() => {                         this.PageLabel.Text = value;                     }));                     response.StatusCode = 200;                     await response.WriteAsync("ok");                 }                 else                 {                     response.StatusCode = 404;                 }             });                      } 

操作PPT

首先,由于涉及到了COM编程,我们需要注意内存回收与释放,所以需要用到COMReferenceTracker类进行应用管理。

每一步用到COM的地方,都要用T方法进行资源回收。

        private dynamic T(dynamic comObj)         {             return this.comReference.T(comObj);         }  

以下操作使用dynamic进行操作,所有操作需要去查询VBA文档了解具体用法,以下仅演示部分操作

打开一个PPT的操作实现

        private void button1_Click(object sender, EventArgs e)         {             // 文件选择框             openFileDialog.Filter = "ppt文件|*.ppt;*.pptx;*.pptm";             if (openFileDialog.ShowDialog() != DialogResult.OK)             {                 return;             }                        string filename = openFileDialog.FileName;             this.ClearComRefs();             // 创建 PPT 对象             dynamic pptApp = T(PowerPointHelper.CreatePowerPointApplication());             // 显示 PPT             pptApp.Visible = true;             dynamic presentations = T(pptApp.Presentations);             // 打开 PPT             this.presentation = T(presentations.Open(filename));             // 全屏显示             T(this.presentation.SlideShowSettings).Run();         } 

PPT上一个动画操作实现

T(T(presentation.SlideShowWindow).View).Previous(); 

下一步,与上一个操作类似,只需更换Previous()方法为Next()即可。

获取注释

首先我们需要一个方法去解析注释

        private string GetInnerText(dynamic part)         {             StringBuilder sb = new StringBuilder();             dynamic shapes = T(T(part).Shapes);             int shapesCount = shapes.Count;             for (int i = 0; i < shapesCount; i++)             {                 dynamic shape = T(shapes[i + 1]);                 var textFrame = T(shape.TextFrame);                 // MsoTriState.msoTrue==-1                 if (textFrame.HasText == -1)                 {                     string text = T(textFrame.TextRange).Text;                     sb.AppendLine(text);                 }                 sb.AppendLine();             }             return sb.ToString();         } 

之后通过

dynamic notesPage = T(T(T(T(presentation.SlideShowWindow).View).Slide).NotesPage); string notesText = GetInnerText(notesPage); 

我们就可以获取具体每页的注释信息。

完善服务器

了解了以上的PPT操作之后,我们就需要去完善我们的Web服务器端配置。

用户访问相应的地址,然后去执行上面PPT操作部分的代码即可。

                else if (path == "/getNote")                 {                     string notesText = null;                     this.Invoke(new Action(() => {                         if (this.presentation == null)                         {                             return;                         }                         try                         {                             dynamic notesPage = T(T(T(T(presentation.SlideShowWindow).View).Slide).NotesPage);                             notesText = GetInnerText(notesPage);                         }                         catch (COMException ex)                         {                             notesText = "";                         }                     }));                     await response.WriteAsync(notesText);                 }                 else if (path == "/next")                 {                     response.StatusCode = 200;                     this.Invoke(new Action(() => {                         if (this.presentation == null)                         {                             return;                         }                         try                         {                             T(T(this.presentation.SlideShowWindow).View).Next();                             hasRun = true;                         } catch (COMException e)                         {                             hasRun = false;                         }                                              }));                      if (hasRun)                     {                         await response.WriteAsync("OK");                     }                     else                     {                         await response.WriteAsync("NO");                     }                 }                 else if (path == "/previous")                 {                     response.StatusCode = 200;                     this.Invoke(new Action(() => {                         if (this.presentation == null)                         {                             return;                         }                         try                         {                             T(T(this.presentation.SlideShowWindow).View).Previous();                             hasRun = true;                         }                         catch (COMException e)                         {                             hasRun = false;                         }                                              }));                     if (hasRun)                     {                         await response.WriteAsync("OK");                     }                     else                     {                         await response.WriteAsync("NO");                     }                      

完成前端

通过轮询的方式,不断的向服务端发送请求,获取最新的消息,这样我们就可以实现通过浏览器去操作PPT了。

<!DOCTYPE html>  <html lang="zh-cn"> <head>     <meta charset="utf-8" />     <meta http-equiv="X-UA-Compatible" content="IE=edge">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <meta name="renderer" content="webkit" />     <title>操作你的PPT</title>     <link rel="icon" href="/logo.ico">     <style>         div {             font-size: 25px         }     </style> </head> <body>     <div id="main" style="width:100vw;height:100vh;">         <p id="note"></p>     </div>     <script src="hammer.min.js"></script>     <script>         function httpGet(url, cb) {             fetch(url, {                 headers: {                     'Content-Type': 'application/json; charset=UTF-8'                 },                 method: 'GET'             }).then(response => response.text())                 .then(text => {                     cb(text)                 })                 .catch(e => {                     return null                 })         }          const note = document.querySelector("#note");          let hasRun = true         let getNotes = setInterval(() => {             httpGet('/getNote', (text) => {                 note.innerText = text             })         }, 500)          function nextPage() {             httpGet('/next', (text) => {                 if (text == 'NO') {                     clearInterval(getNotes)                     note.innerText = "幻灯片播放完毕!"                     hasRun = false                 } else {                     if (!hasRun) {                         getNotes = setInterval(() => {                             httpGet('/getNote', (text) => {                                 note.innerText = text                             })                         }, 500)                         hasRun = true                     }                 }             })         }          function previousPage() {             httpGet('/previous', (text) => {                 if (text == 'NO') {                     clearInterval(getNotes)                     note.innerText = "幻灯片播放完毕!"                     hasRun = false                 } else {                     if (!hasRun) {                         getNotes = setInterval(() => {                             httpGet('/getNote', (text) => {                                 note.innerText = text                             })                         }, 500)                         hasRun = true                     }                 }             })         }          var hammer = new Hammer(document.querySelector("#main"));         hammer.on("swipeleft", function () {             nextPage();         });         hammer.on("swiperight", function () {             previousPage();         });     </script> </body> </html>