Looping through VB6 Structure members in VB.NET

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Frinavale
    Recognized Expert Expert
    • Oct 2006
    • 9749

    Looping through VB6 Structure members in VB.NET

    I'm using a VB6 library in my VB.NET application.

    This library has a function that retrieves report records (as structures).

    One of the structures in this library contains 105 public Boolean members that indicate to the function which "types" of records should be included while running a report.

    The report record structures retrieved by the function contain this report type structure in order to indicate the"type" of record retrieved. You are able to tell which type of record it is by finding the single Boolean member is set to True in the record type structure.

    Is there a way to loop through the VB6 Structure members?

    Please note that the 105 Booleans are not Properties...th ey are simply publicly exposed Booleans. (So the System.Reflecti on PropertyInfo won't work)

    Thanks for your time,

    -Frinny
  • balabaster
    Recognized Expert Contributor
    • Mar 2007
    • 798

    #2
    Hey Frinny...

    Have you tried looking at the FieldInfo?
    VB
    Code:
    Dim t As Type = GetType(MyType)
    Dim f As FieldInfo() = t.GetFields(BindingFlags.ExactBinding
    C#
    Code:
    type t = GetType(MyType);
    FieldInfo[] f = t.GetFields(BindingFlags.ExactBinding);
    I can't be 100% sure this will work with VB6 structures, however, you can reference fields in regular .NET classes (the same as you can with properties). I haven't tried it with any VB6 classes - and I don't have VB6 any more so I can't even knock up a dummy library to test the theory sadly...

    Comment

    • balabaster
      Recognized Expert Contributor
      • Mar 2007
      • 798

      #3
      Does this help any?

      Code:
      public struct MyStruct
      {
              public bool item1;
              public bool item2;
              public bool item3;
              public bool item4;
              public bool item5;
      
              public MyStruct(bool item1, bool item2, bool item3, bool item4, bool item5)
              {
                  this.item1 = item1;
                  this.item2 = item2;
                  this.item3 = item3;
                  this.item4 = item4;
                  this.item5 = item5;
              }
      
      }
      
      class Program
      {
              static void Main(string[] args)
              {
                  MyStruct m = new MyStruct(true, true, false, false, true);
      
                  Type t = typeof(MyStruct);
                  FieldInfo[] f = t.GetFields();
                  FieldInfo[] selected = Array.FindAll(f, item => (bool)item.GetValue(m) );
      
                  foreach (FieldInfo item in selected)
                  {
                      Console.WriteLine(String.Format("{0}: {1}", item.Name, item.GetValue(m)));
                  }
      
                  Console.ReadKey();
              }
      }

      Comment

      • Frinavale
        Recognized Expert Expert
        • Oct 2006
        • 9749

        #4
        What is item in the following code?
        Code:
                    FieldInfo[] selected = Array.FindAll(f, item => (bool)item.GetValue(m) == true );

        Comment

        • Frinavale
          Recognized Expert Expert
          • Oct 2006
          • 9749

          #5
          Ok, hold on a second....I see...I'm looking up Lambda expressions / functions.

          :)

          Comment

          • balabaster
            Recognized Expert Contributor
            • Mar 2007
            • 798

            #6
            Originally posted by Frinavale
            What is item in the following code?
            Code:
                        FieldInfo[] selected = Array.FindAll(f, item => (bool)item.GetValue(m) == true );
            Okay, this uses lambda functions... a new concept that was introduced in .NET 3.0/3.5.

            I don't know of your understanding of delegates, function pointers or callbacks but basically it's declared in a somewhat similar manner as a callback...

            So what's going on here is FindAll grabs "f" which is our array of FieldInfo and for every item in the array it runs the lambda function defined on the right side of the comma.

            "item" is an arbitrary name I used to reference the current item that is being reviewed in the array. I could have called this, n, k, s, foo... it's just arbitrary. FindAll passes the current item in the array into this variable (just the same way you'd pass into another method).

            The lambda function then takes this value as a FieldInfo and uses item.GetValue(m ) (where m is our structure instance) to check if the field that matches the signature specified by "item" in object m is set to true and returns an implicit boolean back to the FindAll method.

            If True is returned to FindAll then the current field is added to the return list. Once FindAll has iterated through all items in the list it returns the list which is stored in the variable Selected.

            Think of the following code which would do the same kind of thing:

            Code:
            public FieldInfo[] FindAll(MyStruct inst, FieldInfo[] fieldList)
            {
              List<FieldInfo> matches = new List<FieldInfo>;
              foreach(FieldInfo item in fieldList)
              {
                 if (item.GetValue(inst) == true)
                    matches.Add(item.Name);
              }
              return matches.ToArray();
            }
            I've got a post on my blog that covers this: Predicates/List.FindAll()

            Comment

            • Frinavale
              Recognized Expert Expert
              • Oct 2006
              • 9749

              #7
              So I ended up learning a lot about the Delegates, Lambda Expressions / Functions, and the Array.Find methods.

              [code=vbnet]
              Private Function RetrieveRecordN ame(ByVal retrievedRecord Type As RecordType) As String
              Dim recordName As String = ""

              Dim rType As Type = GetType(RecordT ype)
              Dim recordTypeMembe rs() As FieldInfo = rType.GetFields (BindingFlags.I nstance Or BindingFlags.Pu blic)

              'Method 1
              'Using Lambda Expression with the Array.FindAll method to retrieve all the selected record types

              'Dim selectedMembers () As FieldInfo = Array.FindAll(r ecordTypeMember s, Function(x As FieldInfo) CType(x.GetValu e(retrievedReco rdType), Boolean) = True)
              'If selectedMembers IsNot Nothing Then
              ' recordName = selectedMembers (0).Name
              'End If

              'Method 2
              'Using Lambda Expression with Array.Find method to retrieve the first selected record type
              Dim selectedMember As FieldInfo = Array.Find(reco rdTypeMembers, Function(x As FieldInfo) CType(x.GetValu e(retrievedReco rdType), Boolean) = True)
              If selectedMember IsNot Nothing Then
              recordName = selectedMember. Name
              End If

              'Method 3
              'Not using the Array.Find methods nor Lambda Expressions to find the first selected item
              'Dim selectedMember As FieldInfo = Nothing
              'For Each item In recordTypeMembe rs
              ' If CType(item.GetV alue(retrievedR ecordType ), Boolean) Then
              ' selectedMember = item
              ' Exit For
              ' End If
              'Next
              'If selectedMember IsNot Nothing Then
              ' recordName = selectedMember. Name
              'End If

              Return recordName
              End Function


              [/code]


              I still haven't totally figured out how to find the Array.Find method with a delegate to solve this problem...yet.. .I'm still working on it.

              Thanks for all your help!

              -Frinny

              Comment

              • balabaster
                Recognized Expert Contributor
                • Mar 2007
                • 798

                #8
                All looks good - just a pointer, it might not seem like much but you might consider using DirectCast instead of CType.

                We're not actually converting the objects to boolean, we're telling the code that these objects should be interpreted as boolean - because while they are passed as a "typeless" object, they are really boolean. Thus DirectCast should perform better than CType in this situation. Syntactically, the two methods are the same (other than their obviously different names)...

                If we were to pass 0 or the string literal "false" but wish to interpret it as boolean, then we'd use CType.

                Comment

                • Frinavale
                  Recognized Expert Expert
                  • Oct 2006
                  • 9749

                  #9
                  Originally posted by balabaster
                  All looks good - just a pointer, it might not seem like much but you might consider using DirectCast instead of CType.

                  We're not actually converting the objects to boolean, we're telling the code that these objects should be interpreted as boolean - because while they are passed as a "typeless" object, they are really boolean. Thus DirectCast should perform better than CType in this situation. Syntactically, the two methods are the same (other than their obviously different names)...

                  If we were to pass 0 or the string literal "false" but wish to interpret it as boolean, then we'd use CType.
                  Good point :)

                  I didn't mention that, because of how the .NET Interop treats the structure, the Booleans are actually shorts.

                  Comment

                  • balabaster
                    Recognized Expert Contributor
                    • Mar 2007
                    • 798

                    #10
                    Originally posted by Frinavale
                    Good point :)

                    I didn't mention that, because of how the .NET Interop treats the structure, the Booleans are actually shorts.
                    ah, gotcha - I guess it's okay the way it is then...

                    Comment

                    • Frinavale
                      Recognized Expert Expert
                      • Oct 2006
                      • 9749

                      #11
                      Originally posted by Frinavale
                      I still haven't totally figured out how to find the Array.Find method with a delegate to solve this problem...yet.. .I'm still working on it.
                      I never did get this to work.
                      The Find method requires a reference to a function that takes 1 parameter...I need there to be 2 parameters.

                      The Lambda Function gets around that requirement very nicely!
                      I'm starting to really understand how powerful Lambda Functions are.


                      Aside from that...
                      I am now attempting to use the System.Reflecti on.FieldInfo class to set values in my type with the following code:
                      [code=]
                      Dim rec As New RecordType
                      Dim recType As Type = GetType(RecordT ype)
                      Dim recordTypeMembe rs() As FieldInfo = kTransactionTyp e.GetFields(Bin dingFlags.Insta nce Or BindingFlags.Pu blic)
                      For Each item In recordTypeMembe rs
                      Dim x As Short = -1
                      item.SetValue(r ec, x)
                      Next
                      [/code]

                      Despite what I try the values are not changed.
                      According to this article on the FieldInfo.SetVa lue(obj,value) Method:

                      This method will assign value to the field reflected by this instance on object obj. If the field is static, obj will be ignored.
                      My field is not static (note the GetFields(Bindi ngFlags.Instanc e Or BindingFlags.Pu blic) returns public instance fields) but this still isn't working.

                      Comment

                      • Frinavale
                        Recognized Expert Expert
                        • Oct 2006
                        • 9749

                        #12
                        Sometimes I'm an idiot.
                        The SetValue method requires an Object as the parameter.
                        I'm not passing it an Object, I'm passing it a Structure.

                        I think this is the source of my problem.
                        It is strange that the GetValue method still works when I pass the method a Structure though...

                        <edit>
                        Wouldn't you know, there's a thread here on the bytes about the SetValue method not working with Structures ...too bad the link doesn't work.</edit>
                        Last edited by Frinavale; Nov 18 '08, 08:53 PM. Reason: added more findings

                        Comment

                        • balabaster
                          Recognized Expert Contributor
                          • Mar 2007
                          • 798

                          #13
                          Originally posted by Frinavale
                          I never did get this to work.
                          The Find method requires a reference to a function that takes 1 parameter...I need there to be 2 parameters.
                          There's no nice way to get around this - you have to use a class level field to transfer the value into your delegate - it's ugly as sin, I hate that. So yes, Lambda expressions nicely sidesteps that oversight.

                          As for not being able to set your value... this appears to be an issue with the fact that the object is a structure. I had a play and the only way I could get around it was to change MyStruct from a structure to a class.

                          I'm not guessing that's too much help... I did some checking and it was reported a while back to Microsoft to fix - they closed it stating that this was by design!

                          I'm wondering if they really checked it out though - it seems the guy who reported it was trying to pass the structure as an argument into another method byval instead of byref which doesn't really apply in this case.

                          Comment

                          • Frinavale
                            Recognized Expert Expert
                            • Oct 2006
                            • 9749

                            #14
                            Originally posted by balabaster
                            There's no nice way to get around this - you have to use a class level field to transfer the value into your delegate - it's ugly as sin, I hate that. So yes, Lambda expressions nicely sidesteps that oversight.

                            As for not being able to set your value... this appears to be an issue with the fact that the object is a structure. I had a play and the only way I could get around it was to change MyStruct from a structure to a class.

                            I'm not guessing that's too much help... I did some checking and it was reported a while back to Microsoft to fix - they closed it stating that this was by design!

                            I'm wondering if they really checked it out though - it seems the guy who reported it was trying to pass the structure as an argument into another method byval instead of byref which doesn't really apply in this case.
                            I came across that same information.

                            The best explanation I've found as to why this wont work is in this article.

                            There are a few other forums where people have linked to "solutions" to this problem but all of the links are not working. It almost feels as if someone's trying to hide the answer from me.

                            I've been working on trying to trick the SetValue function by wrapping my structure in an Object.....

                            Seeing as this is an example application (for other developers) I'm really looking for a simple way to do this........... ............... ......

                            This is anything but simple.

                            Comment

                            • balabaster
                              Recognized Expert Contributor
                              • Mar 2007
                              • 798

                              #15
                              Ha! Got it!

                              C#
                              Code:
                              public static void setValue(object var, string fieldName, string newValue)
                              {
                                  Type t = var.GetType();
                                  FieldInfo f = t.GetField(fieldName);
                                  if (t.IsValueType)
                                  {
                                      ValueType vt = (ValueType)var;
                                      f.SetValue(vt, newValue);
                                  }
                                  else
                                  {
                                      f.SetValue(var, newValue);
                                  }
                              }
                              
                              static void Main(string[] args)
                              {
                                  MyStruct m = new MyStruct();
                                  m.item1 = "Old Value";
                              
                                  Type t = typeof(MyStruct);
                                  BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
                                  FieldInfo f = t.GetField("item1", flags);
                              
                                  Console.WriteLine(f.GetValue(m));
                                  setValue(m, f.Name, "New Value");
                                  Console.WriteLine(f.GetValue(m));
                              
                                  Console.ReadKey();
                              }
                              We can apply this to our situation just trading up the boolean values for the strings...

                              It's because Structures are referenced differently than Classes... we need to tell reflection to update a ByVal object and not a ByRef object. Structures are treated in the same way as regular variables - albeit complex variables. Classes are treated as objects and as such the two are referenced slightly differently.

                              Comment

                              Working...