Exception when calling RTD server from Excel - c#

I wrote a VSTO Excel addin using Visual Studio 2010 and after having managed to work around most of the obstacles Microsoft throws into the path of the righteous developer, I finally have to admit defeat.
My project contains a Ribbon with some controls, a custom task pane that allows users to search a database via a RESTful interface and a RTD server that lets them put this data in their worksheets. So far, so ... well, painful, I guess: After a lot of struggle with Interop, ComVisibility and AppDomains (what a great idea!), my current status is as follows.
In the worksheets, I call a wrapper function for RTD like this (snipped):
Public Function call(value as String)
Dim addin as Office.ComAddIn
Set addin = Application.ComAddIns("MyAddin")
addin.Object.RTD(value)
End Function
This is (part of) the addin class:
namespace Some
{
[Guid("...")]
[ComVisibleAttribute(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class MyAddin {
[snip]
public String RTD(String value)
{
String returner = null;
try
{
returner = Globals.ThisAddin.Application.WorksheetFunction.RTD(SERVERID, "", value);
}
catch(COMException ce)
{
returner = ce.StackTrace;
}
return returner;
}
}
}
And the relevant part of the RTD Server class:
namespace Some
{
[Guid("...")]
[ComVisibleAttribute(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("MyRTDServer")]
public class Server : Excel.IRtdServer
{
[snip]
}
}
In Debug mode I:
Create a, empty new workbook
Add an "=RTD(...)" formula to a cell
Add a wrapper function call "=call(...)" to a cell
Save the workbook
Open the workbook
Stop Debugging and start it again
Open the workbook
I observe:
At 3, everything works fine
At 5, everything works fine
At 7, when recalculating my cells, I get a Unable to get the RTD property of the WorksheetFunction class exception in the cell of 3) and #N/A in the cell of 2). However, I can see that the topics are registered in the RTD server and as soon as the data is available, the exception is replaced with the correct data. Also, if I do NOT recalculate the cells, they display the saved value and then correctly update to the retrieved value once the data is available.
If in deployed mode, I observe:
At 2, I get #N/A
At 3, I get a Unable to get the RTD property of the WorksheetFunction class exception
Any help, please? :(
EDIT:
Testing the same procedure with a very basic RTD server in a blank Addin project shows exactly the same results: a loaded excel file displays #N/A if an RTD-formula is recalculated before the server has the data available.
I would like to inquire: WTF?
Cheers,
Che

The 'Unable to get the RTD property....' error is just the Excel object model's way of saying there was an error in the function call.
Does your RTD server have any dependencies on the rest of your add-in? The fact that 2) fails when deployed, makes it look like the RTD server needs some other bits to be initialized before it runs happily. The way the VSTO add-in is put together, your assembly is likely to be loaded totally separately for the RTD server and for the COM add-in, and in your problematic cases the RTD instance is loaded first.
Perhaps you can test with a sample RTD server instead of your own, just to confirm that the error is there and not in the wrapper or elsewhere in the VSTO add-in. Then you can dig into your RTD server to see what goes wrong.
-Govert
Excel-DNA - Free and easy .NET for Excel

Related

Outlook Ribbon Implementation for IRibbonUI

I am confused about the implementation of the IRibbonUI interface that is exposed in the Outlook Object Model.
When the XML file for the either the Explorer or Inspector ribbon are written, we have to define which method will be responsible for loading the XML into Outlook.
Note: that this code was not originally written by me but I've been tasked to fix the solution..or at the least find a reason why this thing doesn't work as expected.
Anyways, the methods to set the ribbons are defined in the following methods: Ribbon_OnLoad and ERibbon_OnLoad
Public Sub Ribbon_OnLoad(ByVal Ribbon As Office.IRibbonUI)
m_Ribbon = Ribbon
End Sub
Public Sub ERibbon_OnLoad(ByVal Ribbon As Office.IRibbonUI)
m_ERibbon = Ribbon
End Sub
Now what I am confused is why the method for the Inspector never fires? When I step through the code and even when I open an inspector object, my global variable m_Ribbon is sometimes Nothing and vice versa for the m_ERibbon variable.
There have been times where m_ERibbon has been nothing and there has been times m_Ribbon has been nothing..I'm starting to think I may have deeply misunderstood how the Ribbon for Office works.
Now it becomes more intricate when I have to invoke the callbacks via the Invalidate method. I have an explorer wrapper and a inspector wrapper which invoke either m_ERibbon.Invalidate() or m_Ribbon.Invalidate() depending if the user is using the Explorer or Inspector.
What I do not understand is this:
Why doesn't Ribbon_OnLoad fire ALL the time, and why doesn't ERibbon_OnLoad fire all the time, despite the fact I've explictly set the onLoad method to fire these methods?
Why are there instances where m_ERibbon or m_Ribbon are Nothing?
From what I can tell is despite the Invalidate() method being invoked from the Explorer or the Inspector...the call to Invalidate() invokes ALL of the callbacks despite some callbacks being placed in the inspector and vice versa.
Have I understood this incorrectly?
EDIT I'm adding code for the GetCustomUI because this is where the markup is defined. Despite the respective XML markups being available for both the Explorer and the Inspector; the method Ribbon_OnLoad (for the Inspector) doesn't fire.
Function GetCustomUI(ByVal RibbonID As String) As String Implements Microsoft.Office.Core.IRibbonExtensibility.GetCustomUI
Select Case RibbonID
Case "Microsoft.Outlook.Mail.Read"
Return basRibbon.QFGetRibbonMailRead()
Case "Microsoft.Outlook.Explorer"
Return basRibbon.GetRibbonExplorerFolder()
Case Else
Return String.Empty
End Select
End Function
And a snippet of the XML markup is the following (note that this is for the Inspector):
sRibbonXML = "<customUI xmlns=""http://schemas.microsoft.com/office/2006/01/customui"" onLoad=""Ribbon_OnLoad"" >" & _
Snippet of the XML markup for the Explorer:
sRibbonXML = "<customUI xmlns=""http://schemas.microsoft.com/office/2009/07/customui"" onLoad=""ERibbon_OnLoad"" " & _
Microsoft Office applications call the GetCustomUI method to obtain an XML string that defines the user interface of your custom Ribbon.
public class Connect : Object, Extensibility.IDTExtensibility2, IRibbonExtensibility
...
public string GetCustomUI(string RibbonID)
{
StreamReader customUIReader = new System.IO.StreamReader("C:\\RibbonXSampleCS\\customUI.xml");
string customUIData = customUIReader.ReadToEnd();
return customUIData;
}
Note, sometimes you need to return the XML markup for different ribbonID values passed as an argument. In that case you will get the onLoad callback invoked (for inspectors too).
public string GetCustomUI(string ribbonID)
{
string ribbonXML = String.Empty;
if (ribbonID == "Microsoft.Outlook.Mail.Compose")
{
ribbonXML = GetResourceText("Trin_RibbonOutlookBasic.Ribbon1.xml");
}
return ribbonXML;
}
See Customizing a Ribbon for Outlook for more information.
You can read more about the Fluent UI (aka Ribbon UI) in the following series of articles in MSDN:
Customizing the 2007 Office Fluent Ribbon for Developers (Part 1 of 3)
Customizing the 2007 Office Fluent Ribbon for Developers (Part 2 of 3)
Customizing the 2007 Office Fluent Ribbon for Developers (Part 3 of 3)
Please remember that by default, if an VSTO add-in attempts to manipulate the Microsoft Office user interface (UI) and fails, no error message is displayed. However, you can configure Microsoft Office applications to display messages for errors that relate to the UI. You can use these messages to help determine why a custom Ribbon does not appear, or why a Ribbon appears but no controls appear. See How to: Show Add-in User Interface Errors for more information.

Call a SAP transaction/program with the SAP 3.0 .NET Connector

I am aware of the option to call RFC-functions with .NCo 3.0 but is it possible to call transactions/programs directly with the SAP Connector? (Like using the fields defined in SAP as parameters and fill them, or use a variation, something like this?).
This answer provides a workaround that I am aware of, and sure - I could call a VBScript from my C# code but that is not what I want to do.
I also checked all of the 64 Questions tagged with sap-connector but there was nowhere a direct answer if it is possible or not.
Also the SAP documentations I got from the SAP marketplace aren't mentioning transactions/programs at all. Does this mean it is not wanted/possible ?
If so, why is it possible to do it with macros/pre-recorded VBScripts but not with the .NET-Connector ? Or am I just doing something wrong ?
When I try to call a program/transaction with the standart-code:
SAPHandle.ECCDestinationConfig cfg = new SAPHandle.ECCDestinationConfig();
RfcDestinationManager.RegisterDestinationConfiguration(cfg);
RfcDestination dest = RfcDestinationManager.GetDestination("QP2");
dest.Ping(); //works fine -> Connection is OK
RfcRepository repo = dest.Repository;
IRfcFunction zzmkalzzm23fnc = repo.CreateFunction("ZMZKALZZM23");
it gives me the following (expectable) error:
metadata for function ZMZKALZZM23 not available: FU_NOT_FOUND:
function module ZMZKALZZM23 is not available
CreateFunction, as the name already suggests, creates a proxy to call a remote-enabled function module in the SAP system. You can't call a transaction or program this way. I am not aware of any way to call a report with SAP .Net Connector. The solution you linked uses SAP Gui, which provides the SAP system with a UI to display graphical elements. AFAIK, SAP NCo doesn't provide such an interface and you can't call reports from NCo.
However, there are products that allow you to execute transactions and catch their output. We are using the product Theobald Xtract to extract SAP ERP data for BI purposes, but they also have a more generic .Net library (Theobald ERPConnect) available that may be able to provide this functionality. It won't be as simple as calling a function and extracting the strongly typed data, but with some filtering you should be able to get the output you need. Those products are not cheap, but they do provide a nice set of functionality you otherwise would have to reinvent yourself.
Some example code how you could call the transaction you ended up calling through VBS-Scripts.
From the Theobald ERPConnect Knowledgbase:
private void button1_Click(object sender, System.EventArgs e)
{
// Reset the batch steps
transaction1.BatchSteps.Clear();
// fill new steps
transaction1.ExecutionMode = ERPConnect.Utils.TransactionDialogMode.ShowOnlyErrors;
transaction1.TCode = "MMBE";
transaction1.AddStepSetNewDynpro("RMMMBEST","1000");
transaction1.AddStepSetOKCode("ONLI");
transaction1.AddStepSetCursor("MS_WERKS-LOW");
transaction1.AddStepSetField("MS_MATNR-LOW",textBox1.Text);
transaction1.AddStepSetField("MS_WERKS-LOW",textBox2.Text);
// connect to SAP
r3Connection1.UseGui = true;
R3Connection r3Connection1= new R3Connection("SAPServer",00,"SAPUser","Password","EN","800");
r3Connection1.Open(false);
// Run
transaction1.Execut e();
}

Excel sheet as frontend for C# backend server

I have a C# feeder that generates some data and sends them to some sort of frontend. Is there a direct way in which I can receive these data using excel microsoft office ?
P.S. I do not want to write a code that recieves the data and then writes it into excel sheet. I want to be able to directly get the data from excel itself, as a frontend for my C# backend. Is that ever possible ?!
I did a quick dr. Google search for WebService calls from Excel and found the following article
Option Explicit
' Excel VBA Function wrapper to call currency conversion Web Service on the web!
Public Function ccyConvert(rsCurrIn As String, rsCurrOut As String,
ByVal vfAmtIn As Single) As Single
Dim objSClient As MSSOAPLib30.SoapClient30
' Remove the 30 if using an earlier version of SOAP
Dim fResult As Single
' Point the SOAP API to the web service that we want to call...
Set objSClient = New SoapClient30
Call objSClient.mssoapinit(
par_WSDLFile:="http://webcontinuum.net/webservices/ccydemo.wsdl")
' Call the web service
fResult = objSClient.calcExcRate(rsCurrIn, rsCurrOut, vfAmtIn)
Set objSClient = Nothing
ccyConvert = fResult
End Function
As Excel has the possibility to access data (from different dataSources) it may be a proper solution to create a c# WebService which delivers the requested data.
Pls note that this is not my code - it's just a copy from the mentioned article - so credit goes to the author!
I just thought about another solution. You may consider making your own C# com component. Within any excel VBA code you are now able to call any method within your assembly. Once again mr. google found an article describing how to achieve this!
c# code for your own com component
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace DotNetLibrary
{
[ClassInterface(ClassInterfaceType.AutoDual)]
public class DotNetClass
{
public DotNetClass()
{
}
public string DotNetMethod(string input)
{
return "Hello " + input;
}
}
}
VBA code within an excel macro
Private Sub TestDotNetCall()
Dim testClass As New DotNetClass
' or do whatever you want with return value
MsgBox testClass.DotNetMethod(“World”)
End Sub

How do I create a new worksheet and populate it with rows of data using Excel-DNA?

I have c# code behind my Excel-dna addin which is successfully downloading data from a service. I have created a ribbon in Excel-dna with a button which triggers the download, and now I want to display the data in a new worksheet. How do I create a worksheet and add rows?
I tried calling xlcWorkbookInsert from my c# code using:
ExcelReference newSheet = (ExcelReference)XlCall.Excel(XlCall.xlcWorkbookInsert, 1);
but I always get a ExcelDna.Integration.XlCallException exception. Is this the correct approach, or is there a simpler way to go about doing this?
I also tried pasting an object[,] of data to an existing sheet:
ExcelReference sheet1 = (ExcelReference)XlCall.Excel(XlCall.xlSheetId, "Sheet1");
ExcelReference myTargetPasteArea = new ExcelReference(1, 1, 2, 10, sheet1.SheetId);
myTargetPasteArea.SetValue(result);
There are no errors this time, but nothing happens (although I can see the code being executed when I step through in debug).
Your code is calling to Excel via the C API (that's how the XlCall.Excel(...) and ExcelReference stuff in Excel-DNA works). But you can't call the C API directly from your ribbon event handler. You have two options:
Make a detour via a macro. This is easy if you change your ribbon xml code:
<button onAction="RunTagMacro" tag="MyMacro" />
and then define a macro:
public static void MyMacro()
{
// ... do your work here ....
}
You can also call the macro yourself from the event handler - the RunTagMacro internally just calls
object app = ExcelDnaUtil.Application;
app.GetType().InvokeMember("Run", BindingFlags.InvokeMethod,
null, app, new object[] { control.Tag }, new CultureInfo(1033));
Another option is to change your interaction code to use the COM API. For this you'll need to use the 'dynamic' support in .NET 4, or reference an interop assembly - either the version-specific Primary Interop Assemblies for Office, or some version-independent interop assemblies like NetOffice.
XlCall.Excel(XlCall.xlcWorkbookInsert, 1);
returns a bool: true - success, false - failure
So casting it to ExcelReference is the cause of the exception.
You may need an xlcNew before that xlcWorkbookInsert. Take a look in the Excel-Dna source at the GetApplication method in Excel.cs.

How do i load an automation addin (dll) programmatically from within a vsto addin

VSTO
VS2008 SP1
.NET 3.5
Excel 2007
I am a .net noob. I am trying to load an automation addin that is an excel application/automation addin (it is a dll not xla or xll) from within a vsto addin in the ThisAddIn_Startup() method of the vsto addin. From google I got the below solution which is not working.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application excel = Globals.ThisAddIn.Application;
//Also tried without display alerts being set to false
excel.DisplayAlerts = false;
foreach (AddIn addin in excel.AddIns)
{
if (addin.progID.Equals("MY_ADDIN_PROG_ID"))
{
Debug.WriteLine("Addin installed is " + addin.Installed);
addin.Installed = false;
Debug.WriteLine("Addin is: " + addin.FullName + ", " + addin.progID);
Debug.WriteLine("Addin installed is " + addin.Installed);
}
}
AddIn addIn = excel.AddIns.Add("MY_ADDIN_PROG_ID", false);
addIn.Installed = true;
excel.DisplayAlerts = true;
Debug.WriteLine("Addin is: " + addIn.FullName + ", " + addIn.progID);
Debug.WriteLine("Addin installed is " + addIn.Installed);
excel.DisplayAlerts = false;
//OTHER STARTUP CODE
Debug.WriteLine("Starting up addin!");
}
Note, I can see the addin.installed is being set to false and back to true on startup but when I try to populate worksheet with udfs from the addin I tried to load in a later button_click method, I get #NAME? error. I am at my wits end. Any help will be greatly appreciated.
If I first try to call the udf in excel by typing it in a cell by hand before I call my button click method, the worksheet population works and the udfs get evaluted as expected but this is not ideal.
Also setting installed property to true does not seem to be doing anything as i can still see the udf addin as inactive in excel, it is only if I type it into a cell that it gets activated. Is there anything else I need to do to activate the automation addin in my vsto startup?
Thanks!
I'm not sure you want to do this in the startup event. I have done something similar but not quite the same before which may be applicable. I exposed some COM visible functions to VBA in a different event handler:
protected override object RequestComAddInAutomationService()
{
// return something com-visible
}
So maybe you can try to load your automation dll this way? This happens before the startup event fires... Excel might be doing something like locking its list of addins while a startup event is being handled - who knows? If it were possible to know Excel programming would be less tedious.
It is harder than it seems to combine VSTO and Automation in Excel. You may find my blog post helpful:
Communicating Between VSTO and UDF's in Excel
Just need to add String Value to the following registry key and you are good.
For Office 2007
Find regkey, HKEY_CURRENT_USER\SOftware\Microsoft\Office\12.0\Excel\Options, then create string value, where name = OPEN, value = /A "YOUR ADDIN NAME HERE" (quotes need to be included as well.)
Note that for the first addin, value name should be called OPEN, for the second one and onwards, use OPEN1, OPEN2, ... etc.
For Office 2010
Just replace 12.0 with 14.0 in the above regkey path, the rest are all the same.
Check out below article on MSDN, which will also help you a lot.
http://support.microsoft.com/kb/291392
Looks like this is a bug specific to VSTO. I converted my addin to a COM addin and was able to use the automation addin from code after that. My team has sent the issue to microsoft so we'll see what they say.

Categories

Resources