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