zoukankan      html  css  js  c++  java
  • 使用C#实现一个PPT遥控器

    说明

    本项目参考了 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的操控界面,所以某些安全软件可能会报毒。但是程序本身是没有问题的。

    截图

    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" style="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>
    
  • 相关阅读:
    抽屉视图第三方控件
    数组NSArry注意事项
    NSInteger 和 NSUInteger 比较大小的注意事项
    IOS应用中的二维码生成和二维码扫描解析
    FMDatabase 的使用(转)
    ASIHTTPRequest的简单使用
    关于MBProgressHUD的使用
    获取文件夹大小
    31. Next Permutation(中等,搞清楚啥是 next permutation)
    Deap Learning (吴恩达) 第一章深度学习概论 学习笔记
  • 原文地址:https://www.cnblogs.com/puzhiwei/p/14769391.html
Copyright © 2011-2022 走看看