Passing a COM method default parameter - c#

I have a ComVisible COM class written in C#. I want to call it from another C# bit of code using COM and pass the default value for the parameter. I can call plenty of other methods without default arguments.
This is the best I can come up with. The first two lines work for all my other methods.
Type mytype = Type.GetTypeFromProgID("MyType");
dynamic myinstance = Activator.CreateInstance(mytype);
object missingValue = System.Reflection.Missing.Value;
myinstance.generatecsvdocument("mystring", ref missingValue);
My method looks like this:
public void generatecsvdocument(string mystring, string rowseperator = "\n")
When I run it I get the error:
The best overloaded method match for 'generatecsvdocument(string,
string)' has some invalid arguments

object missingValue = System.Reflection.Missing.Value;
That cannot work here. It is only valid for a COM method that takes a VARIANT as an argument. Looks like object or dynamic in C#. A very different kind of default argument mechanism than C# supports, it is the callee that determines the default value. In C# it is the caller that determines it, the C# compiler uses metadata to know that default.
Missing.Value turns in a variant of type vtError with the value DISP_E_PARAMNOTFOUND at runtime. Signalling the COM method to use the default value. Not actually that commonly used, usually only implemented in COM servers that support scripting languages. Office Automation is the most common example, probably what inspired you to try this.
But no, your argument is string, not a variant. There is no way to discover the default either when you use late binding, implicit is that you don't know anything about the default value stored in metadata. Otherwise the reason that the vtError mechanism exists, scripting languages have the same problem. The only real way to get ahead is to rewrite the method and test for a null argument, substituting "\n" if that's the case.

Related

.add(true) what does a "true" parameter/argument mean

I have just started learning C# and use Visual Studio to write programs. After a lot of searching via internet and my C# book...and asking other experienced programmers...nothing to answer the following question: What does the parameter/argument "true" in the add() method mean in the following lines of code:
var excel = new microsoft.office.interop.excel.application();
var workbook = excel.workbooks.add(true**);
the add() argument usually has nothing in the parenthesis or maybe 1 or 2 to indicate the number of workbooks to open...but "true"? Thanks
Here is the documentation for the C# VSTO Workbooks.Add(...): https://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.workbooks.add.aspx and here is the documentation for the VBA version: https://msdn.microsoft.com/en-us/vba/excel-vba/articles/workbooks-add-method-excel
Office's COM automation APIs for VSTO definitely need some work for C# ergonomics. The problem is that the method accepts a COM Variant value (System.Object in .NET) argument value, which means it will accept anything you throw at it and handle it internally without any compile-time type safety. This is a poor API design, instead it should have typed overloads instead ("overloads" are different methods/functions with the same name, but strongly-typed parameters). But there's no point me complaining about it now :D
Further confounding things, the conversion from your Boolean true value to a COM Variant object is done entirely behind-the-scenes. But first, let's consider the documented, valid argument values:
Optional Object. Determines how the new workbook is created. If this argument is a string specifying the name of an existing Microsoft Excel file, the new workbook is created with the specified file as a template. If this argument is a constant, the new workbook contains a single sheet of the specified type. Can be one of the following XlWBATemplate constants: xlWBATChart, xlWBATExcel4IntlMacroSheet, xlWBATExcel4MacroSheet, or xlWBATWorksheet. If this argument is omitted, Microsoft Excel creates a new workbook with a number of blank sheets (the number of sheets is set by the SheetsInNewWorkbook property).
...that's a lot to consider!
We can derive what the overloads would be and then document them separately:
workbook.Add() - As the parameter is optional, it means you can call it without any arguments. The effect is creating a new workbook with a number of blank sheets (where the number comes from SheetsInNewWorkbook).
workbook.Add( String templateFileName ) - If the argument is a string, then it's treated as a filename to a template file to use. Obviously this is not marshalable to a Boolean value so this is not happening in your case.
workbook.Add( Int32 constant ) - The documentation uses the term "constant" which I believe really means an Int32 value. It only accepts the values from XlWBATemplate which are defined here: https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlwbatemplate-enumeration-excel - those values are -4109, 4, 3 and -4167.
The documentation does not say what happens if the argument value is none of those. The fact it seems to work in your case suggests the value passed as a COM Variant containing the bool and it is simply ignored. I don't believe the runtime would marshal the Boolean to an Int32, given Variant supports boolean values, and even if it were marshaled to an Int32 then you'd likely get an argument error as 1 is not defined in XlWBATemplate.
Solution: Change Workbooks.Add( true ) to just Workbooks.Add() and see if there's any change in behaviour. If not, then keep the change; otherwise then you've encountered some undocumented behaviour in the Excel COM API and you should document this in a code-comment (and let us know too!)

c# MongoDB RunCommand

