Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I embed any file type into Microsoft Word without interop assemblies

I'm trying to do this How can I embed any file type into Microsoft Word using OpenXml 2.0

but only using the OpenXml SDK 2.5 (no interop assemblies )

I can embed other word (or office) files using the following code sample from here
(We need to add a reference to DocumentFormat.OpenXml from NuGet and WindowsBase)

but I'm having trouble refactoring it to accept any file type (pdf ,mp3 etc)

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
using v = DocumentFormat.OpenXml.Vml;
using ovml = DocumentFormat.OpenXml.Vml.Office;
using System.IO;

class Program
{

    static void Main(string[] args)
    {
        //Just update the following 2 variables to point to existng files
        string containingDocumentPath = @"C:\Temp\Word\ContainingDocument.docx";
        string embeddedDocumentPath = @"C:\Temp\Word\EmbeddedDocument.docx";
        CreatePackage(containingDocumentPath, embeddedDocumentPath);
    }

    private static void CreatePackage(string containingDocumentPath, string embeddedDocumentPath)
    {
        using (WordprocessingDocument package = WordprocessingDocument.Create(containingDocumentPath, WordprocessingDocumentType.Document))
        {
            AddParts(package, embeddedDocumentPath);
        }
    }

    private static void AddParts(WordprocessingDocument parent, string embeddedDocumentPath)
    {
        var mainDocumentPart = parent.AddMainDocumentPart();
        GenerateMainDocumentPart().Save(mainDocumentPart);

        var embeddedPackagePart = mainDocumentPart.AddNewPart<EmbeddedPackagePart>("application/vnd.openxmlformats-" + "officedocument.wordprocessingml.document", "rId1");

        GenerateEmbeddedPackagePart(embeddedPackagePart, embeddedDocumentPath);

        var imagePart = mainDocumentPart.AddNewPart<ImagePart>("image/x-emf", "rId2");

        GenerateImagePart(imagePart);
    }

    private static Document GenerateMainDocumentPart()
    {
        var element =
          new Document(
            new Body(
              new Paragraph(
                new Run(
                  new Text(
                    "This is the containing document."))),
              new Paragraph(
                new Run(
                  new Text(
                    "This is the embedded document: "))),
              new Paragraph(
                new Run(
                  new EmbeddedObject(
                    new v.Shape(
                      new v.ImageData()
                      {
                          Title = "",
                          RelationshipId = "rId2"
                      }
                    )
                    {
                        Id = "_x0000_i1025",
                        Style = "width:76.5pt;height:49.5pt",
                        Ole = TrueFalseBlankValue.FromBoolean(false)//v.BooleanEntryWithBlankValues.Empty
                    },
                    new ovml.OleObject()
                    {
                        Type = ovml.OleValues.Embed,
                        ProgId = "Word.Document.12",
                        ShapeId = "_x0000_i1025",
                        DrawAspect = ovml.OleDrawAspectValues.Icon,
                        ObjectId = "_1299573545",
                        Id = "rId1"
                    }
                  )
                //{
                //    DxaOriginal = (UInt32Value)1531UL,
                //    DyaOriginal = (UInt32Value)991UL
                //}
                )
              )
            )
          );

        return element;
    }

    public static void GenerateEmbeddedPackagePart(OpenXmlPart part, string embeddedDocumentPath)
    {
        byte[] embeddedDocumentBytes;

        // The following code will generate an exception if an invalid
        // filename is passed.
        using (FileStream fsEmbeddedDocument = File.OpenRead(embeddedDocumentPath))
        {
            embeddedDocumentBytes = new byte[fsEmbeddedDocument.Length];
            fsEmbeddedDocument.Read(embeddedDocumentBytes, 0, embeddedDocumentBytes.Length);
        }

        using (BinaryWriter writer =
          new BinaryWriter(part.GetStream()))
        {
            writer.Write(embeddedDocumentBytes);
            writer.Flush();
        }
    }

