如何在没有互操作程序集的情况下将任何文件类型嵌入到 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,此代码可能会失败(正如我所说,这取决于您是否安装了 Adobe 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;
}
我正在尝试这样做 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,此代码可能会失败(正如我所说,这取决于您是否安装了 Adobe 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;
}