Developing teleprompter application using NET 5,based on embedded Web server and PowerPoint automation

1. Introduction

When I use the computer to record video tutorials, I will show the PPT slides to the audience, and also have some cue words for myself to read. This is similar to the teleprompter used on many TV shows.

teleprompter

In the PC environment, PowerPoint also has the teleprompter function, when editing PPT, write the prompt words in the notes of each slide. The images projected onto the screen for the audience have no cue words, whereas the images on the speaker’s computer screen have cue words. But this requires the use of a projector or dual screens. My video was recorded on the same screen as the audience, so I couldn’t use PowerPoint’s teleprompter feature. So I had to build my own app.

Since I’m watching the same screen as the audience, I can only display the prompt on another display device if I want to get the “the audience can’t see the prompt, but I can see it” effect. We all have smart phones, so I thought about using smart phones as a device to display prompt words. Based on this idea, I developed a desktop application, which provides an embedded Web server, provides interfaces for “getting the notes of the current PPT slide” and “navigating between slides” and other functions, and provides a Web page to call these interfaces; In this way, as long as you visit the web page on your mobile phone, you can get the cue word through your mobile phone, and you can also switch the page turning of the PPT through your mobile phone. Below is my actual working settings using this teleprompter application:

my actual working settings

This application is developed in.NET 5, but the idea is not limited to any languages. Developers of other programming languages can also develop in your own language.

My application mainly uses two techniques, one is to embed the Web server in the Winform program, and the other is to control the PowerPoint document through the code. I’ll talk about both of them.

2. Embed a webserver in .NET application

On .NET, we can use Kestrel to implement an embedded Web server, and Kestrel is the default Web server for ASP.NET Core projects. Since Kestrel is just a NuGet package, we can install it into anything.NET projects, such as console, Winform, WPF, Xamarin, etc. NET Core is essentially a console application with Kestrel and other related packages.

I’ll show you how to use Kestrel in WinForm projects. For other types of projects, the steps are similar:

1) First, create a Winform project, and then create a folder named ‘wwwroot’ in the project root directory. This folder is used for storing static files such as HTML, JS, CSS, etc.

2) Add the following code in the *.csproj file of your project.

<None Update=”wwwroot\**”>

<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

</None>

The configuration above means: When building the project, all the files and folders of wwwroot folder will be copied to the output directory.

3) Create a index.html file under ‘wwwroot’.

4) Install the Nuget package: Microsoft.AspNetCore.Owin

Install-Package Microsoft.AspNetCore.Owin

5) Add the following code to your form:

private IWebHost host;

Add the following code to the constructor of the form:

host = new WebHostBuilder()

.UseKestrel()

.UseUrls(“http://*:80")

.Configure(Configure)

.Build();

host.RunAsync();

The “http://*:80" above means the application will listen on all the network interfaces, so that other devices can access the embedded web site; and the port of the http server is 80. To keep connected, please allow the corresponding port on the firewall of the computer.

Please invoke the following code when the form is closed or the web site should be stopped, or the application cannot be closed correctly:

host.StopAsync();

host.WaitForShutdown();

Then, add the following method:

public void Configure(IApplicationBuilder app)

{

app.UseDefaultFiles();

app.UseStaticFiles();

app.Run(async (context) =>

{

var request = context.Request;

var response = context.Response;

string path = request.Path.Value;

if (path == “/report”)

{

response.StatusCode = 200;

await response.WriteAsync(“OK”);

}

else

{

response.StatusCode = 404;

}

});

}

The ‘app.UseDefaultFiles()’ is used for enabling the support of index.html and other default files, and ‘app.UseStaticFiles() ‘ is used for serving wwwroot as static folder. The code in app.Run() means: if a user requests ‘/report’, send ‘OK’ as response, or send 404 as status code.

3. Control PowerPoint document by code

My code needs to read the notes of PowerPoint slides and navigate slides, which requires the use of Office Automation technology, that is to call the COM interface of Office through the code. Of course, the premise is that the computer must have PowerPoint software installed.

Officially recommended by Microsoft, the way to access Office in .NET is to generate Office Interop assemblies in Visual Studio using COM references, which is called “Early Binding”. The advantage of this approach is that all objects are strongly typed, so code is easier to write. The disadvantage is that it is bound to a specific version of Office. We must pay attention to the Office binding when developing, and we must use the lowest possible version of Office to develop. I don’t know if it’s my local environment or.NET 5 support for this approach is not mature, I am in.NET 5 projects using COM references keep showing “ MsoTriState is defined in an assembly that is not referenced. You must add a reference to assembly ‘office’ “. as shown in the figure below:

Error

You can also use the Late Binding, which is to use dynamic to access the COM interface. The advantage of this approach is not dependent on the specific Office version, the disadvantage is weak types, so we need to query the document, so development efficiency is relatively low.

I found an open source project, NetOffice (https://netoffice.io/), that is still strongly typed, but does not depend on a specific version of Office. The biggest cons is the development of the .NET Standard version is ongoing, so the current version is still not supported.NET Core.

After comparison, I choosed the method of Late Binding.

Due to the complexity of COM, especially the older resource management technique of “reference counting,” the object references should be treated cautiously, or Office cannot exit properly. I’ve created a simple library, Zack.ComObjectHelpers, which can simplify recycling COM object resources.

The Nuget command is:

Install-Package Zack.ComObjectHelpers

Then use the ComReferenceTracker class to manage the COM reference: create a ComReferenceTracker object when opening the document, and recycle the resource with T method at every place where the COM object may be returned, and call Dispose after al the operations.

The following code is used to open a PPT document and run the presentation:

private dynamic presentation;

private COMReferenceTracker comRefTracker = new COMReferenceTracker();

private void Form1_Closed(object sender, EventArgs e)

{

this.comRefTracker.Dispose();

}

private dynamic T(dynamic comObj)

{

return this.comRefTracker.T(comObj);

}

private void MiOpen_Click(object sender, System.EventArgs e)

{

string filename = “d:/1.pptx”;

dynamic pptApp = T(PowerPointHelper.CreatePowerPointApplication());

pptApp.Visible = true;

dynamic presentations = T(pptApp.Presentations);

this.presentation = T(presentations.Open(filename));

T(this.presentation.SlideShowSettings).Run();

}

The materials of C# Office Automation is relatively small, but because the COM object itself is cross-language, and VBA Office Automation information is very large, so we can refer to the materials for VBA. For example, the following code is my code for reading the notes of current PowerPoint slide after checking the materials of “ read PowerPoint notes with VBA”:

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

notesText = GetInnerText(notesPage);

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);

if (textFrame.HasText == -1)//MsoTriState.msoTrue==-1

{

string text = T(textFrame.TextRange).Text;

sb.AppendLine(text);

}

sb.AppendLine();

}

return sb.ToString();

}

4. The codes of my teleprompter

The above has introduced the core code of the whole application, please visit the GitHub page of the project to get the code of the whole project:

https://github.com/yangzhongke/PhoneAsPrompter

5. Control the video player on computer using phone

With the technology of “Embedded Web Server in Application”, I also realized an application of “ Control Video Playing in the Computer with Mobile Phone “, which can control the video player in the computer through mobile phone to pause, play, adjust volume, fast forward, fast rewind and other functions, and can even further develop and complete functions such as switching live TV channels. The code is also placed in the VideoreMoteController project on the GitHub page above.

control video player using phone

6. GitHub Repository

https://github.com/yangzhongke/PhoneAsPrompter

A happy developer