    public static void GenerateImagePart(OpenXmlPart part)
    {
        #region Icon Bytes

        // Best practice is that this icon should be stored as a resource
        // in the assembly instead of as a Base64 string.

        string iconBytes = @"
    AQAAAGwAAAAAAAAAAQAAAGQAAAAuAAAAAAAAAAAAAAAODgAAGAkAACBFTUYAAAEAbBQ
    AABAAAAACAAAAAAAAAAAAAAAAAAAAQAYAALAEAAA0AgAApwEAAAAAAAAAAAAAAAAAAN
    ycCACldQYAGAAAAAwAAAAAAAAAGQAAAAwAAAD///8AcgAAAKAQAAAjAAAAAQAAAEIAA
    AAgAAAAIwAAAAEAAAAgAAAAIAAAAACA/wEAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8A
    AAAAAAAAAP///wAAAAAAbAAAADQAAACgAAAAABAAACAAAAAgAAAAKAAAACAAAAAgAAA
    AAQAgAAMAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAA/wAA/wAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuaSV/5F8av9
    4YUz/alM8/2ZON/9iSjL/YEgw/2BIMP9gSDD/YEgw/2BIMP9gSDD/YEgw/2BIMP9gSD
    D/YEgw/2BIMP9gSDD/YEgw/2BIMP9gSDD/YEgw/2BIMP9gSDD/YEgw/wAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAC6pZb//Orf/9/NxP/dxbf/3r2s/964ov/jspj/5bOQ
    /+WzkP/ls5D/5bKO/+awi//nr4j/6a2F/+qsgv/rqn//7Kd7/+6ld//vo3P/8aFv//G
    ga//znmj/851l//SbY/9gSDD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALqllv
    /86+H//Ovh//zr4f/86+D/++rf//vp3//76d7/++nd//ro3P/759z/+uba//rm2f/65
    df/+uTW//rj1P/64tP/+eDS//ngz//53s7/+d3M//ncyv/528j/9Jxl/2BIMP8AAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu6aX//zu5P/77eT//O3k//vs5P/77OP//Ov
    i//vr4v/76+D/++rf//vp3//76d3/++jc//rn2v/65tr/+uTY//rk1v/54tT/+uHT//
    rg0f/538//+d7N//ncyv/znWf/YEgw/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AC7p5j//O/n//zv5//77+b//O/m//zv5v/87eX//O3k//zt5P/87OP//Ovi//zq4f/8
    6t//++re//vo3P/759v/+uba//rl1//65NX/+uLT//nh0f/64ND/+t/O//Keav9gSDD
    /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyomP/88er//PHp//zx6v/88On//P
    Dp//zw6P/97+j//O/n//zu5v/87uX//O3k//zs4//87OH/06mL/9Koif/Rpof/0aWG/
    9CkhP/Po4P/+uPV//rh0v/64ND/8aBt/2BIMP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAvama//3z7f/98+3//fPs//3y7P/98+v//fLr//zy6v/98er//fHp//zw6P/
    87+f//O7m//zt5P/87eP//Ovh//vq3//76d3/++jc//rm2f/65Nf/+uPW//ri0//won
    D/YEgw/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC+qpv//fXv//307//jwaj/4
    r+l/+G9o//fu6H/3rme/923nP/btZr/2rOX/9mxlf/YsJP/166R/9asj//Vq43/1KmL
    /9Koif/Spoj/0aWG//vn2v/65dj/+uPW/++jc/9gSDD/AAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAL+rnP/+9vH//fbx//328f/99vH//fbx//718P/99fD//fXw//307/
    /98+7//fPt//3y7P/88er//PDp//zv5//87uX//O3k//zr4v/86t//++jd//vn2v/65
    dj/7aV3/2BIMP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKye//348//99/P/
    5sWt/+XDq//jwaj/4b6l/+C8ov/euqD/3rmf/923nP/ctpr/2rSY/9mylv/YsJP/166
    R/9atj//Vq43/1KqL/9Ooiv/76uD/++nd//vn2//tp3v/YEgw/wAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAADBrp7//vn1//759f/++fX//vj1//749f/++PX//vj1//339
    P/+9/P//vfz//338v/99vH//fXw//307v/98+3//PLr//zx6f/87+f//O7k//vs4v/7
    6uD/++jd/+upf/9gSDD/AAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAMKun//++vf
    //vr3/+jJsf/nx6//5sWt/+XDq//jwqj/4sCm/+G+pP/gvKH/3rqf/924nP/ctpr/2r
    SY/9mylv/YsJT/166R/9atj//Vq47//O7l//zs4//76t//6auD/2BIMP8AAAAAAAAAA
    AAAAAAAAAABAAAABQAAAA0AAAAUrpyP/9/c2f/b2NX/1dHO/9DLyv/JxcP/xcG//8rG
    xP/Oysj/8ezp//v28//9+PX//vj1//749P/+9/P//fbx//318P/99O3//fLr//zx6f/
    87+f//O3k//zs4v/orof/YEgw/wAAAAAAAAAAAAAAAQAAAAMAAAAOAAAAIgAAADWQgX
    b/tLGv/62rqf+TfGv/mIN0/6KQgv+3p5n/yrus/8Ksmf+6oY//3bqh/+C9o//gvKL/3
    rqf/924nf/ctpv/27SY/9mylv/YsJT/16+S//zx6f/97+f//O3k/+avi/9gSDD/AAAA
    AAAAAABuOSXAczwnzWw4JNF5VEbekHpw6q+dlv/FuLL/3tbS//Xx7P/8+PP/+/bv//n
    06//48uf/vqmb/9jW1P/r6Ob//fr4//77+f/++vj//vn2//749f/99/P//fbx//307/
    /98+3//fLr//3w6P/87ub/5bGP/2BIMP8AAAAAAAAAAIxLMPDeuar/vpqL/9Gmlf///
    //////////////+/vz//vz6//369v/8+PP/+/bv//n06//Bqpz/v6OO/9KznP/jwaj/
    4sCn/+G+pP/gvKL/37qg/924nf/ctpv/27SY/9qzlv/98+3//PHq//zw6P/ls5L/YEg
    w/wAAAAAAAAAAjk0z8Nq2qf+xclj/0720///////68vD/6MS9///////z4t3/2Z6T//
    369v/8+PP/+/bv/828sP/U0tL/6+no//78+//+/Pv//vz6//77+f/++vj//vn2//749
    P/99/P//fbx//307v/98u3//fHq/+S1lv9gSDD/AAAAAAAAAACQUDbw2bWm/6RTMf+4
    jXv///////nx7/+3XEX/9+3q//Hg3P+zVDv/7NTN//369v/8+PP/2MvB/9HPz//q6Oj
    //vz8///9/P/OoYH/zqGB/86hgf/OoYH/zqGB/86hgf/OoYH//vXw//307v/98uz/4r
    ea/2BIMP8AAAAAAAAAAJJSOfDYtKT/ok8s/6RoT////////v38/6RHJ/+/fWf/79/Z/
    6dNL/+5cln//Pj1//369v/j2NH/z87N/+no5//+/fz///38//PKqv/mvqL/58On/+e/
    pP/is5P/5LaX/86hgf/+9vH//fTv//3z7f/huZz/YEgw/wAAAAAAAAAAlFU88Nizpf+
    iTyz/kEUm////////////n0gk/8WQev/Il4P/oEon/9WwoP/Ooo///vz6//Dq5f/My8
    v/6Ofn//79/f///v3/88qq/9mpiP/fspL/5bmb/+Cxkv/kt5n/zqGB//728v/99vD//
    fPu/+G6n/9gSDD/AAAAAAAAAACWWD/w2LSl/6RPLf+LORb/8evp//////+lVTP/6dbO
    /7FrTv+fSSb//v38/69nSf/v4dn/+/j2/8nIyP/m5eX//fz8///+/f/zyqr/6cGl/+j
    DqP/mvqL/3q+O/+O4mf/OoYH//vfz//328f/99e//37uh/2BIMP8AAAAAAAAAAJlbQf
    Dmy8H/x451/7F3X//h1c//+/j3/6ZXNv/VsKD/7+Hc/6ZXNv//////wYly/7yAZ//+/
    vz/y8rK/+Xk5P/9/Pz///79//PKqv/049f/7NjK//v18P/kwqr/48Gn/+K/pf/eu6X/
    3rul/967pf/fvKP/YEgw/wAAAAAAAAAAm15E8OnQx//PmoX/vYZx/9bCuv/59PH/69j
    R/+zb1P/////////////////////////////////R0ND/5eTk//z7+////v3/88qq//
    359v/+/f3/69fJ/76snf+MdmP/iHJe/4FrV/96Y07/cltF/2pTPP9gSDD/AAAAAAAAA
    ACVXEPk59DG/9eql//IlH//z7Oo////////////////////////////////////////
    /////////+Df3//p6Oj//fz8///+/f/zyqr///////v07//049f/v62d/+zSv//qzrv
    /58q1/+TErv/hvqn/YEgw/yUkJCkAAAAAAAAAAG1EM6XVsKH/8N3W/9Sij//VsaL///
    ////////////////////38/P/x6eX/8+zq//Tv7v/cysL/9PPz//v6+v/+/f3///7+/
    /PKqv/zyqr/88qq//PKqv/Brp///+7k//vo3P/03tD/7tTD/2BIMP8lJCQpGBgXGgAA
    AAAAAAAAIBQPMKhrUfzkyL7/8uDa/9KllP/SpZT/0qWU/9Wol//XqZj/16mY/9iqmP/
    SpZT/0qWU/7WEbv/8+/v//v39///+/v///v7///79//7+/f///fz///37/8KwoP//7u
    T/++jc//Xe0P9lTjb/JSQkKRgYFxoAAAAAAAAAAAAAAAAAAAAAQSkgYKZrUvbUrp//9
    eXf//Tk3v/05N7/9OTe//Xl3//15d//9eXf//Xl3//15d//u4hz//79/f///v7///7+
    ///+/v///v3///39///9/P///fv/xLKj///u5P/76Nz/bFU+/yUkJCkYGBcaAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAFg4LIXFJOKWcZU3kr3JX/69yV/+vclf/r3JX/69yV/
    +vclf/r3JX/69yV/+9i3T///7+///+/v///v7///7+///+/v///v3///39///9/P/Gt
    KT//+7k/4NuWf8lJCQpGBgXGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAADRv7H////////////////////////////////////////////////////
    ////+/v////7///7+///+/v///f3///38/8i1pf+chnT/JSQkKRgYFxoAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANG/sf/Rv7H/0b+x/9G/s
    f/QvrD/z72v/8+9r//PvK7/zbut/827rP/Nuaz/zLmr/8u4qv/Kt6n/yreo/8m2p//I
    tqf/ybam/yUkJCkXFxcZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAYAAAADAAAAAAAAAISAAAADAAAAAEAAABSAAAAcAEAAAEA
    AAD1////AAAAAAAAAAAAAAAAkAEAAAAAAAAAQAAiVABhAGgAbwBtAGEAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT310CQ
    AA3LMCWf//AADy4QAAAABkWczmMADASrNZAAAAAAAAAADwtAoAAAAAAEkaZlkAAAAAv
    EqzWQAAAADLdGRZAACJAXznMAAAbI8BfOcwAAAAAAAAAAAAAAAAAAAAiQFcf0daBAAA
    AGzmMAAXdGRZAgAAAAFKT33o5TAAAAAAADD6MAA0mDN3BZtGCv7///8QTjd3IU83dwA
    AZFkAAAAAdAkAAMzmMAAAAAAAwOYwAPW4LnbA5jAAB7kudgAAZFkAAAAAAABkWQAAAA
    Dc5jAA3OYwACDnMAAhZR9YBQAAANzmMAAAbI8BfOcwAAAAAAAkAAAA3LMCWSA3A1lkd
    gAIAAAAACUAAAAMAAAAAQAAAFQAAAC0AAAAAAAAACIAAABkAAAALgAAAAEAAAAAAA1C
    AAANQgAAAAAiAAAAEQAAAEwAAAAEAAAAAAAAAAAAAABmAAAAQgAAAHAAAABFAG0AYgB
    lAGQAZABlAGQAIABEAG8AYwB1AG0AZQBuAHQAAAAGAAAACAAAAAYAAAAGAAAABgAAAA
    YAAAAGAAAABgAAAAMAAAAHAAAABgAAAAUAAAAGAAAACAAAAAYAAAAGAAAABAAAACUAA
    AAMAAAADQAAgEYAAAAgAAAAEgAAAEkAYwBvAG4ATwBuAGwAeQAAAAAARgAAADAAAAAk
    AAAARQBtAGIAZQBkAGQAZQBkACAARABvAGMAdQBtAGUAbgB0AAAARgAAAGAAAABUAAA
    AQwA6AFwAUABSAE8ARwBSAEEAfgAxAFwATQBJAEMAUgBPAFMAfgAxAFwATwBmAGYAaQ
    BjAGUAMQAyAFwAVwBJAE4AVwBPAFIARAAuAEUAWABFAAAARgAAABAAAAAEAAAAAQAAA
    EYAAAAgAAAAEgAAAEkAYwBvAG4ATwBuAGwAeQAAAAAADgAAABQAAAAAAAAAEAAAABQA
    AAA=";

        #endregion

        using (BinaryWriter writer = new BinaryWriter(part.GetStream()))
        {
            writer.Write(System.Convert.FromBase64String(iconBytes));
            writer.Flush();
        }
    }
}
like image 433
George Vovos Avatar asked Apr 06 '17 16:04

