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(@"http://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("http://localhost:" + myPort + "/" + terminationGuid.ToString());
WebResponse response = request.GetResponse();
}
#endregion
}
}