Can someone give me an example of the use of the RunCommand method that takes a string argument only (called CommandName) available in the MongoDB .NET driver? I know there is an overloaded RunCommand method that takes an object reference (I think a CommandDocument object) as an argument, but I'd rather not use that one.
I'm having trouble getting the syntax right for CommandName. Thanks in advance!
If you are using some recent version of the official C# driver, the "real" string based version you are referring to (CommandResult RunCommand(string commandName)) is only part of the legacy driver component (check the namespace). I would hence not recommend using it.
The "official" interface currently looks like this:
TResult RunCommand<TResult>(Command<TResult> command, /* and some additional optional parameters */)
And since the C# driver heavily relies on implicit type conversions, there also is one from a string (and a BsonDocument) to the corresponding sub types of Command<TResult> (JsonCommand<TResult> and BsonDocumentCommand<TResult>). So you can effectively pass a string to the above new RunCommand() method, too.
You can therefore write either one of the following lines both of which do the exact same thing:
RunCommand<BsonDocument>("{count: \"collection_name\"}")
RunCommand<BsonDocument>(new BsonDocument("count", "collection_name"))

Why do optional parameters in C# 4.0 require compile-time constants?

Also is there a way to use run-time values for optional method parameters?
Optional parameters are required to be constants because they are written out as values of an attribute. Hence they inherit all of the restrictions that an attribute value has.
There is no way to directly encode a runtime value. However you can get close with the following pattern
public void MyApi(SomeType type = null) {
type = type ?? new SomeType();
...
}
Optional parameters are compiled into the assembly and as such (just like anything that is designated as const) they must be a compile-time constant.
And no, you cannot use execution-time values as optional parameters.
Optional parameters are determined at compile time, and substituted into the method if you call a method with too few parameters. They are handled via adding an attribute to the parameter in the method's IL.
As such, they need to be fully resolved at compile time (both for creation, since they're an attribute, but also when used). There is no way to use runtime values for optional method parameters.

How to create an instance of value types using reflection

I want to create an instance of value types like System.String, System.Boolean, System.Int32, etc. I get qualified names of types like System.String or MyNamespace.Employee and I have to create an instance and return back. I use Activator.CreateInstance and FormatterServices.GetUninitializedObject to create instances. But it fails in case of value types. I cannot hard code the logic in case of value types. I need a generic way of creating instances of both value types and reference types.
What exactly is it you are trying to do? FormatterServices.GetUninitializedObject is used mainly by serialization code; outside of that you shouldn't really use it. It sounds like you might just need something like TypeConverter, i.e. (for these types)
TypeConverter tc = TypeDescriptor.GetConverter(someType);
object obj = tc.ConvertFromString(s);
What exactly is failing? I tried the following code to see if there is a problem with value types:
var instance = Activator.CreateInstance(typeof(Int32));
It gives me an Int32 instance set to 0.
Where exactly is your code failing? Otherwise I would say the problem lies with the way you are loading the type, not the way you are creating the instance.
For BCL Value Types (and when using Strings to describe types) ensure you are not using C# keywords and ensure the Type is fully qualified with namespace. For example, C# int is successfully created this way with Activator.CreateInstance(..)
object num = Activator.CreateInstance(Type.GetType("System.Int32"));
You will get failed attempts if you try to use language-specific aliases like "int" or short forms like "Int32".
This works for me:
int x = (int)Activator.CreateInstance(typeof (Int32), true);

DefaultMemberAttribute - what does it do?

I've already read the MSDN article about it. It seems internally it is the way c# sets which is the function that is going to work as indexer(am I right?). Now, I've seen the following example:
[DefaultMemberAttribute("Main")]
public class Program {
public static void Main() {
...
}
}
Now, I don't get it what it means.
Thanks all. But I still can't get its usefulness, apart from the indexer thing. When are we going to call InvokeMember?
No, the DefaultMemberAttribute is used by languages such as VB.NET to find out the member that is acted on by default if no member is referenced from an object, i.e. the member invoked by InvokeMember. This is often used in conjunction with indexers, as you noted, but it is not used by C# directly (unless you use InvokeMember explicitly).
However, for the benefit of other .NET languages, C# does emit the DefaultMemberAttribute for the indexer of a class (if it has one), as indicated by MSDN:
The C# compiler emits the
DefaultMemberAttribute on any type
containing an indexer. In C# it is an
error to manually attribute a type
with the DefaultMemberAttribute if the
type also declares an indexer.
I think MSDN confuses things by referring to indexers a lot in the remarks but then giving an example that does not use an indexer. To clarify, the default member can be anything, but C# gives special behavior for indexers by emitting the attribute for you (if an indexer exists) to the exception of all other use cases.
I personally have never used it, but as far as I can tell you are defining the default method to be invoked when calling InvokeMember. So, using the code snippet you provided if I was to say:
Program prog = new Program();
typeof(Program).InvokeMember("", null, null, prog, null);
Because I left the first argument empty of the InvokeMember call it would use the attribute to determine what the default member is of your class, in your case it is Main.
The DefaultMemberAttribute attribute defines the default member to be called on a when InvokeMember is called with an empty string as the first argument.
If you read the MSDN docs for InvokeMember, it explicitly says:
Parameters
name
Type: System.String
The String containing the name of the constructor, method, property, or field member to invoke.
-or-
An empty string ("") to invoke the default member.
The default member will be the one declared by the DefaultMemberAttribute attribute.

Categories