如何在没有互操作程序集的情况下将任何文件类型嵌入到 Microsoft Word 中

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

我正在尝试这样做 How can I embed any file type into Microsoft Word using OpenXml 2.0

但仅使用 OpenXml SDK 2.5(无互操作程序集)

我可以使用来自 here
的以下代码示例嵌入其他 word(或 office)文件 (我们需要从 NuGet 和 WindowsBase 添加对 DocumentFormat.OpenXml 的引用)

但我无法重构它以接受任何文件类型(pdf、mp3 等)

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

这是适用于大多数类型的代码,但仅适用于 Windows 框,因为它需要 OLE 技术。其实最终还是要看你的机器是怎么配置的,因为OLE的亲和力,所以你需要稍微尝试一下。

这是创建 .docx 文件的示例代码:

// 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;
}

这里是实用程序代码,它将根据输入文件创建一个包和一个图像 (EMF) 文件。 现在,对于 PDF,此代码可能会失败(正如我所说,这取决于您是否安装了 Adob​​e Reader,它的版本等)。 如果它在 OleCreateFromFile 上失败,那么您可以尝试 create/add 一个名为 'PackageOnFileDrop' 的注册表项,如下所示:

HKEY_CLASSES_ROOT\AcroExch.Document.DC\PackageOnFileDrop

HKEY_CLASSES_ROOT\Acrobat.Document.DC\PackageOnFileDrop

哪个密钥取决于您使用的 Acrobat 版本,通常,父密钥(HKEY_CLASSES_ROOT\AcroExch.Document.DC 或 HKEY_CLASSES_ROOT\Acrobat.Document.DC 应该已经存在).有疑问,您可以安全地创建两者...

我不确定为什么 PDF 文件不直接支持 OleCreateFromFile(卧底,如果你安装了 Acrobat Reader,这一切都由 AcroRd32.exe 处理,这是一个Adobe 的 COM 服务器提供商),我认为这可能是一项安全功能,我不知道 Word 是如何绕过这个的...

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