Guest Post by Iouri Simernitski: Inserting an image into a Word file from VSTO

Editor's note: This is a guest post by Iouri Simernitski, a developer on the VSTO team.

Recently someone asked how to insert an image into a Word file from the VSTO DLL’s resources. There are several ways of doing this:

  • Saving the image to a temporary file and using InlineShapes.AddPicture (you’d need to delete the file afterwards)

  • Saving the image to the clipboard and pasting it into the document (you will lose the previous contents of the clipboard)

  • Inserting a PictureBox control with the picture in it (this will not work in Zoom mode)

There is actually a fourth way – call InlineShapes.AddPicture with a URL instead of a file name and implement a mini-Web server in your VSTO application. This is actually quite easy – the Web server will be listening on a private port and would only serve the one image that it is configured with.

Here’s the code:

ThisDocument.cs

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

        {

            byte[] contents;

            // This GUID will be used in the URL

            Guid unique = Guid.NewGuid();

            // Use any private port in the range 49152–65535

            int portNumber = 50213;

           

            // Read the new contents from embedded resources

            using (System.IO.Stream resource = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("WordWebServer.ashura.jpg"))

            {

                using (System.IO.BinaryReader reader = new System.IO.BinaryReader(resource))

                {

                    contents = reader.ReadBytes((int)resource.Length);

                }

            }

            // Use any private port in the range 49152–65535

            using (new WordWebServer.MyWebServer(portNumber, unique, contents))

            {

                // the request will land with the WebServer object in this process

                this.Range(ref missing, ref missing).InlineShapes.AddPicture(@"https://localhost:" + portNumber + "/" + unique.ToString(), ref missing, ref missing, ref missing);

            }

            

        }

WordWebServer.cs

using System;

using System.IO;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading;

namespace WordWebServer

{

    class MyWebServer : IDisposable

    {

        private TcpListener myListener;

        private static readonly Guid terminationGuid = new Guid("{7b0d9a9f-fbec-4fe5-8ea7-fc3b313510fc}");

        byte[] contentsToServe;

        Guid uniqueID;

        int myPort;

        //The constructor which makee the TcpListener start listening on the

        //given port. It also creates a Thread on the method StartListen().

        public MyWebServer(int port, Guid uniqueID, byte[] contents)

        {

            contentsToServe = contents;

            this.uniqueID = uniqueID;

            this.myPort = port;

            //start listing on the given port

            myListener = new TcpListener(IPAddress.Parse("127.0.0.1"), port);

            myListener.Start();

            //start the thread which calls the method 'StartListen'

            Thread thread = new Thread(new ThreadStart(StartListen));

            thread.Start();

        }

        public void StartListen()

        {

            int startPos = 0;

            String errorMessage;

            while (true)

            {

                //Accept a new connection

                using (TcpClient tcpClient = myListener.AcceptTcpClient())

                {

                    if (tcpClient.Connected)

                    {

                        System.Diagnostics.Debug.WriteLine(

                            String.Format(

                         "\nClient Connected!!\n==================\nClient IP {0}\n", tcpClient.Client.RemoteEndPoint));

                        NetworkStream stream = tcpClient.GetStream();

                        //make a byte array and receive data from the client

                        Byte[] receivedBytes = new Byte[1024];

                        // Only reading 1024 characters, this is enough to

                        // see if the GUID is there

                        stream.Read(receivedBytes, 0, receivedBytes.Length);

                        //Convert Byte to String

                        string receivedString = Encoding.ASCII.GetString(receivedBytes);

                        // Look for HTTP request

                        startPos = receivedString.IndexOf("HTTP", 1);

                        // Get the HTTP text and version e.g. it will return "HTTP/1.1"

                        string httpVersion = receivedString.Substring(startPos, 8);

                        //At present we will only deal with GET type

                        // if OPTION received, still OK

                        if (!receivedString.StartsWith("GET"))

                        {

                            System.Diagnostics.Debug.WriteLine("Only Get Method is supported..");

                            SendHeader(httpVersion, null, 0, "501 Not Implemented", tcpClient);

                            continue;

                        }

                        if (receivedString.Contains(terminationGuid.ToString()))

                        {

                            SendHeader(httpVersion, null, 0, "200 OK", tcpClient);

                            break;

                        }

                        if (!receivedString.Contains(uniqueID.ToString()))

                        {

                            errorMessage = "<H2>404 Error! File Does Not Exist...</H2>";

                            SendHeader(httpVersion, "", errorMessage.Length, "404 Not Found", tcpClient);

                            SendToBrowser(Encoding.ASCII.GetBytes(errorMessage), tcpClient);

                            continue;

                        }

                        SendHeader(httpVersion, null, contentsToServe.Length, "200 OK", tcpClient);

                        SendToBrowser(contentsToServe, tcpClient);

                    }

                }

            }

            myListener.Stop();

        }

        public void SendHeader(string httpVersion, string mimeHeader, int totalBytes, string statusCode, TcpClient tcpClient)

        {

            StringBuilder responseBuilder = new StringBuilder();

            // if Mime type is not provided set default to text/html

            if (string.IsNullOrEmpty(mimeHeader))

            {

                mimeHeader = "text/xml"; // Default Mime Type is text/xml

            }

            responseBuilder.Append(httpVersion);

            responseBuilder.Append(' ');

            responseBuilder.AppendLine(statusCode);

            responseBuilder.AppendLine("Server: VSTOServer");

            responseBuilder.Append("Content-Type: ");

            responseBuilder.AppendLine(mimeHeader);

            responseBuilder.AppendLine("Accept-Ranges: bytes");

            responseBuilder.Append("Content-Length: ");

            responseBuilder.AppendLine(totalBytes.ToString());

            responseBuilder.AppendLine("");

            Byte[] bSendData = Encoding.ASCII.GetBytes(responseBuilder.ToString());

            SendToBrowser(bSendData, tcpClient);

            System.Diagnostics.Debug.WriteLine("Total Bytes : " + totalBytes.ToString());

        }

        public void SendToBrowser(Byte[] data, TcpClient tcpClient)

        {

            if (tcpClient.Connected)

            {

                NetworkStream stream = tcpClient.GetStream();

                stream.Write(data, 0, data.Length);

                stream.Flush();

            }

            else

            {

                System.Diagnostics.Debug.WriteLine("Connection Dropped....");

            }

        }

        #region IDisposable Members

        public void Dispose()

        {

            // Send a termination request to stop the web server

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://localhost:" + myPort + "/" + terminationGuid.ToString());

            WebResponse response = request.GetResponse();

        }

        #endregion

    }

}