Passing a string[] as an object reference to a COM object - c#

I have a webservice that wraps a COM object's functions for the purpose of reducing RPC communication errors. The parameter that I pass to a particular web service is a string[]. A problem seems to lie in the COM object function, which requires an object reference.
Some errors will show on the webpage because they come from the COMClass (error 87), others are compiler errors. I've tried to indicate which is which below.
[WebMethod(EnableSession=true)]
public int WSFunc(string[] StringArray){
//object StringArr = (object)((object[])StringArr); //returns error 87 in webpage log
//object StringArr = (object)StringArr; //returns error 87 in webpage log
//public override int COMClass.Func(ref object pStringList)
return COMClass.Func(ref StringArr);
//return COMClass.Func(ref StringArray); //Doesn't build, cannot convert from 'ref string[]' to 'ref object' (CS1503)
//return COMClass.Func(ref (object)StringArr); //Doesn't build, A ref or out argument must be an assignable variable (CS1510)
}
Also, this produces the same result (error 87 - parameter incorrect):
[WebMethod(EnableSession=true)]
public string WSFunc(string[] StringArray){
object[] StringObj = new object[StringArray.Length];
for(int i = 0; i < StringArray.Length; i++){
StringObj[i] = StringArray[i];
}
object MyString = (object)StringObj;
return ComClass.Func(ref MyString);
}
Has anyone else run into this issue?

I think this article should help you setup your marshalling for the com interop.
http://msdn.microsoft.com/en-us/library/aa645736(v=vs.71).aspx

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!

Get custom resource and custom key converting string to "ResourceManager"

I'm trying get a resource for translation in ASP.NET, using conversion of string to type ResourceManager and a extertnal Resources file for translation. But when i execute i'm look this error:
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 24: Type myProp = element.GetProperty("ResourceManager").GetType();
Line 25: MethodInfo getString = myProp.GetMethod("GetString");
Line 26: var r = getString.Invoke(this, new object[] { key });
Line 27: return r.ToString();
Line 28: }
This is my code:
protected string getText(string key)
{
Type element = Type.GetType($"Website.Language.{translation}.general");
Type myProp = element.GetProperty("ResourceManager").GetType();
MethodInfo getString = myProp.GetMethod("GetString");
var r = getString.Invoke(this, new object[] { key });
return r.ToString();
}
Apparently, the getString variable returns null, but te method "GetString" should be call "GetString" method of ResourceManager.
Someone could help me with this?
The first parameter of MethodInfo.Invoke must be an instance of some object (as far as I know, it needs to be of the same type that you reflect on, in your case Website.Language.{translation}.general).
This might not work, but just to show you in which direction you need to go.
protected string getText(string key)
{
Type element = Type.GetType($"Website.Language.{translation}.general");
var instance = Activator.CreateInstance(element);
Type myProp = element.GetProperty("ResourceManager").GetType();
MethodInfo getString = myProp.GetMethod("GetString");
var r = getString.Invoke(instance, new object[] { key });
return r.ToString();
}
See also this example.

Untrappable COMException with GetRefTypeInfo

