Outlook COM: Invoke OpenSharedItem via late binding - c#

The returned type of namespaceObjType is a System.__ComObject and it's impossible to call InvokeMember("OpenSharedItem", ...) on it.
How do you need to call this method with the late binding technique?
The only difference I see is that the returned object type of the Session property is only an interface instead of a real COM Class.
Code example:
object outlookApp = System.Runtime.InteropServices.Marshal.GetActiveObject("Outlook.Application");
Type outlookAppType = outlookApp.GetType();
object templateObj = null;
System.IO.File.Copy(templateName, temporaryFileName, true);
object namespaceObj = outlookAppType.InvokeMember("Session", System.Reflection.BindingFlags.GetProperty, null, outlookApp, new object[0]);
if(namespaceObj != null)
{
Type namespaceObjType = namespaceObj.GetType();
// Exception on the next line of code
templateObj = namespaceObjType.InvokeMember("OpenSharedItem", System.Reflection.BindingFlags.InvokeMethod, null, outlookApp, new object[] { temporaryFileName });
}
Exception after executing is: Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))

You need to pass a namesapce object instead of the outlookApp:
namespaceObjType.InvokeMember("OpenSharedObject", System.Reflection.BindingFlags.InvokeMethod, null, namespaceObj, new object[] { temporaryFileName });
Anyway, the Namespace class doesn't provide the OpenSharedObject moded.
Are you interested in the OpenSharedItem method?

Related

Can't call COM-object-method with parameter of type Array

I have a COM class with a method with the following signature:
public class MyComClass {
public void SomeMethod(Array myarray) { }
}
Now in an other project I call this registered COM class using the following code block:
Type t = Type.GetTypeFromProgID("ProgIdOf.MyComClass");
object o = Activator.CreateInstance(t);
object[] contentOfMyArray = new object[] { "Hello World" };
t.InvokeMember("SomeMethod", BindingFlags.InvokeMethod, null, o, new object[] { contentOfMyArray });
I'm trying to pass the contentOfMyArray as the first parameter to the SomeMethod-method. But when I try that I get this exception:
System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.'
ArgumentException: Value does not fall within the expected range.
The method itself does not throw this exception, I checked that.
When I change the signature to public void SomeMethod(object[] myarray) { } or use no array at all and pass the values it works perfectly. When I create an object of the type Array like this:
Array contentOfMyArray = Array.CreateInstance(typeof(object), 1);
contentOfMyArray.SetValue("Hello World", 0);
and pass it as a parameter it still throws this exception.
I read a lot of stuff about marshalling but I couldn't get it to work.
Btw. I cant use a "normal" array like object[], I have to use the Array type.
I hope you can help, I'm not very experienced with COM at all. Thank you!

Invoking com instance methods using Reflection in .NET

I'm trying to use Type.InvokeMember to dynamically call a method in a com object during runtime.
The method I'm trying to execute is DTE2.ExecuteCommand (http://msdn.microsoft.com/en-us/library/vstudio/envdte._dte.executecommand.aspx).
Here is the current code I'm using and doesn't seem to work at all.
var DTE2 dte = ....;
var T = typeof(DTE2);
T.InvokeMember("ExecuteCommand",
BindingFlags.InvokeMethod,
null,
dte,
new object[] { "File.New", "" });
I get the following error
Method 'EnvDTE80.DTE2.ExecuteCommand' not found.
On further inspections I did see ExecuteCommand method info when using the following code.
var methods = T.GetMethods();
So I believe I need to change my BindingFlags.InvokeMethod to something else. What would that be?

Beginner's reflection issues

I'm trying to learn reflection in C# and need some help with my code. I've had trouble finding good code examples/guides, so I apologize if my code is poorly done.
Essentially I'm just trying to check out a given assembly dll for a particular method name (path and method name have been redacted).
The problem occurs on the line object lateBoundObj = asm.CreateInstance(typeName); and it reads An object reference is required for the non-static field, method, or property...
I understand this has to do with static vs non-static and creating a new Assembly or something along those lines, but need some help understanding the issue and how to fix it.
Thank you!
public const string assemblyPath = #"<my file path>";
Assembly asm;
static void Main(string[] args)
{
//asm = new Assembly();
Console.Read();
MethodInfo mi;
object result = null;
object[] arguments = new object[] { "ABC123" };
try
{
Assembly assemblyInstance = Assembly.LoadFrom(assemblyPath);
Type[] types = assemblyInstance.GetTypes();
foreach (Type t in types)
{
mi = t.GetMethod("<my method name>");
if (mi != null)
{
string typeName = t.FullName;
object lateBoundObj = asm.CreateInstance(typeName);
result = t.InvokeMember("GetWeb", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance, null, lateBoundObj, arguments);
break;
}
}
//set return for find method
}
catch (Exception ex) { }
}
The problem is that you're never assigning a value to asm, so it's got the default value of null. Perhaps you meant to use assemblyInstance instead?
In fact, I wouldn't use Assembly.CreateInstance or Type.FullName at all there - I'd use:
object lateBoundObj = Activator.CreateInstance(t);
Also note, you should always avoid code like this:
catch (Exception ex) { }
Always at least log the exception. Ideally, don't catch an exception you can't really "handle" at all.
asm variable is never assigned. You should call CreateInstance on assemblyInstance instead.

