I have put together a simple C# Winforms Application that queries AS400 data based on user criteria and returns the results in a ListView Control. On Button_Click() I then store the Headers and Data in a .txt file. Below I am trying to use that .txt file as the DataSource for a Mail Merge word document.
When the code reaches oWrdDoc = oWord.Documents.Open(oTemplatePath); the program seems to just freeze up. Nothing occurs and I cannot step through to the next line. Anyone have ideas for what I am doing wrong?
public void Print(string docLoc, string docSource)
{
try
{
Word.Application oWord = new Word.Application();
Word.Document oWrdDoc = new Word.Document();
oWord.Visible = true;
Object oTemplatePath = "C:\\Users\NAME\\Desktop\\B-AIAddChgDual10-06-NEW.doc";
oWrdDoc = oWord.Documents.Open(oTemplatePath);
Object oMissing = System.Reflection.Missing.Value;
oWrdDoc.MailMerge.OpenDataSource("C:\\Users\\NAME\\Desktop\\Test2.txt", oMissing, oMissing, oMissing,
oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing);
oWrdDoc.MailMerge.Execute();
}
catch (Exception ex)
{
MessageBox.Show("Source:\t" + ex.Source + "\nMessage: \t" + ex.Message + "\nData:\t" + ex.Data);
}
finally
{
//
}
}
EDIT: Turns out I did not have the new Word instance set to Visible = true so when the instance brought up a dialog saying the file was locked for editing (by myself???) it was prompting me to open a Read-Only version, which previously I could not see, making it look like everything was frozen in processing. I've modified my code above to reflect the changes.
Any ideas for why I'm locked out of my own file, and how to prevent this?
These are the dialogs I receive after accepting open a Read-Only document (in order):
After selecting how to replace the fields in the above:
Original Mail Merge Fields:
After Personal Selections:
How do I tell the Word Application to use the '!' character as the Field Delimiter in my C# code?
Also, how do I proceed with the dialogs? I'm assuming I receive each one due to my datasource not containing fields matching those listed as Mail Merge Fields?
Here are my Mail Merge Fields:
-fuldate
-sys
-memno
-name
-address1
-address2
-address3
-sal
And here are my Delimited fields from my .txt DataSource file:
memno!name!addr1!addr2!city!state!zip!old_addr1!old_addr2!old_city!old_state!old_zip
You can set OpenAndRepair Mode to True and then Open the Document
Replace this:
oWrdDoc = oWord.Documents.Open(oTemplatePath);
With following:
oWrdDoc = word.Documents.Open(oTemplatePath, OpenAndRepair: true);
Related
Background
I have a SharePoint 2013 (SP2013) environment (env) where the Word Automation Services (WAS) has stopped working, so my application has be failing to convert XML documents to PDF.
Previous Status
I use OpenXML SDK to convert the XML InfoPath document to Word document (works as expected). Then convert the Word document to PDF using WAS on SP.
Current Status
The WAS stopped working. My application converts the XML to Word but never converts to PDF. As a stop gap I am using a C# code snippet (shown below) to try converting to PDF but I keep getting the error "Object reference not set to an instance of an object."
...
using Word = Microsoft.Office.Interop.Word;
...
string fileName = generatedDoc; //generatedDoc is Word doc converted from XML
string pdfFileName = fileName.Replace("docx", "pdf");
string sourceUrl = siteUrl + "/DocLibMemo/" + fileName;
string destUrl = siteUrl + "/ApprovedMemoPDF/" + pdfFileName;
Convert(sourceUrl, destUrl, Word.WdSaveFormat.wdFormatPDF);
public static void Convert(string input, string output, Word.WdSaveFormat format)
{
// Create an instance of Word.exe
Word._Application oWord = new Word.Application();
// Make this instance of word invisible (Can still see it in the taskmgr).
oWord.Visible = false;
oWord.ScreenUpdating = false;
// Interop requires objects.
object oMissing = System.Reflection.Missing.Value;
object isVisible = false;
object readOnly = false;
object doNotSaveChanges = Word.WdSaveOptions.wdDoNotSaveChanges;
object oInput = input;
object oOutput = output;
object oFormat = format;
// Load a document into our instance of word.exe
Word._Document oDoc = oWord.Documents.Open(
ref oInput, ref oMissing, ref readOnly,
ref oMissing, ref oMissing, ref oMissing, ref oMissing,
ref oMissing, ref oMissing, ref oMissing, ref oMissing,
ref isVisible, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
// Make this document the active document.
oDoc.Activate(); // The execption is hit here
// Save this document using Word
oDoc.SaveAs(ref oOutput, ref oFormat, ref oMissing, ref oMissing,
ref oMissing, ref oMissing, ref oMissing, ref oMissing,
ref oMissing, ref oMissing, ref oMissing, ref oMissing,
ref oMissing, ref oMissing, ref oMissing, ref oMissing);
// Always close Word.exe.
oWord.Quit(ref oMissing, ref oMissing, ref doNotSaveChanges);
}
The above snippet worked when I tested it using a console application and the Word files was on my C drive. However now that the Word files are on a SP library It doesn't convert to PDF.
I have encountered with the same problem. SharePoint library is a network drive which doesn't have a physical location. The files of SharePoint are indeed saved in database, so that is the reason why we cannot write and save files in SharePoint library. MS Office has a different approach of saving files to SP Library, it actually upload files instead of saving them directly.
Solution for the problem is to take a local copy of the Word file and make changes to the local copy and upload(i.e Copy) it to the same location in the SharePoint library.
Hope this helps.
Thanks.
My question is how to we change the excel filename without we manual changing by our own?
Example , i get a list of naming from excel format from vendor and he will put on the specified location . I need to run a program which using this file to generate a cancellation progress with a specific format of excel in MMYYSP15.
Here my code and i wish to add on the function as i need . Kindly advise
object oMissing = System.Reflection.Missing.Value;
Microsoft.Office.Interop.Excel.ApplicationClass xl = new Microsoft.Office.Interop.Excel.ApplicationClass();
Microsoft.Office.Interop.Excel.Workbook xlBook;
Microsoft.Office.Interop.Excel.Worksheet xlSheet;
//System.Threading.Thread.CurrentThread.CurrentCulture = oldCI;
string laPath = System.IO.Path.GetFullPath("D:\\New & Renewal Summary Report 201409.xls");
xlBook = (Microsoft.Office.Interop.Excel.Workbook)xl.Workbooks.Open(laPath, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing);
xlSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlBook.Worksheets.get_Item(1);
xlSheet.Name = "Sheet 1";
xlBook.Save();
xl.Application.Workbooks.Close();
You have to call xlBook.SaveAs. You can't change the current file's name.
xlBook.SaveAs(Filename: yourFileName);
If you do not intend to do anything with the Excel workbook itself, a simple File.Move would do.
File.Move(fromFileName, toFileName);
I am building a word document using Office's Word interop. I am settings heading styles via code. The content is getting styled correctly, however, one opening the created word document, the style is not selected for the selection in the header. This is causing the table of contents to not find the heading.
object oMissing = Missing.Value;
//Start Word and create a new document.
var application = new Application();
application.Visible = true;
var document = application.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing);
foreach (var member in assembly.Members)
{
//Insert a paragraph at the beginning of the document
var paragraph = document.Content.Paragraphs.Add(ref oMissing);
paragraph.set_Style(WdBuiltinStyle.wdStyleHeading1);
paragraph.Range.Text = member.MemberName;
paragraph.Range.InsertParagraphAfter();
}
document.TablesOfContents.Add(document.Content, true /*use heading styles*/, oMissing, oMissing, oMissing,
oMissing, oMissing, oMissing, oMissing, oMissing,
oMissing, oMissing);
document.SaveAs(#"C:\test.docx", oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing);
document.Close();
application.Quit();
This example causes the table of contents to display "No entries found".
Note that the content is correctly styled with the heading style. However, when I open the document manually and select "Heading 1", then the table of contents finds the entry correctly.
Any ideas why this would happen? I am callign set_Style with a built in style name. Why does it apply the style but not actually get treated as the style (in this case, a heading)?
UPDATE
Adding the following code seems to make only the first heading recognized as a heading in Word.
foreach (var member in assembly.Members)
{
document.ActiveWindow.Selection.set_Style(WdBuiltinStyle.wdStyleHeading1);
//Insert a paragraph at the beginning of the document
var paragraph = document.Content.Paragraphs.Add(ref oMissing);
paragraph.set_Style(WdBuiltinStyle.wdStyleHeading1);
paragraph.Range.Text = member.MemberName;
paragraph.Range.InsertParagraphAfter();
}
I have noticed difference in the order of the operations
Follow code will not generate real header, it is only looks like header
/*1*/ paragraph.set_Style(WdBuiltinStyle.wdStyleHeading1);
/*2*/ paragraph.Range.Text = "Head";
/*3*/ paragraph.Range.InsertParagraphAfter();
To solve propblem you should use this code
/*1*/ paragraph.Range.Text = "Head";
/*2*/ paragraph.set_Style(WdBuiltinStyle.wdStyleHeading1);
/*3*/ paragraph.Range.InsertParagraphAfter();
After Adding the Paragraph select the paragraph it might work .
paragraph.Range().Select()
I had to do something similar, but the paragraph was not getting the style...
I used then TypeText instead, and then worked...I used a Word.Selection too. I´m merging documents, but I guess your code should be something like this:
(after the line: var document = application.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing)).I´m using Word = Microsoft.Office.Interop.Word on the usings, so that "Word" before the selection is realated to that.
Word.Selection selection = application.Selection;
foreach (var member in assembly.Members)
{
selection.TypeText(member.MerberName);
selection.set_Style(WdBuiltinStyle.wdStyleHeading1);
selection.TypeParagraph();
}
That worked here and the TOC is getting filled.
How can I copy contents of one word document and insert it in another pre-existing word document using C#. I've had a look around but everything looks really complicated (I'm a newbie). Surely there must be an easy solution?
I have found this code which gives me no errors but it doesnt seem to be doing anything. It's certainly not copying into the correct word doc. put it that way.
Word.Application oWord = new Word.Application();
Word.Document oWordDoc = new Word.Document();
Object oMissing = System.Reflection.Missing.Value;
oWordDoc = oWord.Documents.Add(ref oTemplatePath, ref oMissing, ref oMissing, ref oMissing);
oWordDoc.ActiveWindow.Selection.WholeStory();
oWordDoc.ActiveWindow.Selection.Copy();
oWord.ActiveDocument.Select();
oWordDoc.ActiveWindow.Selection.PasteAndFormat(Word.WdRecoveryType.wdPasteDefault);
P.S these word docs are .doc
Word.Application oWord = new Word.Application();
Word.Document oWordDoc = new Word.Document();
Object oMissing = System.Reflection.Missing.Value;
object oTemplatePath = #"C:\\Documents and Settings\\Student\\Desktop\\ExportFiles\\" + "The_One.docx";
oWordDoc = oWord.Documents.Add(ref oTemplatePath, ref oMissing, ref oMissing, ref oMissing);
oWordDoc.ActiveWindow.Selection.WholeStory();
oWordDoc.ActiveWindow.Selection.Copy();
oWord.ActiveDocument.Select();
// The Visible flag is what you've missed. You actually succeeded in making
// the copy, but because
// Your Word app remained hidden and the newly created document unsaved, you could not
// See the results.
oWord.Visible = true;
oWordDoc.ActiveWindow.Selection.PasteAndFormat(Word.WdRecoveryType.wdPasteDefault);
It's funny to see all the C# guys asking now the questions the VBA developers have answered since 15 years. It's worth to dig in VB 6 and VBA code samples, if you have to work with Microsoft Office automation issues.
For the point "nothing happens" it's simple: if you start Word through automation, you must set the application also to visible. If you run your code, it will work, but Word remains an invisible instance (open Windows Task Manager to see it).
For the point "easy solution", you can try to insert a document at a given range with the InsertFile method of the range, e.g. like this:
static void Main(string[] args)
{
Word.Application oWord = new Word.Application();
oWord.Visible = true; // shows Word application
Word.Document oWordDoc = new Word.Document();
Object oMissing = System.Reflection.Missing.Value;
oWordDoc = oWord.Documents.Add(ref oMissing);
Word.Range r = oWordDoc.Range();
r.InsertAfter("Some text added through automation!");
r.InsertParagraphAfter();
r.InsertParagraphAfter();
r.Collapse(Word.WdCollapseDirection.wdCollapseEnd); // Moves range at the end of the text
string path = #"C:\Temp\Letter.doc";
// Insert whole Word document at the given range, omitting page layout
// of the inserted document (if it doesn't contain section breakts)
r.InsertFile(path, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
}
NOTE: I was using framework 4.0 for this example, which allows for optional parameters.
How can I convert an RTF file to a PDF one? I have the adobe PDF printer, should I use it? If so, how can I programmatically access it?
You can use a PDF printer, but then you still have a few problems to solve.
In order to handle text that spans multiple pages, you need this article to create a descendant of RichTextbox that handles the EM_FORMATRANGE Message.
There are a lot of (free) PDF printer out there, but I found that only BioPdf will let you control the filename of the output. They also have reasonable rates for licensed versions.
I have used this to create complex reports (combinations of multiple RTF segments and custom graphics) as attachments for emailing.
You could use the virtual print Driver doPdf http://www.dopdf.com/ if this is permitted on the production machine. This will convert more or less any file type to a pdf format not just rtf. It just appears as another printer within Print Manager once installed.
To use it in say winforms code I adapted the code found on the msdn printing example http://msdn.microsoft.com/en-us/library/system.drawing.printing.printdocument.aspx
private void button1_Click(object sender, EventArgs e)
{
try
{
streamToPrint = new System.IO.StreamReader
(#"F:\temp\labTest.txt");
try
{
printFont = new Font("Arial", 10);
PrintDocument pd = new PrintDocument();
pd.PrinterSettings.PrinterName = "doPDF v6";//<-------added
pd.PrintPage += new PrintPageEventHandler
(this.pd_PrintPage);
pd.Print();
}
finally
{
streamToPrint.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The only part of the code I needed to add was that marked above e.g. pd.PrinterSettings.PrinterName = "doPDF v6";
There may be a printer enumeration method which would be more elegant and robust and against this one could test to see if the print driver existed perhaps against a config file setting.
Update:
Handling multiple pages is taken care of in this method : this.pd_PrintPage as per the msdn sample.
PrintDocument supports from and to page printing.
DoPdf will pops up a fileSaveAsDialog box automatically so the files can be saved as a pdf document.
What about rtf though?
A Microsoft format not supported very well so it would seem. This article http://msdn.microsoft.com/en-us/library/ms996492.aspx with demo code uses the RichTextBox as a starting point and by using P/Invoke leverages the power of Win32 to print RTF as WYSIWG. The control defines it's own page length method replacing the one used above in the code snippet and still uses PrintDocument so it should be easy to use. You can assign any rtf using Rtb.rtf method.
An RTF document has to be read and interpreted by some app that can understand that format. You would need to programmatically launch that app, load your RTF file, and send it to the PDF printer. Word would be good for that, since it has a nice .NET interface. An overview of the steps would be:
ApplicationClass word = new ApplicationClass();
Document doc = word.Documents.Open(ref filename, ...);
doc.PrintOut(...);
You will need to use the Microsoft.Office.Interop.Word namespace and add a reference to the Microsoft.Office.Interop.Word.dll assembly.
Actually, none of these are terribly reliable or do what I want. The solution is simple, install Adobe Acrobat and just have it open the RTF file using the Process class.
I also found a more reasonable approach. I save the file as an RTF, the open it in word, and save it as PDF (Word's Print As PDF plugin must be installed)
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Personal Document File (*.pdf)|*.pdf";
if (sfd.ShowDialog() == DialogResult.OK) {
String filename = Path.GetTempFileName() + ".rtf";
using (StreamWriter sw = new StreamWriter(filename)) {
sw.Write(previous);
}
Object oMissing = System.Reflection.Missing.Value; //null for VB
Object oTrue = true;
Object oFalse = false;
Microsoft.Office.Interop.Word.Application oWord = new Microsoft.Office.Interop.Word.Application();
Microsoft.Office.Interop.Word.Document oWordDoc = new Microsoft.Office.Interop.Word.Document();
oWord.Visible = false;
Object rtfFile = filename;
Object saveLoc = sfd.FileName;
Object wdFormatPDF = 17; //WdSaveFormat Enumeration
oWordDoc = oWord.Documents.Add(ref rtfFile, ref oMissing, ref oMissing, ref oMissing);
oWordDoc.SaveAs(ref saveLoc, ref wdFormatPDF, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing,
ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
oWordDoc.Close(ref oFalse, ref oMissing, ref oMissing);
oWord.Quit(ref oFalse, ref oMissing, ref oMissing);
//Get the MD5 hash and save it with it
FileStream file = new FileStream(sfd.FileName, FileMode.Open);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
using (StreamWriter sw = new StreamWriter(sfd.FileName + ".md5")) {
sw.WriteLine(sfd.FileName + " - " + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToShortTimeString() + " md5: " + BinaryToHexConverter.To64CharChunks(retVal)[0]);
}
}