Activate an Excel ComAddin from C# code - c#

In fact, I have a C# application that is runned.
When it's launched, it runs an excel instance and save it to a variable :
excelApp = new Excel.Application();
Then I cycle through each of the excel's addins to find my own created added :
private void getAddin()
{
const string addinName = "myAddInName";
foreach (Office.COMAddIn addin in excelApp.COMAddIns)
if (addin.Description.ToUpper().Contains(addinName.ToUpper()))
{
myAddin = addin;
return;
}
}
The problem is that, it may happens that my plugin is in the deactivated elements list. (Due to some crash)
Then I must go to (on excel's frame) :
File >Options >AddIns >Manage >Deactivated elements >Achieve >"myAddin" >Activate
to get my addin working again (after excel's restart)...
I tried using an addin's property to reactivate it but may app crashes again in that case.
myAddin.Connect = loadAddin;
Someone has an idea on how to resolve it / auto-reactivate a disabled plugin using C# ?

This should help
http://msdn.microsoft.com/en-us/library/aa662931(v=office.11).aspx
I believe you need to do
if(!myAddin.Connect)
{
myAddin.Connect =true;
}

Related

Outlook Add-In disabled because too slow

I am developing a simple Add-In for Outlook with C#. Now as I was testing the release, suddenly Outlook print an error message and disables my Add-In:
This add-in caused Outlook to start slowly. (1.594 seconds)
I am not sure what causes this. All I do Onload are these this:
Ribbon Button
I am using a single ribbon button which I initialize in my AddIn as follows:
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
return new MyRibbonButton();
}
Ribbon Button constructor
The ribbon button initialize an object. So nothing special yet.
public MyRibbonButton()
{
this.guiSettings = new AppSettingsManager(root.localmachine, "GUI", false);
}
...
public AppSettingsManager(root type, string subpath, bool writable)
{
if (subpath != "")
{
this.PATH += #"\" + subpath;
}
this.type = type;
this.writable = writable;
}
Icon
In the same MyRibbonButton class I declare the icon I want to use for my button depending on a value in the registry.
public Bitmap imageSuper_GetNotifyImage(IRibbonControl control)
{
switch (guiSettings.GetValueInt32("Icon", 1))
{
case 1:
return Properties.Resources.icon1;
case 2:
return Properties.Resources.icon2;
case 3:
return Properties.Resources.icon3;
default:
return Properties.Resources.icon1;
}
}
As you can see I don't do anyting special except maybe the Registry Read in the last part. Do you think this causes Outlook to start slowly? If so, how can I optimize it.
You get penalized for loading the .Net system. There used to be a "warmup" registry key which could be used to force Outlook to load the right version of the .Net run-time without being penalized, but that key no longer works.
The only way I was able to work around that problem is to create a stub addin in Delphi (C++ would work just as well if not better) that did nothing but get loaded by Outlook (in about 20ms) and start a timer. When the timer fires (Outlook would be idle and not monitoring the addin), it would load the slave .Net addin using the IManagedAddin interface.

Accessing Internet Explorer with Shdocvw results in UnauthorizedAccessException

I'm working on a portion of an application that has a 'finder' tool that allows the user to drag and drop a finder onto an application or internet explorer web browser so that our program can locate it and do what it needs to do. This portion of the application was not under any active development for the last few years (last time I believe IE 7 was the latest browser), but it has worked for IE 9 and below. Starting at IE 10 we get problems, and I specifically am using IE 11.
The following code locates the different Internet explorer windows (including tabs) and file explorer windows. We made a quick VB script for extra testing -
dim objShell
dim objShellWindows
set objShell = CreateObject("shell.application")
set objShellWindows = objShell.Windows
if (not objShellWindows is nothing) then
WScript.Echo objShellWindows.Count
for each win in objShellWindows
wscript.Echo TypeName(win)
WScript.Echo win.LocationUrl
WScript.Echo win.LocationName
WScript.Echo win.HWND
next
end if
set objShellWindows = nothing
set objShell = nothing
The above code will execute without error, and it gives the URL and title bar name of all the tabs we have open.
Below is our C# code. It attempts to get the main IE window (not the tab). this.Handle is the handle of this main IE window that the finder tool gets when the user drops it. We're attempting to iterate through the open windows and find the Internet Explorer window that the user selected. This particular snippet is changed slightly from our original implementation, but the end result is the same. As soon as it hits test = window.Item(i) it throws a UnauthorizedAccessException.
ShellWindows windows = null;
IWebBrowser2 shellWindow = null;
IHTMLDocument2 actualDoc = null;
Type shellApplicationType = Type.GetTypeFromProgID("Shell.Application");
Shell32.Shell shellInterface = (Shell32.Shell)Activator.CreateInstance(shellApplicationType);
windows = (ShellWindows)shellInterface.Windows();
bool found = false;
for (int i = 0; i < windows.Count; i++)
{
try
{
object test;
try
{
test = windows.Item(i); //Exception here
shellWindow = (IWebBrowser2)test;
}
catch
{
}
if (shellWindow != null && (IntPtr)shellWindow.HWND == this.Handle)
{
//the rest of the code gets the correct tab from the main IE window
This is the original unedited code from when we first revisited this portion of the program.
ShellWindows windows = null;
IWebBrowser2 shellWindow =null;
IHTMLDocument2 actualDoc = null;
windows = new SHDocVw.ShellWindowsClass();
bool found = false;
for (int i = 0; i < windows.Count; i++)
{
try{
shellWindow = windows.Item(i) as SHDocVw.IWebBrowser2; //Exception here
if (shellWindow != null && (IntPtr)shellWindow.HWND == this.Handle)
{
I would also like to note that instead of a for loop I have tried a foreach loop that followed this syntax
foreach(IWebBrowser2 browser in windows)
and
foreach(InternetExplorer browser in windows)
In those instances, the loop skips over the IE window.
I have looked at IE's security settings. I have disabled Enhanced Protection Mode and allowed cross domain. There does not seem to be a lot of information on this issue, and every approach we try seems to always end up with an UnauthorizedAccessException.
Edit: In response to Hans answer, I do not have any anti malware running on this machine. It is a virtual machine with windows 7 that has the latest SP and updates (no microsoft security essentials). I tried running the app on a 32 bit machine with similar settings and it also failed. After testing it on IE 11, I uninstalled IE 11, rebooted, tried IE 10 (it failed, same error), uninstalled IE 10, rebooted, tried IE 9, and it worked without making any other changes to the system.
Here is the SSCCE as requested by Hans. This code actually works on my target machine. I will return to this post in the next day or two while I handle other tasks that require my attention.
I build this as a 32 bit console app, and ran it on a 64bit machine.
using System;
using SHDocVw;
using mshtml;
namespace GetBrowserSSCCE
{
class Program
{
static void Main(string[] args)
{
ShellWindows windows = null;
IWebBrowser2 shellWindow = null;
windows = new SHDocVw.ShellWindowsClass();
for (int i = 0; i < windows.Count; i++)
{
try
{
shellWindow = windows.Item(i) as SHDocVw.IWebBrowser2;
Console.WriteLine(string.Format("Window Found. HWND: {0}\nName: {1}", shellWindow.HWND, shellWindow.Name));
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("UnauthorizedAccessException caught. Exception Text: " +ex.Message);
}
}
Console.ReadLine();
}
}
}
That this fails in your C# program but not when you run it from the VBScript interpreter points strongly to an environmental problem. Both use the exact same shell interface. The specific exception (underlying error code is 0x8007005) has been reported before on the interwebs but never diagnosed.
First thing you should focus on whenever you have an environmental problem, particularly the kind associated with access rights, is the anti-malware installed on the machine. Disable it and try again.
Second one you should focus on is a quirk associated with ShellWindows, it doesn't just enumerate Internet Explorer windows but also Windows Explorer windows. You've been looking at having sufficient access to IE but that isn't enough, this code can also fail if you happen to have an Explorer window opened and there's an access problem with the explorer.exe process. Do note that your Activator.CreateInstance() method call is not equivalent to the VBScript code, Activator.GetObject() is. So your changes would actually make the problem worse if this is underlying problem.
Third detail that's worth checking is the bitness of your process. By default, your VBScript code will run in the 64-bit script interpreter on a machine that boots the 64-bit version of Windows. But the default setting for a C# project is to run in 32-bit mode. Right-click your EXE project, Properties, Build tab and tinker with the Platform target setting, the 'Prefer 32-bit' checkbox if you see it. This is not an explanation for the error code, it can however affect the effectiveness of anti-malware to intrude.

Create an Excel Add In Application in C#

I'd like to create an Excel Add In. But I want it to have a special behavior.
Then problem is that the Excel Add In I developed stays within Excel. Even when I run a normal Excel instance from Windows...
So what I want, to be more precise, is to have an Excel add in that will only appear in the Excel's ribbon when launched by my own C#_made application.
How should I do that ?
The behavior would be :
Run my C# application
My C# application calls a new Excel's instance
My C# application adds a new tab (and its elements) to the ribbon of this Excel's instance
My C# application adds actions binded to the tab's elements
Excel's instance is being closed > Remove all added components, functions, methods, ...
Close my C# appliclation
Here's a nice tutorial for you: http://www.add-in-express.com/free-addins/net-excel-addin.php
Edit:
Have you considered just disabling the addin, then reenabling it whenever you launch the app with a server that runs in the background and when excel is closed, disables the addin?
Here's some unload code I found here:
private void UnloadBadAddins(bool unloadAddin)
{
const string badAddin = "iManage Excel2000 integration (Ver 1.3)";
foreach(Office.COMAddIn addin in this.ExcelApp.COMAddIns)
{
if (addin.Description.ToUpper().Contains(badAddin.ToUpper()))
{
if (addin.Connect == unloadAddin)
{
addin.Connect = !unloadAddin;
return;
}
}
}
}
I have found the following two properties on the Microsoft.Office.Interop.Excel.Application class:
var excel = new Application();
excel.AddIns
excel.AddIns2
Maybe these can help you programmatically add/remove addins during your application run?

Opening Access report via .net working, but not working

I am trying to open an access report through .net. I can open it in normal mode using Access.AcView.acViewNormal, but it's not what I want to do because that prompts the user to save the first and then it can be viewed. I want the report to pop up and it seems .acViewPreview or .acViewReport is what i should use, but it's not popping up.
It does open up a process and seems to be opening, but i can't see the report. Perhaps it's closing really fast. The following is the code I have currently
private void buttonResults_Click(object sender, EventArgs e)
{
Microsoft.Office.Interop.Access.Application oAccess = new Microsoft.Office.Interop.Access.Application();
oAccess.OpenCurrentDatabase("D:\\path.mdb",true);
try
{
oAccess.DoCmd.OpenReport("rptChartData", Microsoft.Office.Interop.Access.AcView.acViewPreview);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
What am i doing wrong?? Thanks in advance...
I don't know Dot Net. I put this code in an Excel module and it created an Access instance and displayed my report. The key seemed to be oAccess.Visible = True Without setting Visible = True, Access opened (and remained open) with the report but was just not visible.
Dim strDbFullPath As String
Dim oAccess As Access.Application
strDbFullPath = "C:\Access\webforums\whiteboard2003.mdb"
Set oAccess = New Access.Application
oAccess.Visible = True
oAccess.OpenCurrentDatabase strDbFullPath, True
oAccess.DoCmd.OpenReport "rptFoo", acViewPreview
So I think this may not be a Dot Net issue, but standard behavior for an Access application instance.

Is it possible to open a PDF inside a c# application with Acrobat.dll?

I know that I can display a PDF file in my c# executable (not web app) with:
private AxAcroPDFLib.AxAcroPDF axAcroPDF1;
axAcroPDF1.LoadFile(#"somefile.pdf");
axAcroPDF1.Show();
But that is the regular pdf viewer like in the browser. I don't want that. I want full Adobe Standard or Professional functionality in my C# application using the Adobe controls. For example, if I use the code above, it loads in the C# app and I can see the adobe toolbar (print, save, etc.) But it is useless to me because I need things like save which cannot be done with the activex viewer above. Specifically, you cannot save, just as you cannot within the broswer.
So, I referenced the acrobat.dll and am trying to use:
Acrobat.AcroAVDocClass _acroDoc = new Acrobat.AcroAVDocClass();
Acrobat.AcroApp _myAdobe = new Acrobat.AcroApp();
Acrobat.AcroPDDoc _pdDoc = null;
_acroDoc.Open(myPath, "test");
pdDoc = (Acrobat.AcroPDDoc)(_acroDoc.GetPDDoc());
_acroDoc.SetViewMode(2);
_myAdobe.Show();
It opens adobe acrobat but it opens it outside of my c# application. I need it to open in my c# application like the activex library does. Can it be done with these libraries?
If I cannot open it in my c# application I would like to be able to "hold" my c# app tied to it so the c# app knows when I close the adobe app. At least that way I'd have some measure of control. This means I would hit open, the adobe app opens. I close the adobe app, my C# app is aware of this and loads the newly changed doc with the activex library (because I don't need change ability anymore, just displaying.)
I have the full versions of adobe acrobat installed on my computer. It is not the reader.
Thank you for any help.
edit:
There is an example in vb in the adobe acrobat sdk. I believe it is called activeview.
you can check out ABCpdf. I dont know if it has this capability but we have used it for several of our apps
Using a webbrowser control would be an option to display the content.
IText# may help you out.
You can create PDF's and I believe you can use it to read and modify them.
As for displaying in the app..... I am not sure how to display them with iText or if it is possible (have not tried this yet), sorry. iText does let you convert to RTF which may be one approach.
Best option is to write a listener which tells your calling code when Adobe.exe is no longer running. Something like the following (with tweaks for your uses) should work:
public void Open(string myPath)
{
Acrobat.AcroAVDocClass _acroDoc = new Acrobat.AcroAVDocClass();
Acrobat.AcroApp _myAdobe = new Acrobat.AcroApp();
Acrobat.AcroPDDoc _pdDoc = null;
_acroDoc.Open(myPath, "test");
_pdDoc = (Acrobat.AcroPDDoc) (_acroDoc.GetPDDoc());
_acroDoc.SetViewMode(2);
_myAdobe.Show();
NotifyAdobeClosed += new EventHandler(Monitor_NotifyAdobeClosed);
MonitorAdobe();
}
private void Monitor_NotifyAdobeClosed(object sender, EventArgs e)
{
NotifyAdobeClosed -= Monitor_NotifyAdobeClosed;
//Do whatever it is you want to do when adobe is closed.
}
private void MonitorAdobe()
{
while(true)
{
var adcount = (from p in Process.GetProcesses()
where p.ProcessName.ToLower() == "acrobat"
select p).Count();
if (adcount == 0)
{
OnNotifyAdobeClosed();
break;
}
}
}
public event EventHandler NotifyAdobeClosed;
public void OnNotifyAdobeClosed()
{
if (NotifyAdobeClosed != null)
NotifyAdobeClosed(this, null);
}

Categories