George Vovos


1 Answers

Here is a code that will work for most types, but only on a Windows box, as it requires OLE technology. In fact it ultimately depends on how your machine is configured, because of OLE intimacy, so you need to try this a bit.

This is a sample code that creates a .docx file:

// make sure this all is running in an STA thread (for example, mark Main with the STAThread attribute)

// the file you want to embed
string embeddedDocumentPath = @"C:\mypath\myfile.txt";

// a temp file that will be created, and embedded in the final .docx    
string packagePath = @"C:\Temp\test.bin";

// a temp image file that will be created and embedded in the final .docx
string packageIconPath = @"C:\Temp\test.emf";

// package embeddedDocumentPath -> create the two OpenXML embeddings
Packager.PackageFile(packagePath, packageIconPath, embeddedDocumentPath);

// create the word doc
using (var doc = WordprocessingDocument.Create(@"C:\mypath\ContainingDocument.docx", WordprocessingDocumentType.Document))
{
    var main = doc.AddMainDocumentPart();

    // add icon embedding
    var imagePart = main.AddImagePart(ImagePartType.Emf);
    imagePart.FeedData(File.OpenRead(packageIconPath));

    // add package embedding
    var objectPart = main.AddEmbeddedObjectPart("application/vnd.openxmlformats-officedocument.oleObject");
    objectPart.FeedData(File.OpenRead(packagePath));

    // build a sample doc
    main.Document = BuildDocument(main.GetIdOfPart(imagePart), main.GetIdOfPart(objectPart), "Package");
}