Trying to use Reflection to Invoke Method within same class

I have a WCF service that accepts an object as a parameter that has a URI and Method Name.
What I am trying to do is have a method that will look # the URI, if it contains the words "localhost" it will use reflection and call a method, of the name that is passed in as a parameter, within the the same class, return a value and continue on.
public class Test
{
public GetStatResponse GetStat(GetStatRequest request)
{
GetStatResponse returnValue = new GetStatResponse();
if(Helpers.Contains(request.ServiceURI,"localhost", StringComparison.OrdinalIgnoreCase))
{
MethodInfo mi = this.GetType().GetMethod(request.ServiceMethod /*, BindingFlags.Public | BindingFlags.IgnoreCase*/);
returnValue = (GetStatResponse)mi.Invoke(this,null);
}
The above is the code segment pertaining to this question. I pull the MethodInfo no problem but I am running into issues on the mi.Invoke. The exception that I receive is "Exception has been thrown by the target of an invocation." With an Inner Exception "Object reference not set to an instance of an object". I have tried changing the code to (GetStatResponse)mi.Invoke(new Test(), null), with no luck. Test being the class.
I'm open to other suggestions as to how to resolve this, I just thought reflection might be the easiest.
The Method that I am calling with my testing is defined as
public GetStatResponse TestMethod()
{
GetStatResponse returnValue = new GetStatResponse();
Stat stat = new Stat();
Stat.Label = "This is my label";
Stat.ToolTip = "This is my tooltip";
Stat.Value = "this is my value";
returnValue.Stat = stat;
return returnValue;
}
Because you are not specifying BindingFlags in your GetMethod() call, you are only going to be returned methods matching the name containing request.ServiceMethod that are PUBLIC.
Check whether the method you are trying to invoke is public, otherwise MethodInfo will return null.
If it is not public, either make the method public or include the BindingFlags.NonPublic flag.
Also, you should always make sure that mi != null before calling mi.Invoke
Before calling the method you might want to make sure that the MethodInfo you are pulling through reflection is not null:
MethodInfo mi = this.GetType().GetMethod(
request.ServiceMethod,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase
);
// Make sure that the method exists before trying to call it
if (mi != null)
{
returnValue = (GetStatResponse)mi.Invoke(this, null);
}
After your update it seems that the exception is thrown inside the method you are calling:
GetStatResponse returnValue = new GetStatResponse();
// Don't forget to initialize returnValue.Stat before using it:
returnValue.Stat = new WhateverTheTypeIs();
returnValue.Stat.Label = "This is my label";

Object of type 'customObject' cannot be converted to type 'customObject'

I receive the following error when I invoke a custom object
"Object of type 'customObject' cannot be converted to type 'customObject'."
Following is the scenario when I am get this error:
I invoke a method in a dll dynamically.
Load an assembly
CreateInstance....
When calling MethodInfo.Invoke() passing int, string as a parameter for my method works fine => No exceptions are thrown.
But if I try and pass one of my own custom class objects as a parameter, then I get an ArgumentException exception, and it is not either an ArgumentOutOfRangeException or ArgumentNullException.
"Object of type 'customObject' cannot be converted to type 'customObject'."
I am doing this in a web application.
The class file containing the method is in a different project. Also the custom object is a separate class in the same file.
There is no such thing called a static assembly in my code. I am trying to invoke a webmethod dynamically. this webmethod is having the customObject type as an input parameter. So when i invoke the webmethod i am dynamically creating the proxy assembly and all. From the same assembly i am trying to create an instance of the cusotm object assinging the values to its properties and then passing this object as a parameter and invoking the method. everything is dynamic and nothing is created static.. :(
add reference is not used.
Following is a sample code i tried to create it
public static object CallWebService(string webServiceAsmxUrl, string serviceName, string methodName, object[] args)
{
System.Net.WebClient client = new System.Net.WebClient();
//-Connect To the web service
using (System.IO.Stream stream = client.OpenRead(webServiceAsmxUrl + "?wsdl"))
{
//--Now read the WSDL file describing a service.
ServiceDescription description = ServiceDescription.Read(stream);
///// LOAD THE DOM /////////
//--Initialize a service description importer.
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12"; // Use SOAP 1.2.
importer.AddServiceDescription(description, null, null);
//--Generate a proxy client. importer.Style = ServiceDescriptionImportStyle.Client;
//--Generate properties to represent primitive values.
importer.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;
//--Initialize a Code-DOM tree into which we will import the service.
CodeNamespace nmspace = new CodeNamespace();
CodeCompileUnit unit1 = new CodeCompileUnit();
unit1.Namespaces.Add(nmspace);
//--Import the service into the Code-DOM tree. This creates proxy code
//--that uses the service.
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit1);
if (warning == 0) //--If zero then we are good to go
{
//--Generate the proxy code
CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
//--Compile the assembly proxy with the appropriate references
string[] assemblyReferences = new string[5] { "System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll" };
CompilerParameters parms = new CompilerParameters(assemblyReferences);
CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
//-Check For Errors
if (results.Errors.Count > 0)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError oops in results.Errors)
{
sb.AppendLine("========Compiler error============");
sb.AppendLine(oops.ErrorText);
}
throw new System.ApplicationException("Compile Error Occured calling webservice. " + sb.ToString());
}
//--Finally, Invoke the web service method
Type foundType = null;
Type[] types = results.CompiledAssembly.GetTypes();
foreach (Type type in types)
{
if (type.BaseType == typeof(System.Web.Services.Protocols.SoapHttpClientProtocol))
{
Console.WriteLine(type.ToString());
foundType = type;
}
}
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
MethodInfo mi = wsvcClass.GetType().GetMethod(methodName);
return mi.Invoke(wsvcClass, args);
}
else
{
return null;
}
}
}
I can't find anything static in what I do.
Any help is greatly appreciated.
Regards,
Phani Kumar PV
Have you looked at what the proxy class looks like that gets generated? You don't need the proxy to call a web service. Just create a class that inherits from SoapHttpClientProtocol and call Invoke(methodName, params).
You are making this SO much more complicated than you need to. Honestly.
EDIT
If you create a class like this:
public class SoapClient : SoapHttpClientProtocol
{
public SoapClient()
{
}
public object[] Invoke(string method, object[] args)
{
return base.Invoke(method, args);
}
}
and call it like this:
SoapClient soapClient = new SoapClient();
soapClient.Url = webServiceAsmxUrl;
soapClient.Invoke(methodName, args);
I think you will see that it has the exact same results as what you are doing.
Let me try to explain the most probable reason for the problem to come in my approach.
When I invoked a method in the assembly called as "methodname" in the webservice I am trying to pass the parameters required for that as args[] to the function "CallWebService"
This args[] when passed will be successfully working when I try to pass a normal parameters like primitive types including string.
But this is what I did when I tried to pass a custom object as a parameter.
Three things that are done in this.
create an object of that type outside the CallWebService function (using reflection). when I did that way what happens is an instance of the customobject created with a temporary dll name internally.
once I set the set the properties of the object and send it across to the CallWebService function as an object in the args array.
I tired to create an instance of the webservice by creating the dynamic dll.
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
When I finally tried to invoke the method with the instance of the dynamic assembly created
I tried to pass the customobject that is created at step 1,2 via args property.
at the time of invocation the CLR tries to see if the customobject that is passed as input and the method that is being invoked are from the same DLL.
which is obviously not from the way the implementation is done.
So following is the approach that should be used to overcome the problem
I need to create the custom object assembly with the same assembly that I used to the create the webservice instance..
I implemented this approach completely and it worked out fine
MethodInfo m = type.GetMethod(methodName);
ParameterInfo[] pm = m.GetParameters();
object ob;
object[] y = new object[1];
foreach (ParameterInfo paraminfo in pm)
{
ob = this.webServiceAssembly.CreateInstance(paraminfo.ParameterType.Name);
//Some Junk Logic to get the set the values to the properties of the custom Object
foreach (PropertyInfo propera in ob.GetType().GetProperties())
{
if (propera.Name == "AppGroupid")
{
propera.SetValue(ob, "SQL2005Tools", null);
}
if (propera.Name == "Appid")
{
propera.SetValue(ob, "%", null);
}
}
y[0] = ob;
}
this can occur when the version of a dll you have referenced in your reflected code is different from the version of that dll in your compiled code.
This is an old thread, but I just had a similar problem. I looked on here, this one popped up, but I saw no useful solutions.
The OP's error was this: Object of type 'customObject' cannot be converted to type 'customObject'.
My very similar error was this: Object of type 'System.String' cannot be converted to type 'System.Windows.Forms.AccessibleRole'.
Here is how I solved my problem:
I performed a Find and Replace (use CRTL+SHIFT+F to bring the dialog box up) search in the Current Project for the term AccessibleRole.
Within one of the Form's Designer's was a place where I was assigning an AccessibleRole value to a String variable using ToString().
I fixed this, and my problem went away.
I hope this provides help to others.

Categories