
Mike_R
Avatar/Signature-
Posts
316 -
Joined
-
Last visited
Content Type
Profiles
Forums
Blogs
Events
Articles
Resources
Downloads
Gallery
Everything posted by Mike_R
-
C# AddIn can't change cells in Excel
Mike_R replied to Hippo Man's topic in Interoperation / Office Integration
No. The .NET code recieves a RCW ("RuntimeCallable Wrapper") from the COM Interop. It does not know how that object was created. It might have been a COM object already in memory, or a new one created via .Add() or .Open() or the like. Your .NET code does not know and does not care... That said, your results are pretty quirky. Here's a few things to think about: (1) The Application.CalculateFull() command does not exist in older versions of Excel. I think it might also throw an error if there is not at least one Excel workbook open at the time it is called. (2) I'm not quite sure what you are trying to do with the range.set_Item() method. I think you might be looking for the Range.set_Value() method instead? [Edit: Ok, my bad, Range.set_Item() should be valid. (I forgot that the Range.Item() property is Read-Write -- it's not usually accessed this way.) So I don't know what's wrong here. I assume that the Worksheet is not protected? You might wan to try Range.set_Value() anyway, to see if you do any better...] Hope this helps! Mike -
Ok, I was wrong, in C# the results are substantially the same. Here are the two key classes: public class BaseList<BaseType> : System.Collections.Generic.List<BaseType> { } public class DerivedList<BaseType, DerivedType> : BaseList<BaseType> where DerivedType : BaseType { public void Add(DerivedType item) { base.Add(item); } public DerivedType Item(int index) { return (DerivedType)base[index]; } } Ok, and now our PersonList and EmployeeList classes: class PersonList : BaseList<Person> { } class EmployeeList : DerivedList<Person, Employee> { } And now our testing routine: void RunIt { PersonList pList = new PersonList(); EmployeeList eList = new EmployeeList(); MessageBox.Show((pList is PersonList).ToString()); // True MessageBox.Show((eList is PersonList).ToString()); // *** False *** MessageBox.Show((pList is EmployeeList).ToString()); // False MessageBox.Show((eList is EmployeeList).ToString()); // True } Using C#, the 2nd and 3rd lines in the above give a light-warning whereas in VB.NET it is a stronger compiler error and won't run. Running it in C# returns the results shown above, where 'eList is PersonList' returns False. Oh well, it was worth a shot. Although theoretically interesting to kick this around... I can't imagine that this could actually ever be a problem in anyone's code. It is interesting that Arrays *CAN* exhibit this kind of "Container Inheritance" and in effect are exhibiting multiple inheritance if you think about it -- inheriting from the Array class as well as from the inner-type as well. :cool:
-
I don�t think there was much confusion, really... For Value types the advantage is generally execution speed. (One isn't usually coercing between value types.) For Reference types the advantage is strong typing so that you don't have to cast; the problem here is not so much execution speed, but the possibility of programmer error. Generics allow us to make a strong-typed collection class in one line; it really is beautiful. But back to the issue at hand... I think we just have to accept that Generics really are just "Templates" and should be thought of (more or less) as a short-cut to our actually having to Copy-Paste code and then refactor to change the data type. That's all that Generics are really doing for us... So a List(Of Person) versus a List(Of Employee) really is just a Copy-Paste / Find-Replace operation. While we know that the two inner-classes are inheriting, the outer List classes are really more of a copy-paste construction - no inheritance. Here was my next attempt at how this might be handled, although I will not in advance that is fails. But here was the attempt: Class BaseList(Of BaseType) Inherits System.Collections.Generic.List(Of BaseType) End Class Class DerivedList(Of BaseType, DerivedType As BaseType) Inherits BaseList(Of BaseType) Sub Add(ByVal newItem As DerivedType) MyBase.Add (newItem) End Sub Function Item(ByVal index As Integer) As DerivedType Return DirectCast(MyBase.Item(index), DerivedType) End Function End Class There is very little code required to code here because the BaseList(Of BaseType) inherits from Generic.List(Of BaseType) and the DerivedList(Of BaseType, DerivedType As BaseType) inherits from BaseList(Of BaseType). Now, within the DerivedList(Of BaseType, DerivedType As BaseType) we have to overload the members that we wish to strong-type 'As DerivedType'. But we only have to do this once. For our PersonList and Employee list all we need to do is the following: Class PersonList Inherits BaseList(Of Person) End Class Class EmployeeList Inherits DerivedList(Of Person, Employee) End Class That's it! But it does not *quite* work. In VB.NET, I attempted to run the following: Shared Sub TestIt() Dim pL As New PersonList Dim eL As New EmployeeList MessageBox.Show((TypeOf (pL) Is PersonList).ToString) MessageBox.Show((TypeOf (eL) Is PersonList).ToString) ' *** Error *** MessageBox.Show((TypeOf (pL) Is EmployeeList).ToString) ' *** Error *** MessageBox.Show((TypeOf (eL) Is EmployeeList).ToString) End Sub But the two lines marked "*** Error ***" failed to compile, with the message stating "Expression of type 'EmployeeList' can never be of type 'PersonList'.". Hmmm... oh, well, it was worth a shot. By the way, it also will not compile in C# either, but for a different reason... I'll put that in the next post. -- Mike
-
Well, you'd expect that in this case, right? ('Person' is the base class in this mini-example, and 'Employee' is the derived class.)
-
Ah yes! Very cool... And this actually opens a door to something else... If you think about what you've just shown, it suggests that arrays exhibit a form of multiple inherintance, which is not supposed to be allowed by the CLR. That is, arrays inherit from the 'System.Array' class, but they also seem to inherit from the type to which they are declared. (You have proven the latter.) I don't know whether to think of arrays as "inherently generic" or "exhibiting mutiple inheritance", but it would seem to be closer to the latter. From what I understand, multiple inheritance is supposed to be a bear to implement on the back end, which is why we don't have it in .NET... But I wonder if for Array's that they found it necessary and decided to implement multiple inheritance for array's only behind-the-scenes? Or perhapse array's are actually generic behind-the-scenes and they have some *extra* check or the like to allow type-checking to make them appear to exhibit multiple inheritance? Pure speculation on my part, for I do not have a clue here, but maybe some of the guru's around here can shed some light....
-
Cool, glad you got it. :)
-
Usually it should, but I find that it won't always in VB.NET, especially if you Dim a variable such as 'myVar' and then rename it 'MyVar'. In this case, all the uses of 'myVar' later in your code will not change their capitalization. I suppose that this is a minor annoyance, but in VB.NET you do not need this so much as we did in VB 6.0. With .NET, the squigly undererlines will tell you if a variable is not being recognized. That said, if your variables are never identifying the capitalization, then it sounds like something is wrong. Either there is a setting for this (somehow I don't think so?) or there is something wrong with your installation? Not sure...
-
Hi guys, This is very theoretical, and not very practical, but I was wondering what other people's thoughts were on the following: Shared Sub RunIsTypeOf() Dim obj As Object Dim isSameType As Boolean obj = New Employee ' <-- Which Inherits from the 'Person' class. isSameType = TypeOf obj Is Person MessageBox.Show(isSameType.ToString) ' <-- Returns True obj = New Generic.List(Of Employee) isSameType = TypeOf obj Is Generic.List(Of Employee) MessageBox.Show(isSameType.ToString) ' <-- Returns True isSameType = TypeOf obj Is Generic.List(Of Person) MessageBox.Show(isSameType.ToString) ' <-- Returns False! End Sub Class Person End Class Class Employee Inherits Person End Class Note that the last line returns 'False' which makes sense, but, well, in some sense you might like it to return 'True'. As a practical matter, so long as the list is not empty, one could pop out one element and then check the type on that, so, really, I don't see any *real* problem with this scenario... But I was wondering what anyone else's thoughts were on this? Mike
-
Heh, thanks for trying 'MustInherit NotIheritable'. :) Sounds ridiculous, but, well, it was worth a shot. It's no less sensible than a private constructor, if you think about it, but a private constructor is allowed, so that's the only way I guess. On the other hand, it is interesting that an abstract class can actually have its shared methods called directly; astract classes feel like an incomplete beast, but their static members are fully formed and callable. Cool. :cool: The IL on a Module is interesting, I think a Module might default to being Friend/Internal, but I'm pretty sure that it can be public. I think that the Microsoft.VisualBasic.Constants namespace is a public module in order for constants such as 'vbCrLf' can be accessible without the need for a long namespace path or imports statement. Kinda neat. But for the most part, a static class is a better idea, I think...
-
Is there an 'Out' Keyword or Attribute for VB.NET?
Mike_R replied to Mike_R's topic in Visual Basic .NET
Hmmm... More food for thought: I'm not sure that I agree with him here... (Or maybe I just don't understand him?) Generally, I think of an exception being thrown meaning that "all bets are off", and I would NOT expect a return value in this case. For example, if a Function or Property Get that is intended to return a value throws an exception, then no value is assigned. You can't even try to "trick it" into doing so by assigning the return value and then throwing the exception:Function MyFunction() As Integer MyFunction = 5 Throw New Exception End Function Any attemt to assign a variable to 'MyFunction' will never execute, regardless of any Try..Catch..Finally protections. E.g.: Dim i As Integer Try i = MyFunction() Catch ex As Exception End Try MessageBox.Show(i.ToString) The above still returns '0'. So I'm not sure what he's getting at... Anyway, I obviously have a strange bee in my bonnet today, I'll try to keep it in check. :p Have a good one guys :), Mike -
Yeah, I just hit on this too and was surprised... And I agree with you, one would think it would be easy for them to do this. (And so would an 'out' keyword, for that matter.. :p) Thanks PD :)
-
Hi guys... Are there "Shared" Classes in VB.NET 2.0 to match C#'s "Static" classes? Or did they not bother in VB.NET because VB.NET has Modules? The thing is though, a Static Class (enforcing that there are no instance members) is not quite the same as a Module, which is essentially a Static class plus an implicit global imports statement... Is the solution, then, that I have to continue creating a Private New() contructor for VB.NET classes that I wish to enforce as Static? Using the 'Shared' keyword in front of a Class definition does not seem to be accepted by the compiler. Anyway, I'm guessing that this is in fact the "lay of the land", but I was wondering if anyone else had some thoughts on this... Thanks in advance! :), Mike
-
Is there an 'Out' Keyword or Attribute for VB.NET?
Mike_R replied to Mike_R's topic in Visual Basic .NET
Ok, fair enough, I guess the most you could get there is an "unused variable" warning or the like, but, as I showed above, passing it in somewhere 'ByRef' was enough to saticefy it... LOL. :p Pretty funny... Maybe someday we'll have full blown AI to trace through this sort of thing! :eek: Thanks PD -
Is there an 'Out' Keyword or Attribute for VB.NET?
Mike_R replied to Mike_R's topic in Visual Basic .NET
Hey, Thanks Marble Eater, I just think that it's a nice "handshake" making everything explicit what this variable is to do. ByRef is a tad sloppier, because it can work as an in/out parameter not just 'out'. VB can force an 'out' parameter by using a Function or Property Get procedure, so it's not impossible to force an 'out' interpretation, but, well, sometimes you want a "regular" parameter to be tagged as 'out'. As you suggested, I could initialize the variable, sure, to suppress the warning, or I could even just turn that warning off, but the real idea is to enforce the contract. As for the compiler error, it's actually just a soft compiler "warning" so it's no big deal... but this would not be occurring in C# if using the 'out' keyword. Even trying again now, I was almost unable to reproduce it, as it seems fine for value types. For example, the following shows no compiler warnings: Sub MySub Dim i As Integer ReturnInteger(i) End Sub Sub ReturnInteger(ByRef out_Integer As Integer) ' out_Integer = 10 End Sub Even with the code commented out, the compiler sees that the 'i' variable is at least passed in somewhere to a ByRef parameter and looks no further. There is no warning on this code. However, with a reference type, you can get a warning: Sub MySub() Dim c As Class1 ReturnClass1(c) MessageBox.Show(c.Name) End Sub Sub ReturnClass1(ByRef out_Class1 As Class1) out_Class1 = New Class1 End Sub Public Class Class1 Public Name As String = "Yoda" End Class The above runs 100% fine, of course, but the VB 2005 IDE will place a squigly line under the "c" within the line 'ReturnClass1©, with the warning stating: Not the biggest deal in the world, at all, but I was previously hoping that VB might recognize the OutAttribute, which upon reading is really only used for COM Interop marshalling. Oh well, maybe VB will add the 'out' keyword some day, there's no reason for them not to... Thanks for your thoughts here, ME, :), Mike -
Does anyone know if there is a way to effect the C# 'out' keyword in VB.NET? There is an OutAttribute, but this apparently is only for use when working through the COM Interop. This does seem to be a weakness in VB.NET that bugs me from time to time -- for example, the compiler will warn about an unused variable that has actually been passed into a 'ByRef' parameter, which is the best that VB.NET can do, as far as I can tell... Does anyone know of a way to effect the 'out' keyword in VB? Thanks in advance for any thoughts on this... Mike
-
Wow, thank you Nerseus for that outstanding reply... The only thing that I did not quite understand was your line: I guess this is weird if you think about it, but I guess even though different "objects", the system must be discerning the address of the object passed into the delegate (along with the delegate's type) to determine if a delegate is the *same*? But what other syntax is there? (You refer to yours as "awkward", but I'm not sure why?) [Edit: Ok, I get, it, we're stuck with this syntax, and you don't like it, yeah, I get you... You can always switch to VB and use 'AddHandler' and 'RemoveHandler' instead! :p] Overall, thank you so much for your feedback... Mostly, it makes me comfortable "thinking for myself" and therefore strong-typing the Delegate's or Event's 'sender' parameter and not worry too much about .NET rules on this. I do think that strong-typing it here makes the code clearer and more of a compile-time contract. I suppose it would be kind of nice if the Event or Delegate could have a strong-typed definition such as 'sender As TextBox' while the Handler could use a widecasted definition of the same, such as 'sender As Control' or 'sender As Object', if one wanted to handle a "wider" definition of objects that could raise this Event. Unfortunately, however, it does seem that the Delegate-to-Handler and definitions have to match precisely, or the compiler complains...
-
Hey Cags, Yeah, you know, I think you are dead-on. I was just comming to this realization and was about to post this idea myself. Still, I would think that 99.9% of the time that one is dealing with either a single 'sender' type, or perhaps the 'sender' is a base class, such as a control, whereby the handler could discern using .GetType and then lcast to Button, or TextBox, etc. as needed and so strong-typing the handler using 'sender As Control' would still seem to be valid here, where each event actually fires a 'sender As TextBox' or what-have-you, where the 'sender' is inheriting from 'control'. So, I guess using Sender As Object is the most flexible, but I wonder how bad it would be, really, to strong-type it? It just "feels" better if strong-typed I think; the handler having to cast the object to the required type when you *know* what it is, but the compiler doesn't, just feels lousy. :( Just thinking out loud, I guess...
-
I'm not quite sure why I'm bugged by this, but does anyone have an opinion as to why the .NET rules regarding custom Event Arguments is that the 'sender' parameter be typed 'As Object'? Why not strong-type it to the actual type of the sender? For that matter, why have a separate parameter at all; why not have it included within the 'e As EventArguments' parameter, and then the handler can utilize 'e.Sender' as needed? Any thoughts on this? For the life of me I can't think of one good reason for keeping 'Sender As Object' and so I'm thinking of strong-typing my Events... However, this would be non-standard, so I was wondering if anyone knew or could explain the rationale behind this? Thanks in advance! Mike
-
How do you kill excel when you are finished?
Mike_R replied to mskeel's topic in Interoperation / Office Integration
Mskeel, Glad it worked for you. :) Once you get the hang of it, as Bri189a says, it's really not that hard... Bri189a, Well, there are a few issues here that can make it easy or hard. Once you get used to it, it's pretty easy, but the early days can be very frustrating. Also, if you are controlling Excel from an in-process DLL add-in, you cannot make Excel hang no matter how badly you code. If not using a DLL and are controlling from an out-of-process EXE, then using the Marshal.ReleaseComObject() approach requires some rather awkward coding. Here's an older tutorial I wrote using this technique: http://www.xtremevbtalk.com/showthread.php?t=129690 But I find it extremely awkward to use in practice and I really do not recommend it to anyone... -- Mike -
How do you kill excel when you are finished?
Mike_R replied to mskeel's topic in Interoperation / Office Integration
Using Marshal.ReleaseComObject() properly is almost impossible to do corerctly, unfortunately. It's extremely easy to make a mistake -- if you forget to release even one object absolutely anywhere in your code, then Excel will hang. Mskeel, your code by contrast looks pretty good. I don't see any flaw in it and the only thing I can suggest is possibly adding a call to GC.WaitForPendingFinalizers() after calling GC.Collect(). But other than that, it looks clean, best I can tell. If all else fails, you can close the Excel instance by force via the Process class and then calling Process.Kill(). An example of this can be found here: http://www.xtremevbtalk.com/showthread.php?p=956122#post956122 Hope this helps! Mike -
Excel with c#.net: loop through range?
Mike_R replied to ThienZ's topic in Interoperation / Office Integration
For reasons that make no sense whatsoever, the For..Each enumeration is not available on many Excel objects that do have them in VBA or VB6 Automation. The Range.Cells collection is the most commonly used one that one will hit. I believe that this is due to a flaw in the PIAs, but I can't say for sure. Perhaps COM [_Enum] For..Each enumeration is not supported through the Interop?? (Somehow I find that very hard to believe though.) Excel has other quirks such as Excel.Application events being private (that is, it is unavailable in .Net) unless you modify the CIL code yourself by hand. Anyway, you will have to enumerate your Ranges as: For r = 1 to xlRng.Rows.Count for c = 1 to xlRng.Columns.Count myValue = xlRng(r,c).Value Next c Next r (Please forgive the VB-code, I'm sure you get the idea.) Making this worse, if the Range is a multi-area range, then you'll need to enumerate the areas as well. (Again, I think For..Each is not avail. for the Areas collection from .Net again): For a = 1 to xlRng.Areas.Count Dim area As Range = xlRng.Areas(a) For r = 1 to area.Rows.Count for c = 1 to area.Columns.Count myValue = area(r,c).Value Next c Next r Next a Note too that when Range.Areas(1).Cells.Count is > 1 then xlRng.Value will return an array consisting of all the values within Areas(1).Cells(). This array can then can be enumerated vastly faster than iterating through the Range.Cells(), which is notoriously slow. See this thread for more info: Why is this running so slow?. Hope this helps... Mike -
Developing with COM Components
Mike_R replied to RookPSU's topic in Interoperation / Office Integration
-
Ok, thanks Ingis, I figured that there had to be something like that... Ok! Despite all my whining in Post #16, I'm going to convert all my low-level libraries to C#. I'm going to leave my higher-level libraries dealing with VBA stuff as VB.Net, but I'll convert the lower stuff. This will be a lot of work, but probably a great way to learn the language as well... God help me, this might take forever, LOL. :)
-
Odd, but it can. It has to do with the Generation of your Objects. When a GC.Collect() call occurs (usually because the Heap has been exhausted) the GC will release the unreachable objects and then compact/move the rest of them. (There are some exceptions for locked objects and larger objects that are never moved.) It then increments the Generation of the surviving objects. All objects start at generation 0, meaning they have "survived" zero GC.Collect() calls. Once a GC.Collect() has occured, the survivors all get incremented by 1. So all Objects that had a 0 now have a 1. If you survive a 2nd GC.Collect() call, then you're bumped up to 2. That's the limit though, generatiion '2' objects don't get incremented -- 2 is the highest that it goes. The point of all this is that there are different types of objects. Generally there are larger resource objects, often Singletons, that are held in memory for the duration of your Application. These are typically opened when your Application begins and then close when your Application ends. It's too expensive (slow to open/close) so you keep it open. Or it's an object maintaining your entire internal data structure -- it's critical to your App and so it stays "alive" for the duration of your Application. But then there are relatively small "throw away" objects. String objects, for example, are created and thrown away like popcorn in a typical application. A few strings like the Login Name might be held, but if any parsing or string manipulation is involved, you are likely creating and destroying a fair volume of Strings. This would be true of a number of minor objects of your Apps own creation as well. So when a GC.Collect() call does occur, the "Major Objects" will survive, because a reference is held, but most of the "Minor Objects" are likely collectable. What happens after a couple of GC.Collect's is that the Major Objects will quickly graduate to level 2. Now the GC.Collect() mechanism is efficient in the sense that it typically will not check all generations for collection. Something that has survived to Generation 2 is likely to survive to Generation 5 or even 17! (Which is why they stop the counting at 2.) So the Garbage Collector knows that checking the Generation 2 guys is not a very high probability undertaking. So it usually only checks the Generation 0 objects for collection. Sometimes the Level 1 and mostly not the Level 2. So, here's the upshot of forcing a call to GC.Collect(): Let's say that you pre-maturely call GC.Collect(). You may have a number of "Minor Objects" that currently have references holding them, but probably would not survive much longer. But if you impatiently call GC.Collect() -- especially if you do it twice -- then you could artificially promote these objects to Generation 2. Now that they are Generation 2 they are likely to persist for a very long time... ... unless you call GC.Collect() again! Fixing a bad habit with another bad habit! You're now forced into the need to manage this process "manually" in a vain attempt to be "efficient". This whole scenario is a bit of a stretch, but you get the idea... The only time I find the GC.Collect() call to be legitimate (other than in time testing situations) is when releasing unmanaged resources. Some COM ActiveX Servers such as MS Excel just hang. :( Using Marshal.ReleaseComObject() is supposed to be the mechanism to release this, but as a practical matter it is all but impossible to use it correctly. I find that a call to GC.Collect() tends to be the magic bullet 99% of the tme, and is less brutal than resorting to something like Process.Kill(). Well, there you go, hopefully this is what PlausiblyDamp had in mind... Otherwise I've just wasted a lot of bandwidth! :)
-
That advice is fine, and it's worth knowing a bit about the compiler options... But there's no need for that much hassle. All you need to do is this: DLL/ActiveX project template for VB .net Std ed Also, the 1st Project I send you in Post #8 is a perfectly valid blank Class Library Project. Just keep a copy of this around. When you need a new Class Library project, just copy the entire folder to a new location and then rename it as needed. :)