private static Document BuildDocument(string imagePartId, string objPartId, string progId)
{
    var shapeId = "R" + Guid.NewGuid().ToString("N");  // come up with a unique name...

    var element =
      new Document(
        new Body(
          new Paragraph(new Run(new Text("This is the containing document."))),
          new Paragraph(new Run(new Text("This is the embedded document: "))),
          new Paragraph(
            new Run(
              new EmbeddedObject(
                new v.Shape(
                    new v.ImageData
                    {
                        RelationshipId = imagePartId,   // rel to image part
                        Title = ""                      // this is important!
                    })
                    {
                        Id = shapeId
                    },
                new ovml.OleObject
                {
                    Type = ovml.OleValues.Embed,
                    DrawAspect = ovml.OleDrawAspectValues.Icon,
                    ProgId = progId,    // COM progid
                    Id = objPartId,     // rel to embedded part
                    ShapeId = shapeId,  // link to shape
                    ObjectId = "R" + Guid.NewGuid().ToString("N") // come up with a unique name...
                }
                  )))
          )
      );
    return element;
}

And here is the utility code that will create a package and an image (EMF) file from the input file. Now, for PDF, this code may fail (like I said it depends if you installed Adobe Reader or not, its version, etc.). If it fails on OleCreateFromFile, then you may try to create/add a registry key named 'PackageOnFileDrop' like this:

