-2

Hi all you c# wizards!

I need to store all the memory offset values of (packed) nested structs within these respective structs. Recusively looping through all the members works fine so far. Also, i get the appropriate memory offset values. This struct contraption might contain several dozends of structs, and several hundreds of other members in the end. But i do this whole thing at initialization time, so CPU performance won't be an issue here.


But:

In this iteration process, it seems i have trouble accessing the actual instances of those structs. As it turns out, when i try to store these offset values, they don't end up where i need them (of course, i need them in the instance "SomeStruct1" and its containing other struct instances, but the debugger clearly shows me the init values (-1)).

I suspect "field_info.GetValue" or "obj_type.InvokeMember" is not the proper thing to get the object reference? Is there any other way to loop through nested struct instances?

Please help! I've desperately debugged and googled for three days, but i'm so out of ideas now...

Thanks for your efforts!

-Albert


PS - the reason i do this unusual stuff: I communicate between two embedded CPU cores via the mentioned nested struct (both are mixed c/c++ projects). This works like a charm, as both cores share the same memory, where the struct resides.

Additionally, i have to communicate between a c# host application and theses embedded cores, so i thought it could be a neat thing, if i implement a third instance of this struct. Only this time, i oviously can't use shared RAM. Instead, i implement value setters and getters for the data-holding members, find out the memory offset as well as the lenght of the data-holding members, and feed this information (along with the value itself) via USB or Ethernet down to the embedded system - so the "API" to my embedded system will simply be a struct. The only maintenance i have to do every thime i change the struct: i have to copy the holding .h file (of the embedded project) to a .cs file (host project). I know it's crazy - but it works now.

Thanks for your interest. -Albert


This is a simplified (buggy, see below) example that should compile and execute (WinForms, c#7.3):

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace CodingExample
{
    public interface Interf
    {
        Int32   Offset  {get; set; }
    }

    [StructLayout (LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    public struct sSomeStruct2 : Interf
    {
        public sSomeStruct2 (bool dummy)
        {
            Offset      = -1;
            SomeMember3 = 0;
        }
        public Int32    Offset  {get; set; }
    
        public Int32    SomeMember3;
        // much more various-typed members (e. g. nested structs)...
    }

    [StructLayout (LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    public struct sSomeStruct1 : Interf
    { 
        public sSomeStruct1 (bool dummy)
        {
            Offset      = -1;
            SomeMember1 = 0;
            SomeStruct2 = new sSomeStruct2 (true);
            SomeMember2 = 0;
        }
        public Int32        Offset  {get; set; }

        public Int32        SomeMember1;
        public sSomeStruct2 SomeStruct2;
        public Int16        SomeMember2;
        // much more various-typed members...
    }

    public partial class Form1 : Form
    {
        void InitializeOffsets (object obj)
        {
            Console.WriteLine ("obj: {0}", obj);

            Type obj_type   = obj.GetType ();

            foreach (FieldInfo field_info in obj_type.GetFields ())
            { 
                string field_name   = field_info.Name;
                Int32 offset        = (Int32) Marshal.OffsetOf (obj_type, field_name);
                Type field_type     = field_info.FieldType;
                bool is_leafe       = field_type.IsPrimitive;

// none of theses three options seem to give me the right reference:
//                object node_obj     = field_info.GetValue (obj);
//                object node_obj     = field_info.GetValue (null);
                object node_obj     = obj_type.InvokeMember (field_name, BindingFlags.GetField, null, obj, null);

                Console.WriteLine ("field: {0}; field_type: {1}; is_leafe: {2}; offset: {3}", field_name, field_type, is_leafe, offset); 

                if (! is_leafe)
                {
// this writes not as expected:                    
                    (node_obj as Interf).Offset = offset;
    
                    InitializeOffsets (node_obj);
                }
            }
        }

        sSomeStruct1 SomeStruct1; 

        public Form1 ()
        {
            InitializeComponent ();

            SomeStruct1 = new sSomeStruct1 (true);

            InitializeOffsets (SomeStruct1);
        }
    }
}
  • 2
    Maybe you should describe what you want to do. What you do this all for. There must be a better solution, C# has never been about byte pushing and offset counting in memory structures. – nvoigt Nov 16 '21 at 16:42
  • 2
    What do you mean by *"the right reference"*? You have structs. They're getting boxed. Passing as `object`, casting to `Interf`... new references are being created and discarded all over the place. The original `SomeStruct1` that you pass to `InitializeOffsets` is getting copied; the original is unaffected by all this. Use classes instead. – madreflection Nov 16 '21 at 16:50
  • @nvoigt: I agree, there must be a better solution. Okay, i'll add a description why i do this thing. – Albert -Al- Hollmann Nov 17 '21 at 18:49
  • @madreflection: you are right. I have trouble avoiding copies. Unfortunately, i need structs. – Albert -Al- Hollmann Nov 17 '21 at 18:51

1 Answers1

0

Meanwhile i found out, what i did wrong:

  1. i have to do boxing, so i can use "ref" when i call my initialize function:
// instead of this:
SomeStruct1 = new sSomeStruct1 (true);

// i have to do it this way:
object boxed_SomeStruct1 = new sSomeStruct1 (true);
InitializeOffsets (ref boxed_SomeStruct1);
SomeStruct1 = (sSomeStruct1) boxed_SomeStruct1;

  1. Within the "InitializeOffsets" function, "field_info.GetValue (obj)" delivers a copy of my member object. That's why i have to copy the modified copy back at the very end of the foreach loop:
field_info.SetValue (obj, node_obj);

After these changes, the code works as intended. Thanks for your interest. -Albert