I have a C# dll exposed to vb6 via com-interop. This is all working, but I am noticing something strange when I pass an array of a custom objects from .Net into VB6.
Accessing the array from VB6 is what baffles me. If I access the array directly I have to do it like this:
Dim manager as New ObjectManager
'Access with two sets of parentheses:
msgbox manager.ReturnArrayOfObjects()(0).Name
However, if I copy the array first I can access it how I would normally expect to:
Dim manager as New ObjectManager
Dim objectArray() As CustomObject
'copy the array
objectArray = manager.ReturnArrayOfObjects
'access normally:
msgbox objectArray(0).Name
In the first case I had to use two sets of parentheses: manager.ReturnArrayOfObjects()(0).Name In the second case I could just use one set of parentheses: objectArray(0).Name
Does anyone know why this is the case? Am I doing something wrong here with the interop maybe?
Here is a quick stub/sample of the C# interop code.
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("[Guid here...]")]
public interface IObjectManager
{
[DispId(1)]
CustomObject[] ReturnArrayOfObjects();
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("[guid here...]")]
public class ObjectManager: IObjectManager
{
public CustomObject[] ReturnArrayOfObjects()
{
return new CustomObject[] { new CustomObject(), new CustomObject() };
}
}
The class CustomObject() is also exposed to com-interop and working just fine. Please let me know if you need me to post anymore code, but I think these little snippets represent the problem well enough to begin with.
Thanks in advance for your help.
ReturnArrayOfObjects() in the C# code is a method. Your VB6 code is invoking the method, which returns the array, and then accessing the first element. The difference between this
msgbox manager.ReturnArrayOfObjects()(0).Name
and this
objectArray = manager.ReturnArrayOfObjects
msgbox objectArray(0).Name
Is that in the second, you invoke the method by itself without accessing the first element, and VB is allowing you to leave off the parentheses from the method call. Conversely, the language is not allowing you to leave off the parentheses when you directly access the first element. It's simply a language feature, it's not a "double parentheses array syntax" issue.
ReturnArrayOfObjects is a method, that must be called. In VB6, if you're calling a method and supplying no parameters, and it's the entire statement, then you can omit the parenthesis.
However, in your first example, you're calling the method, and then indexing into the array returned by that method. You need the first set of parenthesis to indicate that you're passing no parameters to the method, and then the second set of parenthesis are being used for array indexing.
Related
I can't find an answer to my problem. In dotNet/C#, is it possible to check if a variable was declared to some type and if not, declare it?
Thanks
[Edit] In this case, C# is used as a preexecute language in Open Text CMS. C# code can be used in any module. Using a non-declared variable throws hard to debug errors, as does double-declaring a variable. That's why I'd like to check.
[Edit2] Yes it is most probably compiled somewhere, but the errors are thrown (or rather not thrown) on runtime
[Edit3] Further explanation:
In Open Text, every page can hold several modules, several instances of a module and the same instance of a module several times. In each module, you can use C# as a "pre-execute" language. This is mostly really easy scripting to maneuver around the failings of OpenText. You introduce small variables, set them to true or false, and three lines later write a condition based on the variable. We could (and do) declare a bunch of variables in an initialization block of the page, but since there are so many, it would help to be able to check if a variable was declared and if not, declare it.
I like the idea of changing this to a key/value dictionary but this is a really large site with loads of pages/modules and instances and I'm looking for a working solution without changing the whole thing.
The actual code is really simple most oft he time:
var hasHeadline = false; // this will throw an error if hasHeadline was declared before
hasHeadline = true; // if some CMS condition is met. this will throw an error if hasHeadline wasn't declared
if(hasHeadline) { ** CMS code ** }
As I said, this will show up in multiple instances over which I don't have full control. The resulting "error" will be that the whole code block is stripped from the page.
Declare a single variable that is dynamic, e.g. an ExpandoObject.
dynamic Globals = new ExpandoObject();
Use this variable to store all of your global state.
Globals.hasHeadline = false; //No declaration needed, so
Globals.hasHeadline = true; //no chance of a duplicate declaration
There's no need to. C# is a statically typed programming language ("type" refers to more than just class, struct, and interface: "static typing" means the "types" (shapes) of data, objects and values in your program are known
statically - i.e. at compile-time). If something isn't declared in scope then your code simply won't compile.
This also applies to locals (local variables, method parameters, etc).
This won't compile:
class Foo
{
void Foo( String x )
{
if( z > 0 ) { // `z` isn't declared as a field, parameter or local.
// ...
}
}
}
Similarly, this won't compile:
class Foo
{
public string x;
}
class Bar
{
void Baz( Foo foo )
{
if( foo.z > 0 ) { // `z` is not declared in `Foo`
}
}
}
That said, there are some things you do need to check-before-using in C#, such as:
Nullable references or nullable values.
Entries in a Dictionary or other keyed collection.
Type-checking when you want a known subclass or interface (As C# still does not natively support algebraic types, grrrr)
...but none of those involve checking for declarations.
I have a huge code base and I recently made a change where I changed the type of a parameter from String to a custom class. On the next compile I got all the areas where the impact was, but areas where the input type was of type Object failed. for e.g.
String str = "32"
int i = Convert.ToInt32(str)
Now I have changed String to a new custom type lets say MyCustomClass I would now want following code to fail on next compile
MyCustomClass str = new MyCustomClass("32")
int i = Convert.ToInt32(str)
but it won't as Convert.ToInt32 also accepts type Object. Is there some way I can make a change in MyCustomClass that it's not considered Object anymore.
Please note: Convert.ToInt32 is only used for sample I have many more such functions, so please focus your suggestion/answer to question asked.
Override ToString() and IConvertible
You said in the comments that your intentions are to find places where your object, which had previously been treated as a string, and are now being treated as an object.
In these situations typically, the third-party code would call .ToString() on your object to get something which it can use.
So, Convert.ToInt32(str) is equivalent to Convert.ToInt32(str.ToString()).
If you implement ToString() and IConvertible to return whatever your old version of str looked like then it should continue to work in the same way as the old version.
Probably.
Sorry I know that is not the 100% perfect compile time answer you were looking for, but I think you also know very well that your MyCustomClass will always be considered object.
Possible compile time answer:
Write a tool which uses reflection to iterate over every class/struct/interface in every system/third-party DLL.
Output a load of CS files which contain all these same classes, but just throw NotImplementedException.
(T4 could help you do this)
Compile these classes into dummy.dll
Your .csproj now references only this one dummy.dll, instead of the real dlls.
Your project should compile fine against the dummy dll.
Look at your dummy.cs files and delete any use of object.
Re-compile... and suddenly you get a load of compile time errors showing you anywhere you are using an object.
Impliment an implicit cast from MyCustomClass to String.
public static implicit operator string(MyCustomClass str)
{
return "Legacy respresentation of str";
}
This allows the complier the choice of choosing ToInt32(Object) or ToInt32(String), and I bet it favours the later.
This way all your existing function calls will remain the same so you wont have to be concerned about third party implentation details.
(Sorry, I am not at a computer right now so I can`t test that my assumtion is correct. If you do test this, be sure to consider extension methods, as they can affect the conpilers desision making in unexpected ways)
I have a class and a method within that class. However this class method returns a string. When I call the class method I don't get an error even though I'm not catching the string value return. Is there a way that I can make C# and .net force me to capture the value when returning a value.
Here is an example of what I mean.
1- create a class test.
class test
{
public string mystring() {
return "BLAH";
}
}
2- call the class method from program or another class
test mystring = new test();
mystring.mystring();
My compiler while working in Visual Studio does not complain that I'm not capturing the return value.
Is this normal or I'm missing something. Can I force the compiler to notify me that the function returns a value but I'm not catching it?
Thanks in advance for any suggestions you may have.
You could convert this to a property instead of a method:
public string myString
{
get
{
return "Blah";
}
}
Then you can't compile if you simply call the property:
myString.myString; //Results in "Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement" Error
In a word, no. Not by force as such.
It's commonplace to not capture return values. Examples in the core libs abound (adding elements to a Hashset<T> for example, the function actually returns a bool to flag whether it was actually added or if it already existed - depending on individual implementation I may or may not care about that).
Of course, you can always just do something like string str = MyFunction() each time then never use str but I guess you probably already knew that.
You can try turning on warnings as errors by right-clicking the project in solution explorer, clicking Properties, going to the Build tab and setting Treat warnings as errors to all. This will force you to resolve all warnings before you can build and will capture some of the you-didn't-assign-this scenarios.
The compiler can't know that the only purpose of your method is to return the string or if it does some work that affects state, and so it can't complain when you don't assign the result to anything.
You can, however, set it up as a get only property per MikeH's answer. This will complain when you don't assign it to anything.
If your function has side effects then your should create unused variable and catch value. Compiler on release options delete this variable.
But if you don't have side effects in function: you may use visual studio tools such as "Watch window" and "Immediate window"
For some future person who comes across this issue, you can use an 'out'. This will force you to always assign the variable.
public void mystring(out returnString) {
returnString = "BLAH";
}
...
string OutString;
test mystring = new test();
mystring.mystring(out OutString);
I'm familiar with the C# syntax to, for example, create a list of strings by List<string> MyList.
Is there a similar syntax in python that will cause it to fail if I attempt to add a type that is not in the collection type?
As others have said, Python doesn't have anything like that built in.
If you wanted to make something like that, you would subclass the built-in list() class, and make all the methods that allow inserting or appending elements do a type check. Or, instead of the type check, you could make your custom methods try to coerce the type.
Here is an example implementation of a list that will only hold int() values. It will coerce anything you try to insert or append into it, and raise an appropriate error. I subclassed the built-in list() type, so the code I wrote is just the exceptions; anything you don't see here will work exactly as the base list() type. I just needed to hook a few methods. I also gave it a custom .__init__() method that accepts a list or other sequence and initializes the object.
This is an incomplete implementation; for example, it doesn't override the .insert() method. But I think it shows you the basic idea, and if you really wanted an IntList() class you could use this as a base.
class IntList(list):
def __init__(self, seq=None):
if seq is not None:
try:
for x in seq:
self.append(int(x))
except ValueError:
raise ValueError, "can only use int values to init IntList"
def __setitem__(self, i, x):
try:
list.__setitem__(self, i, int(x))
except ValueError:
raise ValueError, "can only set int values into IntList"
def append(self, x):
try:
list.append(self, int(x))
except ValueError:
raise ValueError, "can only append int values to IntList"
lst = [0, 1, 't']
try:
o = IntList(lst) # raises exception
except ValueError:
print("lst has a 't' in it")
o = IntList(range(3)) # works
try:
o[1] = 't'
except ValueError:
print("cannot set to 't'")
o[1] = 2 # works
o.append(4) # works
o.append('t') # raises error
The above prints:
lst has a 't' in it
cannot set to 't'
Traceback (most recent call last):
File "t.py", line 38, in <module>
o.append('t') # raises error
File "t.py", line 18, in append
raise ValueError, "can only append int values to IntList"
ValueError: can only append int values to IntList
There is a common Python idiom of having a default argument set to None for list arguments in .__init__() functions. This is because the default value list is only evaluated once, at compile time, and saving a reference to this list would mean that every instance of the class shared the same instance. In this case, since we have a for loop iterating over the loop, building our own copy, I don't actually need to do this idiom; but I wanted to give an example of the idiom for the benefit of newbies reading this.
No. You will need to create a custom class that implements the __*item__() methods appropriately.
I was searching for a way to insert an ellipsis in a C# path, and found an answer here on stackoverflow: C# Path Ellipsis without Win32 API call
Using the RTM versions of VS2010 and .Net 4.0, I was unable to get the suggested method to work. I searched the 'Net and found example code that uses the same method, but it failed in the same way.
You can see the string I'm trying to shorten in my code below.
After calling the MeasureText method, both the input string (OriginalName) and the output string (ellipsisedName) look like this:
d:\abcd\efgh\ijkl\mnop\qrst\...\test.txt\0F\GHIJ\KLMN\OPQR\STIV\WXYZ\test.txt
Two problems:
1) The resulting string is narfed (the path is truncated as expected, but is followed by what looks like a C-style terminating null and a chunk of the original path).
2) My original string is changed to be identical to the output string.
Am I doing something wrong?
namespace WindowsFormsApplication2 {
public partial class Form1 : Form {
public Form1()
{
InitializeComponent();
string OriginalPath = #"d:\abcd\efgh\ijkl\mnop\qrst\uvwx\yzAB\CDEF\GHIJ\KLMN\OPQR\STIV\WXYZ\test.txt";
string ellipsisedPath = OriginalPath;
Size proposedSize = new Size(label1.Width, label1.Height);
TextRenderer.MeasureText(ellipsisedPath, label1.Font, proposedSize, TextFormatFlags.ModifyString | TextFormatFlags.PathEllipsis);
}
}
}
Holy moly, you've found a whopper of a bug. The P/Invoke used inside the TextRenderer class that calls DrawTextEx() is borked. That API function is writing back into the string, which it is allowed to do since the cchText argument is a LPTSTR, not a LPCTSTR. That destroys the .NET string content for both variables because the string is interned.
The bug isn't specific to .NET 4.0, I see it wrong in the ReferenceSource for .NET 3.5 SP1 as well and can repro it on VS2008. The trouble is in the internal WindowsGraphics.MeasureText function. You can report the bug at connect.microsoft.com.
A possible workaround is to alter the string so it gets copied and can't affect the original:
string ellipsisedPath = OriginalPath + '\0';
But the better workaround in this case is to simply not pass the ModifyString option, it serves no purpose. Which is safer too, there is still a possibility of destroying the garbage collected heap with the first workaround. The fix for Microsoft is similarly simple, it should just mask out the ModifyString option. It is documented to have no effect.
My original string is changed to be identical to the output string.
You've asked for this to happen by specifying TextFormatFlags.ModifyString, which the docs say
Modifies the specified string to match the displayed text. This value has no effect unless EndEllipsis or PathEllipsis is also specified.
This is (to my mind) an unusual way for a .NET Framework call to operate, but it does clearly say it will do this. Both the 'original' string and the 'output' string end up being modified, because string is a reference type (though usually with immutable value semantics) - when you say
string ellipsisedPath = OriginalPath;
you are actually just making ellipsisedPath refer to the same string instance as OriginalPath does. When this instance gets modified by the API call, both the references to it will see the modification.
As for
the path is truncated as expected, but is followed by what looks like a C-style terminating null and a chunk of the original path
my guess would be that the abstraction this managed wrapper provides around the Win32 API call is being somewhat leaky, as abstractions are prone to being - it's not shielding you from the fact that the underlying call works with C-style strings. It might be that you'll have to deal with yourself.