HKEY_CLASSES_ROOT\AcroExch.Document.DC\PackageOnFileDrop

or

HKEY_CLASSES_ROOT\Acrobat.Document.DC\PackageOnFileDrop

Which key depends on the version of Acrobat you're using, Usually, the parent key (HKEY_CLASSES_ROOT\AcroExch.Document.DC or HKEY_CLASSES_ROOT\Acrobat.Document.DC should already exist). In doubt, you can create both safely...

I'm not sure why PDF files don't support OleCreateFromFile directly (undercovers, if you have installed Acrobat Reader, this is all handled by AcroRd32.exe which is a COM server provider by Adobe), I think it may be a security feature, and I don't know how Word circumvents this...

public static class Packager
{
  public static void PackageFile(string outputFile, string outputImageFile, string inputFile)
  {
      if (outputFile == null)
          throw new ArgumentNullException(nameof(outputFile));

      if (outputImageFile == null)
          throw new ArgumentNullException(nameof(outputImageFile));

      if (inputFile == null)
          throw new ArgumentNullException(nameof(inputFile));

      IStorage storage;
      CheckHr(StgCreateStorageEx(
          outputFile,
          STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
          STGFMT_DOCFILE,
          0,
          IntPtr.Zero,
          IntPtr.Zero,
          typeof(IStorage).GUID,
          out storage));

      // note 1: if you get 'Use of Ole1 services requiring DDE windows is disabled' error, make sure the executing thread is STA
      //
      // note 2: if you want packager to succeed on PDF files instead of reporting 0x8000ffff (E_UNEXPECTED) or 0x80040005 (OLE_E_NOTRUNNING), make sure
      // there exists a key named "PackageOnFileDrop", like this: HKEY_CLASSES_ROOT\AcroExch.Document.DC\PackageOnFileDrop
      // or like this HKEY_CLASSES_ROOT\Acrobat.Document.DC\PackageOnFileDrop
     // This depends on the version of Acrobat, you can use sysinternals' ProcMon tool to check registry queries

      //
      IDataObject ps; // from System.Runtime.InteropServices.ComTypes
      CheckHr(OleCreateFromFile(
          Guid.Empty,
          inputFile,
          typeof(IDataObject).GUID,
          0,
          IntPtr.Zero,
          IntPtr.Zero,
          storage,
          out ps));

      var format = new FORMATETC();
      format.cfFormat = CF_ENHMETAFILE;
      format.dwAspect = DVASPECT.DVASPECT_CONTENT;
      format.lindex = -1;
      format.tymed = TYMED.TYMED_ENHMF;

      STGMEDIUM medium;
      ps.GetData(ref format, out medium);

      CopyEnhMetaFile(medium.unionmember, outputImageFile);
      DeleteEnhMetaFile(medium.unionmember);

      storage.Commit(0);
      Marshal.FinalReleaseComObject(ps);
      Marshal.FinalReleaseComObject(storage);
  }

