One of my customers having a problem with my ActiveX control. The ActiveX is supposed to download an Excel file, save it in a folder in My documents and then Run Excel and open the file.
Here is the code snippet for downloading and opening the file:
private string Checkout(string strFileUrl, string strPhysicalDir, string strPhysicalName)
{
string strMyDocumentFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string strCiroDocFolder = String.Format(#"{0}\CiroDoc", strMyDocumentFolder);
string strCheckoutFolder = String.Format(#"{0}\{1}", strCiroDocFolder, strPhysicalDir);
string strFilePath = String.Format(#"{0}\{1}", strCheckoutFolder, strPhysicalName);
try
{
if (!Directory.Exists(strCiroDocFolder))
Directory.CreateDirectory(strCiroDocFolder);
if (!Directory.Exists(strCheckoutFolder))
Directory.CreateDirectory(strCheckoutFolder);
if (File.Exists(strFilePath))
File.Delete(strFilePath);
WebClient myWebClient = new WebClient();
myWebClient.DownloadFile(strFileUrl, strFilePath);
}
catch (Exception e)
{
return e.Message;
}
return Run(strFilePath);
}
private string Run(string strFilePath)
{
if (!File.Exists(strFilePath))
return "filenotfound";
if (IsExecutable(strFilePath))
return "isexecutable";
try
{
System.Diagnostics.Process.Start(strFilePath);
}
catch (Exception e)
{
//This get returned
return String.Format("{0} ({1})", e.Message, strFilePath);
}
return "success";
}
The Run method returns the exception "an error occurred when the command is sent to the program".
When I check the My documents folder, the file is not there. Since it's not there, I would expect the Run method stop and return "filenotfound".
This confuses me. Why does the File.Exists return true? Is it Windows file virtualization that kicks in and the file is saved in VirtualStore folder? If so, how can stop that behaviour? Or could it be something else in my customer's machine that causes this behaviour?
I haven't been able to reproduce this problem on my own machine yet. If you know how I would be grateful.
My customer's computer setup
OS: Windows 7
Browser: IE 10
Antivirus: McAfee
If there is some relevant information Im missing I'll try to get it.
Having Internet Explorer in Protective mode and UAC enabled causes System.Diagnostics.Process.Start(strFilePath); not to run as expected.
Using this code instead will open the Excel file, even in Protective mode.
Process myProcess = new Process();
myProcess.EnableRaisingEvents = false;
myProcess.StartInfo.FileName = "excel";
myProcess.StartInfo.Arguments = String.Format(#"""{0}""", strFilePath);
myProcess.Start();
Related
So basically my app triggers an excel macro, from a file, that updates the file and then closes it.
When I open the file I set the "DisplayAlerts = false" variable in order to ignore all popups and it works as expected in my computer... however, a colleague of mine tried to use it and for every file, he gets the popup asking if he wants to save all changes...
Checked other questions about the popups in excel but all suggested solutions use "oBook.Saved = true;" or "oBook.Close(false);", but these did not work for me.
my code is as follows:
using Microsoft.Office.Interop.Excel;
public static bool Trigger_Macro_From_File(string path)
{
ApplicationClass oExcel = null;
Workbook oBook = null;
try
{
string filename = Path.GetFileName(path);
string macro_name = "!some_macro";
string macro = #"'" + filename + #"'" + macro_name;
// Create an instance of Microsoft Excel
oExcel = new ApplicationClass
{
DisplayAlerts = false,
Visible = false
};
oBook = oExcel.Workbooks.Open(path);
RunMacro(oExcel, new Object[] { macro });
oBook.Save();
oBook.Saved = true;
return true;
}
catch (Exception ex)
{
return false;
}
finally
{
oBook?.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oBook);
oBook = null;
oExcel?.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcel);
oExcel = null;
GC.Collect();
}
}
Does anyone know anything about this?
Thanks in advance.
You could double-check that no other "Microsoft Excel" process is running in the Task Manager.
Let's say at some point in your development process you started your program and open the workbook with something like
xlWorkbook = xlApp.Workbooks.Open(filePath);
Then you encountered an exception for some reason, and killed the program without closing the file properly (workbook.Close(..), app.Quit(..) and so on).
The Microsoft Excel process is still running in the background, and has a handle on the file you want to edit. So you cannot execute an instruction that saves the file under the same name. This is why the popup is appearing.
This scenario is taken from the point of view of the developer, but the same behavior could have happened on your coworker's computer if your app crashed without quitting properly, and gets re-started.
Also, be careful that finally statement might not always be executed, so double-check which scenario could cause your app to close without releasing the COM object.
I am developing a Windows application where I manipulate Word Application. More specific, I am opening a Word Document but when I quit it and try to open another Word Document this Error comes out.
How to handle
System.Runtime.InteropServices.COMException (0x800706BA): The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) at Microsoft.Office,Word.ApplicationClass.set_Visible(Boolean Prop)**
If I don't quit the Word Application this error does not come out.
Below I show you the functions that I open and quit the Word Application.
//function to open word Document located in a specific path
public static void openWordDocument(string fileName)
{
try
{
wordApplication.Visible = true;
string filePath = myPath + fileName;
WordApi.Document docx = wordApplication.Documents.Open(filePath);
}
catch (Exception ex)
{
MyLogger.Error(ex.ToString());
}
}
//function to quit wordApplication
public static void CloseWordApp() {
try {
Object wordAppObject = Marshal.GetActiveObject("Word.Application");
WordApi.Application wordApp = (WordApi.Application)wordAppObject; //cast Object to its actual type
wordApp.Quit();
}
catch (Exception ex) {
MyLogger.Error(ex.ToString());
}
I finally figured it out what is the problem.
The main problem was that when I quit it and try to open another Word Document,which opening another Word Document means get/create an Object of Word Application. In my case wordApp != null, after finalizing the application, so I had to create another Word Application Object and return it for the case.
//open word Document located in a specific path
public static void openWordDocument(string fileName)
{
try
{
wordApplication = createWordApplicationObject(wordApplication);
wordApplication.Visible = true;
string filePath = myPath + fileName;
WordApi.Document docx = wordApplication.Documents.Open(filePath);
}
catch (Exception ex)
{
MyLogger.Error(ex.ToString());
}
}
private static WordApi.Application createWordApplicationObject(WordApi.Application wordApp)
{
WordApi.Application wordAppFirstTime;
WordApi.Application wordApp1;
if (wordApp == null)
{
wordAppFirstTime = new WordApi.Application();
return wordAppFirstTime;
}
else
{
wordApp1 = new WordApi.Application();
return wordApp1;
}
}
With CloseWordApp() remain the same.
Most probably the exception is fired by the following line of code:
wordApplication.Visible = true;
You need to make sure the COM server is alive. Because after quitting the object becomes unavailable. I'd suggest setting such object references to null, so later we could check whether the application object is still alive. For example:
try
{
if (wordApplication == null)
{
wordApplication = new Word.Application();
}
wordApplication.Visible = true;
string filePath = myPath + fileName;
WordApi.Document docx = wordApplication.Documents.Open(filePath);
}
catch (Exception ex)
{
MyLogger.Error(ex.ToString());
}
I wanted to add a solution that works for me. We had this issue in a .net web service, along with other errors, like "the remote procedure call failed" on Word.Documents.Open(). i'll list all the things we tried, and finish with the solution.
we tried:
Make sure RPC service is up. Word is not corrupted, opens properly,
including the file we were opening.
restart server and service hosting the web application.
Rollback a windows update that occured the same day it stopped working.
Uninstalled the antivirus software.
We isolated the code to a third party app to validate it was the open()
method that caused the problem, and using different files as well. We
created a win form app, and consol app. We ran that small app as win
admin, a regular account as well as the account that runs the web
app.
We ran procMon.
we did a repair on word.
we installed Office all over, we tried 32 and 64bits version
Finale solution:
we deleted the user profile that runs the web app.
4 days to find that out. I'd thought i'd share my paine with the world. lol
while posting these lines, we are not sure why the local profile created this issue.
I have a Xamarin Project where I generate a .pdf file from scratch and save it in my local storage. This works perfectly fine and can find it and open it in the disk where I saved it. However, I need to open the .pdf file immediately after creation programmatically.
I already tried different variations using Process and ProcessStartInfo but these just throw errors like "System.ComponentModel.Win32Exception: 'The system cannot find the file specified'" and "'System.PlatformNotSupportedException'".
This is basically the path I am trying to open using Process.
var p = Process.Start(#"cmd.exe", "/c start " + #"P:\\Receiving inspection\\Inspection Reports\\" + timestamp + ".pdf");
I also tried ProcessStartInfo using some variations but I'm getting the same errors all over and over.
var p = new Process();
p.StartInfo = new ProcessStartInfo(#"'P:\\Receiving inspection\\Inspection Reports\\'" + timestamp + ".pdf");
p.Start();
The better way is that use LaunchFileAsync method to open file with browser. You could create FileLauncher DependencyService to invoke uwp LaunchFileAsync method from xamarin share project.
Interface
public interface IFileLauncher
{
Task<bool> LaunchFileAsync(string uri);
}
Implementation
[assembly: Dependency(typeof(UWPFileLauncher))]
namespace App14.UWP
{
public class UWPFileLauncher : IFileLauncher
{
public async Task<bool> LaunchFileAsync(string uri)
{
var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(uri);
bool success = false;
if (file != null)
{
// Set the option to show the picker
var options = new Windows.System.LauncherOptions();
options.DisplayApplicationPicker = true;
// Launch the retrieved file
success = await Windows.System.Launcher.LaunchFileAsync(file, options);
if (success)
{
// File launched
}
else
{
// File launch failed
}
}
else
{
// Could not
}
return success;
}
}
}
Usage
private async void Button_Clicked(object sender, EventArgs e)
{
await DependencyService.Get<IFileLauncher>().LaunchFileAsync("D:\\Key.pdf");
}
Please note if you want to access D or C disk in uwp, you need add broadFileSystemAccess capability. for more please refer this .
Update
If the UWP files are network based, not local zone based, you could use Xamarin.Essentials to open file with browser. And you must specify the privateNetworkClientServer capability in the manifest. For more please refer this link.
I am attempting to open an Imanage document, in MS Word, within a temporary test application (for debugging) to later copy over into an ActiveX control project. The error that is popping up is:
Exception thrown at 0x7618851A (msvcrt.dll) in w3wp.exe: 0xC0000005: Access >violation reading location 0x09801000.
If there is a handler for this exception, the program may be safely continued.
The error occurs when running the cmd.Execute line and I am unsure as to why I am getting the error.
using IManage;
using IMANEXTLib;
using System;
namespace WebApplication3
{
public partial class WebForm2 : System.Web.UI.Page
{
IManDatabase imanagedatabase;
IManDMS myDMS = new ManDMSClass();
protected void Page_Load(object sender, EventArgs e)
{
openImanageDoc("docNumber", "versionNumber", "server", "database", ReadOnly);
}
public void imanageLogin(string server, string database)
{
try
{
IManSession session = myDMS.Sessions.Add(server);
IManWorkArea oWorkArea = session.WorkArea;
session.TrustedLogin();
foreach (IManDatabase dbase in session.Databases)
{
if (dbase.Name == database)
{
imanagedatabase = dbase;
}
}
}
catch (Exception ex)
{
throw ex;
}
}
public void openImanageDoc(string docNo, string versionNo, string server, string database, bool isReadOnly = true)
{
IManDocument doc;
try
{
imanageLogin(server, database);
int iDocNo = int.Parse(docNo);
int iVersion = int.Parse(versionNo);
doc = imanagedatabase.GetDocument(iDocNo, iVersion);
openNRTDocument(ref doc, isReadOnly);
imanagedatabase.Session.Logout();
myDMS.Close();
}
catch (Exception Ex)
{
imanagedatabase.Session.Logout();
throw Ex;
}
finally
{
imanagedatabase = null;
myDMS = null;
}
}
public void openNRTDocument(ref IManDocument nrtDocument, Boolean isReadonly)
{
OpenCmd cmd = new OpenCmd();
ContextItems objContextItems = new ContextItems();
objContextItems.Add("NRTDMS", myDMS);
objContextItems.Add("SelectedNRTDocuments", new[] { (NRTDocument)nrtDocument.LatestVersion });
objContextItems.Add("IManExt.OpenCmd.Integration", false);
objContextItems.Add("IManExt.OpenCmd.NoCmdUI", true);
cmd.Initialize(objContextItems);
cmd.Update();
cmd.Execute();
}
}
}
Due to the nature of the error, I am presuming it is a configuration issue rather than a code error although I could be completely wrong as I am very new to programming.
I have found out that w3wp.exe is an IIS worker process created by the app pool but other than that I have no idea what the numeric code represents. Any help or advice is greatly appreciated.
The error is being raised by the OpenCmd instance because it is most likely trying to access resources such as local registry settings. It's not possible to do that in a web application, unless you host your code in a proprietary technology like ActiveX (which is specific to Internet Explorer)
Actually, it is not appropriate for you to use OpenCmd here. Those type of commands (iManage "ICommand" implementations) are intended to be used in regular Windows applications that have either the iManage FileSite or DeskSite client installed. These commands are all part of the so-called Extensibility COM libraries (iManExt.dll, iManExt2.dll, etc) and should not be used in web applications, or at least used with caution as they may inappropriately attempt to access the registry, as you've discovered, or perhaps even display input Win32 dialogs.
For a web app you should instead just limit yourself to the low-level iManage COM library (IManage.dll). This is in fact what iManage themselves do with their own WorkSite Web application
Probably what you should do is replace your openNRTDocument method with something like this:
// create a temporary file on your web server..
var filePath = Path.GetTempFileName();
// fetch a copy of the iManage document and save to the temporary file location
doc.GetCopy(filePath, imGetCopyOptions.imNativeFormat);
In an MVC web application you would then just return a FileContentResult, something like this:
// read entire document as a byte array
var docContent = File.ReadAllBytes(filePath);
// delete temporary copy of file
File.Delete(filePath);
// return byte stream to web client
return File(stream, MediaTypeNames.Application.Octet, fileName);
In a Web Forms application you could do something like this:
// set content disposition as appropriate - here example is for Word DOCX files
Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
// write file to HTTP content output stream
Response.WriteFile(filePath);
Response.End();
I try to open temporary HTML file with default browser and delete the file then:
var tempFileName = Path.ChangeExtension(Path.GetTempFileName(), "html");
// I save document to temp file here...
Process process = null;
try
{
process = Process.Start(tempFileName);
}
catch (Win32Exception)
{
}
catch (ObjectDisposedException)
{
}
catch (FileNotFoundException)
{
}
var worker = new BackgroundWorker();
worker.DoWork += (s, we) => {
if (process != null)
{
process.WaitForExit();
try
{
File.Delete(tempFileName);
}
catch (IOException)
{
}
}
};
worker.RunWorkerAsync();
Unfortunately, Process.Start returns null if a process is not started, but a running one is used (new tab is opened in Google Chrome). So I can't wait for that process to exit.
So, a general question is: how to do the task? How to show a temporary HTML file to a user and delete it after viewing?
If you use ProcessStartInfo and set UseShellExecute then you can start the user's default browser by "running" the HTML directly like you're trying to do now. I haven't tried it, but it should give you a Process back to determine when the user has closed the browser.
I would still prepare for a bunch of edge cases you have no control over. Such as if they leave the browser open but close the app that's watching the browser. At that point do you let the browser stay alive? Do you kill it? When do you delete the HTML file? It might be better to use the Web Browser control. Then you don't even have to worry about other processes or browser compatibility. You can even stream the HTML contents to the control and there is no file to delete later.
You can force a new browser instance, by first figuring out the default browser, and executing it manually:
public Process launchBrowser(string url)
{
string browserName = "iexplore.exe";
using (RegistryKey userChoiceKey = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice"))
{
if (userChoiceKey != null)
{
object progIdValue = userChoiceKey.GetValue("Progid");
if (progIdValue != null)
{
if(progIdValue.ToString().ToLower().Contains("chrome"))
browserName = "chrome.exe";
else if(progIdValue.ToString().ToLower().Contains("firefox"))
browserName = "firefox.exe";
else if (progIdValue.ToString().ToLower().Contains("safari"))
browserName = "safari.exe";
else if (progIdValue.ToString().ToLower().Contains("opera"))
browserName = "opera.exe";
}
}
}
return Process.Start(new ProcessStartInfo(browserName, url));
}
Then you can get a handle to the process:
var process = launchBrowser("www.google.com");
process.WaitForExit();
try
{
//Do whatever
}
catch (IOException)
{
}
You can also read the html content into a Memory Stream or into a string variable using WebClient, and after close the Stream or WebClient, file will be released, ready to be deleted, as you no longer need it.
Them you have the html content in memory, just send it to browser.
Here some example if you need:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/060ea8e0-cc63-44a3-b0dc-b531c29b8a0f/read-html-content-using-cnet?forum=csharpgeneral
Hope it helps.