I'm trying to automate a tedious process that currently involves launching Word, Creating a new document from a .dot, saving it, running one or two plug-ins that were written in C# using VSTO, saving it again, exiting the document, and exiting Word.
I want to write a C# commandline app that the user can launch with one or two args (conveying all the information that would normally require interaction with dialogs in Word), then walk away from as it runs without further interaction... suppressing any and all focus-stealing by Word while it's running if necessary and possible.
Is there some straightforward way to accomplish this? Here's a Java-like pseudocode example of what I have in mind:
// magic to non-interactively launch Word and expose it as an object
WordHost word = xx;
// create new Word document based on a specific template that isn't the default one.
WordDocument doc = MSWord.create("z:\path\to\arbitraryTemplate.dot");
// If we can avoid physically saving it at this point and just assign a concrete
// file path, it would be even better because the network is glacially slow.
doc.saveAs("z:\path\to\newDoc.docx");
// someZeroArgPlugin and aTwoArgPlugin are VSTO plugins written with C#
doc.someZeroArgPlugin();
doc.aTwoArgPlugin("first", "second");
// done!
doc.save();
doc=null;
word=null; // something like word.unload() first?
// now do more things that don't involve Word directly...
Assuming I'm on the right track...
I'm pretty sure I can find most of what I need to know by searching... once I figure out what I need to be searching for. What should I be searching for?
What kind of project do I want to be creating in Visual Studio? A .net 4.5 C# console application? A Word 2010 Add-In? Some other kind of project?
Details that might or might not make a difference:
my program will only be run on computers that have Word 2010 installed. Compatibility with older versions isn't necessary.
It would be nice if it can run under Vista, but it only has to work under Win7.
I have Visual Studio Ultimate 2012
Here's what you'll need to do:
Have Visual Studio and Office installed.
Create a C# console project using the .NET framework of your choice (recommend 4.0 or above).
Add a reference to the Word COM library (Project menu => Add Reference, COM tab, Microsoft Word XX.0 Object library -- Word 2010 is 14.0).
Set the Embed Interop Types setting to false for the references added above
Expand References in Solution Explorer
Select Microsoft.Office.Core, Microsoft.Office.Interop.Word and VBIDE
Right-click and select Properties to bring up the Properties panel for the references.
In the Properties panel, set Embed Interop Types to False
Code away.
Here's some sample code.
using System;
using Microsoft.Office.Interop.Word;
namespace CSharpConsole
{
static class Program
{
[STAThread]
static void Main()
{
var application = new ApplicationClass();
var document = application.Documents.Add();
document.SaveAs("D:\test.docx");
application.Quit();
}
}
}
For more information, see http://msdn.microsoft.com/en-us/library/office/ff601860(v=office.14).aspx
Related
I'm looking for some help. I am in need of figuring out how to get an object reference to Microsoft Access 2013 so that I can (through Automation) call some of the already defined functions in the accdb. For instance; I want to automate the process of "RelinkODBCTables" function which repoints the linked tables to another data source from my .net core 3.0 c# application.
I've not been able to successfully get a reference to interop but I may not be doing it correctly.
Any help would be greatly appreciated.
D-
If you want to
Create a instnce of Access.
Call a VBA sub (say your relink code).
Close the the database.
quit access.
You could use this code:
{
object AccessApp;
AccessApp = Interaction.CreateObject("Access.Application");
AccessApp.OpenCurrentDatabase(#"c:\test\test44.accdb");
AccessApp.Run("MyLinker");
AccessApp.CloseCurrentDatabase();
AccessApp.Quit();
}
So, you don't need any referance at all. Just create a instance of the given applicatin (word, Excel or as per above Access).
At that point, you have full use of the object model, and can use run to call some VBA routine. In above, we call a VBA Sub called MyRelinker.
About the only caution here is that when you create the instance of that object, then all startup code of the application will run. So, if on startup the forms and UI that the developer of the Access program launches any prompt, then you can't "answer" that prompt. So, how well this works will VERY much depend on how nice the application plays on startup, and that calling any of those VBA routines does not trigger some kind of prompt(s) in that Access application. If it does, then you in trouble, since you can't "answer" any of the forms or code prompts that access may very well throw up at the end user.
And, if you do want some "inteli-sense" during coding, then you can add a office "interop" reference to your project. Its not required, but if you not really fluent in the Access VBA + object model, then in place of CreateObject("Access.Application"), if you do referance the office assemby, say this one:
Microsoft.Office.Interop.Access
C:\Program Files (x86)\Microsoft Visual Studio 12.0\
Visual Studio Tools for Office\PIA\Office14\
Microsoft.Office.Interop.Access.dll
Then you code becomes this:
{
Microsoft.Office.Interop.Access.Application AccessApp =
new Microsoft.Office.Interop.Access.Application();
AccessApp.OpenCurrentDatabase(#"c:\test\test44.accdb");
AccessApp.Run("MyLinker");
AccessApp.CloseCurrentDatabase();
AccessApp.Quit();
}
However, while you get stronger typing with the reference, you often will "tie" you code to a given version of Access, and a simple use of CreateObject() quite much means that you can create a instance of Any Access installed on the target computer, and it should work going back all the way to office 2000 - a good 20 years of "coverage".
Keep in mind that you CAN NOT create a instance of the Access runtime, the target computer will requite a full version to "create" a instance of the Access.Application object.
You "can" automate the runtime version. This involves launching Access runtime (via Shell()), and then grabbing an instance with "GetObject()" in place of CreateObject().
Edit
I should point out that in the 2nd above code example, and using the office interop-assembly reference, I choose office 14, which is office 2010. In your case, you have to use office 15 (2013).
I have written a console application which uses UDC (universal document convertor) to convert visio diagrams to an image (more specifically a jpeg).
Everything is working fine (for visio files in 2007 and 2010), and I am using Microsoft.Office.Interop.Visio version 14.0.0.0. I thought I would do a test by creating a visio diagram in Visio 2003 and testing the application. It works fine however, I get the following popup "The Microsoft Visio building plan shapes in this drawing must be updated to function in the current version of Visio". If I click "Yes" then the program works fine. This code will eventually be running as a service and I was wondering if there is anyway to supress these warnings? Or tell it to do any conversion automatically?
We seem to have found the answer (trying nearly every property on the Visio.Application class!):
vApp = new Microsoft.Office.Interop.Visio.Application();
vApp.Visible = false;
vApp.Settings.ShowFileOpenWarnings = false;
vApp.EventsEnabled = 0;
It seems the EventsEnabled = 0 did the trick!
Hope this may be useful to someone eventually.. I spent about 4 hours on it :)
You can set vApp.AlertResponse = vbYes, which suppresses the dialog and chooses Yes for you.
I have written a C# program which import a product list from a .xlsx file and let the user create an order based on that product list.
When the user is finished, the program builds one or more system specifications based on the order.These specifications is written to a .docx file. I have Office 2007 installed on the computer and are using the Microsoft.Office.Interop.Excel and the Microsoft.Office.Interop.Word namespaces.
The problem:
After I have runned the program, Windows Explorer crashes very often and has to restart. This happens when navigating in folders or when right- clicking on folders etc.
This also happens after the program have been closed and the only solution to make it stop is to restart the computer. It seems like it only happens when I have created the output files (.docx). If i start the program and use it like I normally do, but without creating the word files, the problem don't seem to occur.
After the program have created the output files, Word gets "Visible" to the user for manual editing. The user closes the word application when finished editing the documents.
What can make the Windows Explorer crash when running word automation?
I really need help on this one. Any suggestions are welcome.
After execution, do you have ghost excel.exe and word.exe processes remaining?
These ghost are likely to make the system unstable.
You're likely not releasing properly the COM objects you instantiated via automation.
Use Marshal.ReleaseComObject(yourobj); on each and every COM objects you instantiate. It's a real pain, I know.
Note: be sure that you don't instantiate COM objects without knowing it:
mySheet = myExcelObject.workbooks[0].Sheet[0] won't just instantiate a sheet object, but also a workbook object.
Rule of thumb: never ever use a secondary property on a COM object ( foo.bar.baz ) and release everything.
Final note: don't use office automation at all on the server, it's bad, per Microsoft own words, there are fully managed libraries for that.
i have this web application in c# .net running on an external web server.In that i am trying to generate and save the word doc on the server ( without opening it) . It runs fines on my local machine as i am having word installed on my machine but on the server it is showing error on using MyApplication Class. I understand it is not possible to install word on the server . Right now i am referenceing word.interopp assembly in my application .
Is there any better way to solve the issue .
Thanks and regards
Vickey Y.
If you're trying to use Office Interop without installing office on the server, then it's clearly going to fail.
Could you install Word on a different server and expose some sort of web service to create the document for you and serve the file contents back to the original server to save?
You may be able to use some third party libraries which can generate the relevant Word format, but I don't know of any.
What format does it have to be? Is it a complicated document? Could you just use RTF?
We use a third party tool called Aspose.Word. This allows you to perform a whole bunch of Word releated stuff without the need to install Word itself on the server.
the word.interop uses an instance of word to generate and save the documents. Even though it doesnt display the UI, the process of WINWORD will be running. In short, you need word installed on the server to use word.interop. When we did this, we also encountered issues when the servers needed to be compatible with word 2007 documents too, so its not an easy venture working with Word. Hope you get through the issue ok :)
I think you can do something like this.
I should alow you to open a document from the server.
protected void btnCreateWordBulletin_Click(object sender, EventArgs e)
{
String a= Server.MapPath("/Solution/Templates/Sport/Sport.doc");
String b= Server.MapPath("/Solution/Templates/Sport/SportSave.doc");
CreateWordDocument(a, b);
}
protected void CreateWordDocument(object fileName, object saveAs)
{
//Set Missing Value parameter - used to represent
//a missing value when calling methods through interop
object missing = System.Reflection.Missing.Value;
//Setup the Word.App class
Microsoft.Office.Interop.Word.Application WordApp = new Microsoft.Office.Interop.Word.ApplicationClass();
Microsoft.Office.Interop.Word.Document aDoc = null;
// Check to see that file exists
if (System.IO.File.Exists((string)fileName))
{... Activating doc etc...}}
Yes, you can have Word running on server. However, note that opening winword.exe process from web service will probably fire winword.exe as ASPNET user. Some versions of MS word displayed some customization modal form during first run, making it impossible to automate process of using Word in server environment. The solution was making ASPNET “login-enabled” user, logging to server as an ASPNET user, running Word manually, closing all first-time-configuration modal forms, and then setting ASPNET user to its normal state. Since those configuration windows appeared only during first run of winword (more precisely: until configuration was approved by user), this actually worked.
Note: using winword on server needs some legal investigation. As far as I know MS attitude towards such solutions is rather negative, while some legal systems find it perfectly ok. Also take into consideration need for managing winword processes, and … and in fact, this is a bit crude hack.
OfficeWriter is another potential solution for you if you need true DOC and DOCX (as opposed to RTF or some other format):
http://www.officewriter.com
Take a look at OpenXML which is the file format all word (and Office) documents are saved in by default. http://openxmldeveloper.org/
We have a code generator that munges the schema of a given database to automate our inhouse n-tier architecture. The output is various C# partial classes, one per file.
In the code to munge all the strings, we try and keep on top of the indenting and formatting as much as possible, but invariably when you come to open the file in Visual Studio the formatting is awry. A quick ctrl-k, ctrl-d fixes it, but obviously this reformatting is lost the next time the class is generated.
What I'd like to know, is if there's a way I can somehow automatically format the contents of the textfile in the same way Visual Studio does?
Pseudocode
Create "code" object, passing text file to constructor
Invoke "format" method
Re-save text file
Any help greatly appreciated.
EDIT:
I should clarify - I want to be able to invoke the formatting from my C# code that creates the textfile containing my generated C#. The format of the code can be standardised (doesn't have to be per-developer), and I don't want to have to install any 3rd-party apps.
I seem to remember there's a namespace containing loads of classes for creating C# in C#: http://msdn.microsoft.com/en-us/library/system.codedom(VS.80).aspx, but I'm not sure if it contains any classes that could help.
FURTHER EDIT:
My code generator is a winforms app deployed via a click-once install. It's used by many developers in-house. I need a solution that doesn't require each developer to have a tool installed on their machine.
To properly indent code programmatically you would need Microsoft.CodeAnalysis.CSharp nuget package and .NET framework 4.6+. Sample code:
public string ArrangeUsingRoslyn(string csCode) {
var tree = CSharpSyntaxTree.ParseText(csCode);
var root = tree.GetRoot().NormalizeWhitespace();
var ret = root.ToFullString();
return ret;
}
One-liner:
csCode = CSharpSyntaxTree.ParseText(csCode).GetRoot().NormalizeWhitespace().ToFullString();
You may also use NArrange to sort methods in your cs file, organize usings, create regions, etc. Note that NArrange does not indent anything.
Take a look at Narrange.You'll probably need to automate these things as part of the build.
Not sure if it meets all your requirements though.
To quote:
NArrange is a .NET code beautifier
that automatically organizes code
members and elements within .NET
classes.
You can use CodeDOM and the CSharpCodeProvider. It is all in the namespaces Microsoft.CSharp and System.CodeDom.
Her is an example of a property:
StringWriter writer = new StringWriter();
CSharpCodeProvider provider = new CSharpCodeProvider();
CodeMemberProperty property = new CodeMemberProperty();
property.Type = new CodeTypeReference(typeof(int));
property.Name = "MeaningOfLifeUniverseAndEverything";
property.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(42)));
provider.GenerateCodeFromMember(property, writer, null);
Console.WriteLine(writer.GetStringBuilder().ToString());
This code will generate:
private int MeaningOfLifeUniverseAndEverything {
get {
return 42;
}
}
The CodeDOM is a quite chatty way to generate code. The good thing is that you can generate multiple languages. Perhaps you can find a Erlang.NET CodeProvider?
You might be able to do a few shortcuts by using CodeSnippetExpression.
Only if you're running the code generator as a VS add-on - each developer is going to have different settings.
Here's how to do it from the context of a macro or add-in:
var dte = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.8.0");
dte.ExecuteCommand("File.OpenFile", filename);
dte.ExecuteCommand("Edit.FormatDocument", filename);
dte.ActiveDocument.Close(vsSaveChanges.vsSaveChangesYes);
Warning: As #Greg Hurlman says, the output will vary depending on the user's current options.
Edit:
unfortunately your method requires me to have an instance of VS running alongside my winforms app. Can you think of a way to create an instance of VS from within my app (if that's even possible)?
I think it might be possible to do from within your Win.Form app. However, you'll have to have Visual Studio installed on the machine running the code.
Try this:
var dte = (EnvDTE80.DTE2)Microsoft.VisualBasic.Interaction.CreateObject("VisualStudio.DTE.8.0", "");
dte.ExecuteCommand("File.OpenFile", filename);
dte.ExecuteCommand("Edit.FormatDocument", filename);
dte.ActiveDocument.Close(vsSaveChanges.vsSaveChangesYes);
Keep in mind that you'll need references to the EnvDTE80.dll assembly.