  private static void CheckHr(int hr)
  {
      if (hr != 0)
          throw new Win32Exception(hr);
  }

  [InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("0000000B-0000-0000-C000-000000000046")]
  private interface IStorage
  {
      void _VtblGap1_6(); // we only need Commit
      void Commit(int grfCommitFlags);
  }

  [DllImport("ole32.dll")]
  private static extern int StgCreateStorageEx(
      [MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
      int grfMode,
      int stgfmt,
      int grfAttrs,
      IntPtr pStgOptions,
      IntPtr reserved2,
      [MarshalAs(UnmanagedType.LPStruct)] Guid riid,
      out IStorage ppObjectOpen);

  [DllImport("ole32.dll")]
  private static extern int OleCreateFromFile(
      [MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
      [MarshalAs(UnmanagedType.LPWStr)] string lpszFileName,
      [MarshalAs(UnmanagedType.LPStruct)] Guid riid,
      int renderopt,
      IntPtr lpFormatEtc,
      IntPtr pClientSite,
      IStorage pStg,
      out IDataObject ppStg
      );

  [DllImport("gdi32.dll", SetLastError = true)]
  private static extern bool DeleteEnhMetaFile(IntPtr hemf);

  [DllImport("gdi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
  private static extern IntPtr CopyEnhMetaFile(IntPtr hemfSrc, string lpszFile);

  private const int CF_ENHMETAFILE = 14;
  private const int STGM_READWRITE = 2;
  private const int STGM_SHARE_EXCLUSIVE = 0x10;
  private const int STGM_CREATE = 0x1000;
  private const int STGFMT_DOCFILE = 5;
}
like image 58
Simon Mourier Avatar answered Oct 27 '22 21:10

Simon Mourier