Consider this scenario. You are adding VSTO customizations dynamically to Word 2003/Excel 2003 documents using ServerDocument.AddCustomization method. The ServerDocument.AddCustomization method succeeds. But when you try to open this customized document, the customization does not load. If you have the “VSTO_SUPPRESSDISPLAYALERTS” environment variable set to “0” you will get the following error: “The customization assembly could not be found or could not be loaded.”
This is a known issue with Word 2003 and Excel 2003 VSTO customizations, containing embedded customized documents. It doesn’t occur with Word 2007 (or Excel 2007) VSTO customization.
How to avoid this?
To workaround this issue, you can add the customization to the outer document using VSTO related document properties.
For more details on document properties, please visit Custom Document Properties Overview
The _AssemblyLocation custom document property needs to point to the deployment manfiest location. In order to generate the deployment manifest, follow these steps:
The document, assemblies, and manifests are copied to the publish location, with the assemblies and application manifest (<filename>.dll.mainfest) in a subdirectory under the document and deployment manifest (<filename>.application)
For more details please refer to: How to: Deploy Solution Files Using the Publish Wizard (2003 System)
You can add these custom document properties using either of the two approaches:-
Approach 1: Adding Custom Document Properties programmatically using IPropertyStorage
The other approach that you can use is to add the properties using IPropertyStorage interface. You can use the concepts demonstrated in DSOFile sample, to add the custom document properties. This sample can be downloaded using the link http://support.microsoft.com/kb/224351.
Let’s see how to use DSOFile sample to attach custom properties to the document, but before that we need to ensure that DSOFile.dll is registered on the development machine. The self-extracting setup installs and registers the DsoFile.dll component in a location that you want. But, if you move the DLL to another location or to another computer, you have to re-register the DLL before you can use it again. To do this, type regsvr32 [filepath]\dsofile.dll in the Run dialog box on the Start menu (or from elevated rights command prompt in case of Vista).
Let’s start by creating a new windows application project which references DSOFile.dll and adds these properties.
1: string fileName = @"C:\temp\Sample.doc";
2: object vstoDocumentLocation = @"C:\Temp\WordDocument.application";
3:
4: DSOFile.OleDocumentPropertiesClass doc = new DSOFile.OleDocumentPropertiesClass();
5: doc.Open(fileName, false, DSOFile.dsoFileOpenOptions.dsoOptionDefault);
6: object assemblyName = "*";
7: DSOFile.CustomProperties customProperties = doc.CustomProperties;
8: //Check if _AssemblyName property already exists
9: try
10: {
11: DSOFile.CustomProperty nameProp = customProperties["_AssemblyName"];
12: nameProp.set_Value(ref assemblyName);
13: }
14: catch (Exception)
15: {
16: customProperties.Add("_AssemblyName", ref assemblyName);
17: }
18: //Check if _AssemblyLocation property already exists
19: try
20: {
21: DSOFile.CustomProperty loc = customProperties["_AssemblyLocation"];
22: loc.set_Value(ref assemblyLocation);
23: }
24: catch (Exception ex)
25: {
26: customProperties.Add("_AssemblyLocation", ref vstoDocumentLocation);
27: }
28:
29: doc.Save();
30: doc.Close(false);
Approach 2: Add the properties using Word/Excel Object Model:
One more approach that you can use is to add the properties using Excel/Word object model.
Let’s start by creating a new windows application project which references Excel/Word libraries and adds these properties using CustomDocumentProperties Object.
Create an Automation Client for Microsoft Word
1: private void button1_Click(object sender, EventArgs e)
2: {
3: }
4:
5: With:-
6:
7: private void button1_Click(object sender, EventArgs e)
8: {
9: Word.Application oWord;
10: Word._Document oDoc;
11: object oMissing = Missing.Value;
12: object oDocBuiltInProps;
13: object oDocCustomProps;
14: object fileName = @"C:\test.doc";
15:
16: //Create an instance of Microsoft Word and make it visible.
17: oWord = new Word.Application();
18: oWord.Visible = true;
19:
20: //Create a new Document and get the BuiltInDocumentProperties collection.
21: oDoc = oWord.Documents.Open(ref fileName,ref oMissing,ref oMissing,
22: ref oMissing,ref oMissing,ref oMissing,
23: ref oMissing,ref oMissing,ref oMissing,
24: ref oMissing,ref oMissing,ref oMissing,
25: ref oMissing,ref oMissing,ref oMissing,
26: ref oMissing);
27: oDocBuiltInProps = oDoc.BuiltInDocumentProperties;
28: Type typeDocBuiltInProps = oDocBuiltInProps.GetType();
29:
30: //Get the Author property and display it.
31: string strName;
32: string strValue;
33:
34: //Add a property/value pair to the CustomDocumentProperties collection.
35: oDocCustomProps = oDoc.CustomDocumentProperties;
36: object oDocAssemblyNameProp;
37: Type typeDocAssemblyNameProp;
38: Type typeDocCustomProps = oDocCustomProps.GetType();
39: try
40: {
41: strName = "_AssemblyName";
42: oDocAssemblyNameProp = typeDocCustomProps.InvokeMember("Item",
43: BindingFlags.Default | BindingFlags.GetProperty,
44: null, oDocCustomProps, new object[] { strName });
45: typeDocAssemblyNameProp = oDocAssemblyNameProp.GetType();
46: strValue = typeDocAssemblyNameProp.InvokeMember("Value",
47: BindingFlags.Default | BindingFlags.GetProperty,
48: null, oDocAssemblyNameProp, new object[] { }).ToString();
49: }
50: catch
51: {
52: strName = "_AssemblyName";
53: strValue = "*";
54: object[] oArgs = { strName, false, MsoDocProperties.msoPropertyTypeString, strValue };
55: typeDocCustomProps.InvokeMember("Add",
56: BindingFlags.Default | BindingFlags.InvokeMethod, null,
57: oDocCustomProps, oArgs);
58: }
59: try
60: {
61: strName = "_AssemblyLocation";
62: oDocAssemblyNameProp = typeDocCustomProps.InvokeMember("Item",
63: BindingFlags.Default | BindingFlags.GetProperty,
64: null, oDocCustomProps, new object[] { strName });
65:
66: typeDocAssemblyNameProp = oDocAssemblyNameProp.GetType();
67: strValue = typeDocAssemblyNameProp.InvokeMember("Value",
68: BindingFlags.Default | BindingFlags.GetProperty, null,
69: oDocAssemblyNameProp, new object[] { }).ToString();
70: }
71: catch
72: {
73: strName = "_AssemblyLocation";
74: strValue = @"C:\WordDocument.application"; ;
75: object[] oArgs = { strName, false, MsoDocProperties.msoPropertyTypeString, strValue };
76: typeDocCustomProps.InvokeMember("Add",
77: BindingFlags.Default | BindingFlags.InvokeMethod,
78: null, oDocCustomProps, oArgs);
79: }
80:
81: oDoc.Save();
82: oDocBuiltInProps = null;
83: oDocCustomProps = null;
84: oDoc.Close(ref oMissing, ref oMissing,ref oMissing);
85: oDoc = null;
86: oWord.Quit(ref oMissing, ref oMissing, ref oMissing);
87: oWord = null;
88:
89: }
1: using Microsoft.Office.Core;
2: using Word = Microsoft.Office.Interop.Word;
3: using System.Reflection;
Note:- The DocumentProperties and the DocumentProperty interfaces are late bound interfaces. To use these interfaces, you must treat them like you would an IDispatch interface.
For more details, please refer to: How to Use Automation to Get and to Set Office Document Properties with Visual C#.
The next time the document is opened and saved, the Visual Studio Tools for Office runtime attaches the solution assembly to the document, and creates the Runtime Storage Control, if necessary. The Visual Studio Tools for Office runtime also sets the value of the _AssemblyLocation custom document property to the GUID for the Runtime Storage Control.