2021-05-14

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

使用你的手机遥控你正在播放的PPT,并查看注释。

说明

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

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

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

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

截图

APP

具体实现

通过在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" >  <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>








原文转载:http://www.shaoqun.com/a/743790.html

跨境电商:https://www.ikjzd.com/

cicpa:https://www.ikjzd.com/w/1375

点通:https://www.ikjzd.com/w/1913


使用你的手机遥控你正在播放的PPT,并查看注释。说明本项目参考了https://github.com/yangzhongke/PhoneAsPrompter项目来完成实现,并对其进行了一些修改完善。完整代码可以到https://github.com/PuZhiweizuishuai/PPT-Remote-control与https://gitee.com/puzhiweizuishuai/PPT-
败欧洲网站:https://www.ikjzd.com/w/1555
宝付:https://www.ikjzd.com/w/539
邮政电话:https://www.ikjzd.com/w/202
EC21:https://www.ikjzd.com/w/1625
手把手教你如何开发产品(超级干货!!!):https://www.ikjzd.com/home/140176
好事连连!美国又发钱,亚马逊Prime day定了?:https://www.ikjzd.com/home/122359

No comments:

Post a Comment