After doing some research, it seems that you can use Word Interlop to manipulate Word document (Open, modify, change etc.). Like this:
class Program
{
static void Main(string[] args)
{
Application ap = new Application();
Document doc = ap.Documents.Open(#"C:\temp\TestDoc.docx");
doc.Activate();
}
}
But doing it that way, the MS word application itself with the GUI are not showing. I suspect it does not get started at all. I can only see WINWORD.EXE in the Processes tab of the Windows Task Manager but not in Applications tab.
What I want to do is:
Starting the MS Word application
open the document
Show it to the end user, so they can modify/save it.
How can I do this?
Unless you need to control Word application simple Process.Start may be enough:
Process.Start(#"C:\temp\TestDoc.docx")
If you want to stick with using Office Interop, then I think you can get what you're looking for by setting the Visible property of the Application instance to true after you open the document.
ap.Visible = true;
Use Process.Start instead.
That's all you need here and will produce the behavior you're after.
Related
I need to convert a PowerPoint (ppt/pptx) file to PDF using C#
Currently, I'm using this code:
public void PPTXToPDF(string originalPptPath, string pdfPath) {
// Create COM Objects
Microsoft.Office.Interop.PowerPoint.Application pptApplication = null;
Microsoft.Office.Interop.PowerPoint.Presentation pptPresentation = null;
try {
object unknownType = Type.Missing;
//start power point
pptApplication = new Microsoft.Office.Interop.PowerPoint.Application();
//open powerpoint document
pptPresentation = pptApplication.Presentations.Open(originalPptPath,
Microsoft.Office.Core.MsoTriState.msoTrue, Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse);
// save PowerPoint as PDF
pptPresentation.ExportAsFixedFormat(pdfPath,
Microsoft.Office.Interop.PowerPoint.PpFixedFormatType.ppFixedFormatTypePDF,
Microsoft.Office.Interop.PowerPoint.PpFixedFormatIntent.ppFixedFormatIntentPrint,
MsoTriState.msoFalse, Microsoft.Office.Interop.PowerPoint.PpPrintHandoutOrder.ppPrintHandoutVerticalFirst,
Microsoft.Office.Interop.PowerPoint.PpPrintOutputType.ppPrintOutputSlides, MsoTriState.msoFalse, null,
Microsoft.Office.Interop.PowerPoint.PpPrintRangeType.ppPrintAll, string.Empty, true, true, true,
true, false, unknownType);
} finally {
// Close and release the Document object.
if (pptPresentation != null) {
pptPresentation.Close();
pptPresentation = null;
}
// Quit PowerPoint and release the ApplicationClass object.
if(pptApplication != null) {
pptApplication.Quit();
pptApplication = null;
}
}
}
This snippet uses the Interop libraries, which create an instance of the PowerPoint application and use it programmatically.
The problem is that the application randomly crashes.
Sometimes in the finally block, pptApplication comes null, which means that the application didn't even open, sometimes it says The message filter indicated that the application is busy, sometimes I see a dialog titled "Exporting..." with a progressbar showing the export progress, then the dialog disappears and the program hangs forever until I force-close it.
The application fails randomly with the same file: I run it once, it works, I run it again, it works, I run it again, it doesn't work, etc...
I cannot have an application that sometimes works and sometimes fails, so my question is: Is there a reliable alternative for converting PowerPoint files to PDF which doesn't use Interop? Did Microsoft provide an alternative API to do these things without opening an instance of PowerPoint?
The reason why I'm using Interop at the moment is that I also have to read the PowerPoint file and search for shapes in slides, if that matters.
UPDATE
The PC I'm running my application is a Windows 7 PC with Office installed. I cannot install anything else, that's why I need to find an independent library.
Only other way that comes to my mind is try using libreoffice for the same task. It has headless mode (no UI) and can be called from command line, like this:
"C:\Program Files (x86)\LibreOffice 5\program\soffice.exe" --headless --convert-to pdf:writer_pdf_Export test.pptx
Note that it will exit immeditaly (because it is designed for batched processing), but you can watch output directory using FileSystemWatcher and when desired file was created there (and when you are able to acquire exclusive lock on it) - it is done. You can also do batch processing with it, for more options available look here - https://help.libreoffice.org/Common/Starting_the_Software_With_Parameters. I used it for some conversions and had no problems doing that.
Using interop you can try to generate the pdf using this approach:
I have tested making fifty exports at one time and worked fine.
_oPresentation.SaveAs(outputFullPath,
PowerPoint.PpSaveAsFileType.ppSaveAsPDF,
Microsoft.Office.Core.MsoTriState.msoCTrue);
Note: _oPresentation is just the interop presentation instance (Microsoft.Office.Interop.PowerPoint.Presentation).
I have over a thousand e-mails that need to be converted to individual PDFs. The code I've written is able to process the faux-print job up to the "Save PDF As..." Dialog Box.
I need to manipulate the filename in that dialog to the original filename. I cannot find a way to post a string to the filename section of the dialogbox, since this is an unconventional method posting on a separate process.
How do I send this information to an active dialog window that is unrelated to the process the application is calling?
public static void Main(string[] args)
{
string folderIn = Path.GetDirectoryName(#"Z:\Files up to 3-6-13\");
string folderOut = Path.GetDirectoryName(#"c:\users\athomas\desktop\Output");
string[] fileList = Directory.GetFiles(folderIn);
foreach(var fileName in fileList)
{
Console.WriteLine(fileName);
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo(fileName);
info.Verb = "Print";
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(info);
// This is where I need to say something like
// SaveDialog.SaveAs(fileName.SafeFileName)
if(p.HasExited == false)
{
p.WaitForExit(1000);
}
}
Console.ReadLine();
}
You are asking basically about hacking around, which means there is no safe way to do that. The possible way may look like this:
1) You enumerate windowses shown on the screen, via EnumWindows
2) You use for every window GetWindowText to find a Title of the Dialog you are searching for.
3) Ocassionaly there may be more then one dialog, Murphy's law is always there, so may be even with the same title. You can reduce collision risk by
3.a Call GetWindowLong with HWND of "dialog" you found
3.b Call GetParent to get HWND of parent window, that you already know
If you can avoid or may not care about case of window style&title collision, just jump over point (3)
4) Once you found your dialog, run over its HWND EnumChildWindows to get all controls of it
5) Find the contrlol you interested in via some attribute (usually Class_ID)
6) Execute SetWindowtext to set the text you like.
As I said before, there are several places were this process can fail.
For example:
You may happen to have multiple dialogs on the screen in the same moment
You may download a new version of PDF processor, and in SaveAs dialog may have something changed, so your code will break.
But considering that you are hacking, you may assume your own risks.
An excellent tool, may be the best tool, for window investigation on Windows, is probably Spy ++, which is available within visual studio installation. Using it you can try to find some unique attributes of a textbox of that dialog where you want to put the text in. Having that in your hands you can reliably query dialog's children collection to find the textbox.
After you mentioned .msg, I did a Google Search on the .msg file format and I was surprised to find it was well documented. It is an OLESS file, and there are APIs for loading those. I even found a CodeProject C# sample that was able to load an email I just saved from Outlook 2013!
It will be a bit of work since you will have to write code to "render" message how you see fit. Ex: It gives you the body, the recipients, and the attachments. You would have to decide what to do with the attachments and code that up. Then find a PDF library. But at least it is a programmatic solution, rather than relying on hacking the UI.
Also: A Google search for .MSG to PDF returned loads of tools to do this. One is a forum post referring to an Adobe Product called "Acrobat PDFMaker" so maybe you don't even need to write code.
Sorry to post 2 answers. I have a solution to the exact problem of prompting for the file. These instructions vary slightly based on the version of Windows, but you will get the idea:
Add a new printer that uses PDF printer driver
When it asks for the "port" click "Add Port" then "Local Port"
Enter in a file name. Ex: "C:\Users\Myself\MyPdf.pdf"
Complete the printing wizard as usual.
I tested this with the XPS printer and it worked
When you print to this printer, it writes to that exact file: No prompting! You will have to monitor the file (I suggest FileSystemWatcher) or poll for when the file is closed or something like that. After that, you can move the file elsewhere.
I am using document.Active() method which is a part of Microsoft.office.interop.word namespace. I want to open the file i.e. I want to see the file opened in the Word application. I have set thetrackRevisions property true and rest of all the things.
I just want the file to open NOT IN SAVE-AS MODE. Just open so that when I open up a document from DB or from my PC drives I just want it to open.
Here is the code that I am executing:
Word.Document tempDoc = app.Documents.Open("E:\\xyz.docx");
// Activate the document so it shows up in front
tempDoc.Activate();
tempDoc.TrackRevisions = true;
foreach (Revision rev in tempDoc.Revisions)
{
string editedBy = rev.Author;
//string what = rev.Cells;
}
tempDoc.Close(ref Nothing, ref format, ref Nothing);
Any suggestions that come to your mind?
You just want word to open?
Have you tried
Process.Start('path to word plus filename');
I am using Ms Office Interop assemblies to create a MS Project file. To save the file created, I am using FileSaveAs method and it prompts a message saying that if you want to replace the existing file.
I want to suppress the message, and I didn't find any parameter in FileSaveAs method for this purpose.
Any Idea on this?
I'am using C# as my programming language.
I ran into this issue when working with Excel Interop. The best I've been able to find is to disable all Office alerts, like this:
Microsoft.Office.Interop.MSProject.Application msProjectApp = new Microsoft.Office.Interop.MSProject.Application();
msProjectApp.DisplayAlerts = false;
Never double dot COM objects, as they will not be released and this will leave excel open on your server. Unfortunately I have crashed servers because of this.
private void InitialiseExcel()
{
if (excelApp == null)
excelApp = new Excel.Application();
// Turn off User Prompts
excelApp.DisplayAlerts = false;
// Turn off screen updating so we do not get flicker
var app = excelApp.Application;
app.ScreenUpdating = false;
// Specifies the state of the window;
excelApp.WindowState = Excel.XlWindowState.xlMinimized;
Marshal.ReleaseComObject(app);
}
I have the following code:
p.StartInfo.FileName = "notepad.exe";
p.StartInfo.Arguments = "somefile.txt";
p.Start();
When "somefile.txt" is opened in notepad, its text will be modified. I want to save the new text in a temp file with the C# app automatically.
Is it possible to access this text and save it with C#?
It is sorta possible, you could use P/Invoke to call the SendMessage() method and generate the WM_COMMAND messages for the Edit + Select-All and Edit + Copy menu commands. That puts all text on the clip board, readily accessible to your program. You'll need to use the Spy++ utility to find out what the command identifiers are. Looks like 25 and 769 when I look at it on Win7.
There's a more direct way to discover the commands. In Visual Studio choose File + Open + File and select c:\windows\notepad.exe. Open the Menu node and double-click the "1" resource. That opens the menu editor, select Edit + Select-All and look at the Properties window for the ID.
Of course, this technique is specific to Notepad. The command IDs will be different for another program. As will be the code you need to find the proper window handle. Using Process.MainWindowHandle is treacherous at best, you don't know what specific instance of the process you need to select.
It would be very difficult to look at the text in-memory in the notepad process. It could be done, but it depends on the internal memory layout of notepad and your process may need administrative privileges depending on how the memory pages are protected.
Why not use a FileSystemWatcher to detect when the file has been modified, and then read the file in directly using c#?
http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx
Copy the somefile.txt before you start notepad to a temporary file/folder. Start notepad using this temporary file. And wait for Notepad to exit.
Then you can examine if the file is changed (checksum?).
Probably delete the file after the processing, or your system will be full with temp files.
You can bring notepad window to front and send CTRL+S. You will need to use pinvoke for that.
I really don't understand why you would want to use notepad?
If you need a user to edit a text file, and then your app take some action on the text itself, why not just give the user the ability to edit the text right in your app?
What does notepad give you that the TextBox control doesn't have?
Try something like this
using System.Windows.Forms;
using System.IO;
TextBox myEditor = new TextBox();
myEditor.Multiline = true;
myEditor.ScrollBars = ScrollBars.Vertical;
myEditor.AcceptsReturn = true;
myEditor.AcceptsTab = true;
myEditor.WordWrap = true;
// Get the text from the file.
StreamReader sr = new StreamReader("SomeFile.txt"))
myEditor.Text = sr.ReadToEnd();