I have a function whose role is to return the name of a COM type; it works fine on x64, but crashes in the middle of iterating the COM types in the Excel type library (and MS-Project too, ..and probably other ones I'm not aware of), and I can't seem to be able to do anything to catch the exception it's throwing.
System.Reflection.TargetInvocationException was unhandled
Message: An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an invocation.
This results in an AccessViolationException, which results in sudden death for the EXCEL.EXE host process.
I was able to add some debug output and identify exactly which member was blowing up:
Querying AsTypeName for member SheetPivotTableBeforeDiscardChanges
Member EXCEL.EXE;Excel.IAppEvents.SheetPivotTableBeforeDiscardChanges
(Object) added successfully
Querying AsTypeName for parameter Sh
Parameter EXCEL.EXE;Excel.IAppEvents.Sh (Object) added successfully
Querying AsTypeName for parameter TargetPivotTable
When the Excel type library starts loading, the entire VBA type library has successfully loaded. If I run my process in a MS-Word host, everything runs fine, so it looks like there's something about the type of the TargetPivotTable parameter of IAppEvents.SheetPivotTableBeforeDiscardChanges that doesn't like what I've written to query it.
case VarEnum.VT_USERDEFINED:
int href;
unchecked
{
if (Marshal.SizeOf(typeof (IntPtr)) == sizeof (long))
{
href = (int) desc.lpValue.ToInt64();
}
else
{
href = desc.lpValue.ToInt32();
}
}
ITypeInfo refTypeInfo;
info.GetRefTypeInfo(href, out refTypeInfo); // <~ boom
return GetTypeName(refTypeInfo);
Here's the relevant part of the stack trace a contributor was able to get from a MS-Project host:
System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component.
at System.Runtime.InteropServices.ComTypes.ITypeInfo.GetRefTypeInfo(Int32 hRef, ITypeInfo& ppTI)
at Rubberduck.Parsing.Symbols.ReferencedDeclarationsCollector.GetTypeName(TYPEDESC desc, ITypeInfo info) in c:\Users\Andrew\Documents\GitHub\Rubberduck\Rubberduck.Parsing\Symbols\ReferencedDeclarationsCollector.cs:line 95
at Rubberduck.Parsing.Symbols.ReferencedDeclarationsCollector.GetTypeName(TYPEDESC desc, ITypeInfo info) in c:\Users\Andrew\Documents\GitHub\Rubberduck\Rubberduck.Parsing\Symbols\ReferencedDeclarationsCollector.cs:line 89
at Rubberduck.Parsing.Symbols.ReferencedDeclarationsCollector.CreateFieldDeclaration(ITypeInfo info, Int32 fieldIndex, DeclarationType typeDeclarationType, QualifiedModuleName typeQualifiedModuleName, Declaration moduleDeclaration)
The code gets a VT_PTR, so it gets a TYPEDESC out of that pointer (line 89), and then recurses; the same code then gets a VT_USERDEFINED, so it gets an ITypeInfo out of the lpValue of the TYPEDESC (line 95), and normally I get that ITypeInfo and fetch the type's name. For some reason in a 32-bit context GetRefTypeInfo refuses to cooperate and sends everything into flames.
For full context, here's the entire relevant code:
private string GetTypeName(TYPEDESC desc, ITypeInfo info)
{
var vt = (VarEnum)desc.vt;
TYPEDESC tdesc;
switch (vt)
{
case VarEnum.VT_PTR:
tdesc = (TYPEDESC)Marshal.PtrToStructure(desc.lpValue, typeof(TYPEDESC));
return GetTypeName(tdesc, info);
case VarEnum.VT_USERDEFINED:
int href;
unchecked
{
if (Marshal.SizeOf(typeof (IntPtr)) == sizeof (long))
{
href = (int) desc.lpValue.ToInt64();
}
else
{
href = desc.lpValue.ToInt32();
}
}
ITypeInfo refTypeInfo;
info.GetRefTypeInfo(href, out refTypeInfo);
return GetTypeName(refTypeInfo);
case VarEnum.VT_CARRAY:
tdesc = (TYPEDESC)Marshal.PtrToStructure(desc.lpValue, typeof(TYPEDESC));
return GetTypeName(tdesc, info) + "()";
default:
string result;
if (TypeNames.TryGetValue(vt, out result))
{
return result;
}
break;
}
return "Object";
}
private string GetTypeName(ITypeInfo info)
{
string typeName;
string docString; // todo: put the docString to good use?
int helpContext;
string helpFile;
info.GetDocumentation(-1, out typeName, out docString, out helpContext, out helpFile);
return typeName;
}
Is there anything in that switch block that looks like it's waiting to blow up in a 32-bit context?

Null return value throwing Exception in COM client written in C++

I have made a COM wrapper over C# dll and I use this COM component in C++ to invoke the methods in C# dll.
All is working fine except when my C# method returns null through COM to C++.
an excpetion is thrown in C++ that says
"Debug Assertion failed!" ..... atlsafe.h Line 235
Expression psaSrc != null.
How do I avoid this error and accept a null value in the return type.
for eg.
CComSafeArray itemEntities = objController1->ListItems(sPath);
When ListItems method returns a null, system should not throw an error.
Instead itemEntities should be st to NULL.
Please someone suggest a solution.
Thanks,
Gagan
It must be something in the part of your code which you did not show.
This works for me:
C# class:
[ComVisible(true)]
[Guid("BE55747F-FEA9-4C1F-A103-32A00B162DF0")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Test
{
//[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
public string[] GetStringArray()
{
var a = new string[3];
a[0] = "string0";
a[1] = null;
a[2] = "string2";
return a;
}
public string[] GetStringArray2()
{
return null;
}
}
Calling GetStringArray and GetStringArray2 from C++:
SAFEARRAY* pSA = NULL;
testObject->GetStringArray(&pSA);
printf("str0: %ls\n", ((BSTR*)(pSA->pvData))[0]);
printf("ptr1: %x\n", ((BSTR*)(pSA->pvData))[1]);
printf("str2: %ls\n", ((BSTR*)(pSA->pvData))[2]);
SAFEARRAY* pSA2 = NULL;
testObject->GetStringArray2(&pSA2);
printf("pSA2: %x\n", pSA2);
Run:
str0: string0
ptr1: 0
str2: string2
pSA2: 0
I did not have to specify how to marshal the array (the commented out line), because it gets marshaled as SAFEARRAY(VT_BSTR) by default.
EDITED: I guess I see where the problem is. You're using ATL CComSafeArray which doesn't expect a NULL SAFEARRAY by design:
CComSafeArray(_In_ const SAFEARRAY *psaSrc) : m_psa(NULL)
{
ATLASSERT(psaSrc != NULL);
HRESULT hRes = CopyFrom(psaSrc);
if (FAILED(hRes))
AtlThrow(hRes);
}
You should change your code like this:
CComSafeArray<BSTR> itemEntities;
SAFEARRAY* pItemEntities = objController1->ListItems(sPath);
if (NULL != pItemEntities)
itemEntities.Attach(pItemEntities);
Or, assign m_psa directly:
CComSafeArray<BSTR> itemEntities
itemEntities.m_psa = objController1->ListItems(sPath);
if (!itemEntities)
{
// NULL returned
}
The return value in COM is reserved to pass exceptions to different modules. To get a real return value you could add a out parameter to your function and use this as a return value. I thought the C# COM Interop would already do this for you but it seems that you need to do this manually.

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