3

I've seen several related topics on SO and other forums, but haven't found a workable answer to my question.

Here's my code:

[StructLayout(LayoutKind.Explicit, Size = 128)]
internal unsafe struct Frame
{
    [FieldOffset(0)]
    public fixed byte Bytes[128];

    [FieldOffset(0)]
    public long Low;

    [FieldOffset(128 - sizeof(long))]
    public long High;
}

unsafe private void button32_Click(object sender, EventArgs e)
{
    Frame frame;

    // ERROR: Error 15  You cannot use the fixed statement to take the address of an already fixed expression
    fixed (byte* ptr = frame.Bytes)
    {

    }

    // ERROR
    Console.Write(System.Text.Encoding.ASCII.GetString(frame.Bytes, 0, 128));
    frame.Low = 1234;
    //Console.Write(System.Text.Encoding.ASCII.GetString(frame.Bytes));
    frame.High = 5678;
    //Console.Write(System.Text.Encoding.ASCII.GetString(frame.Bytes));
}
8
  • new string((sbyte*) frame.Bytes) should work if you are using ASCII only (or perhaps the overload taking start and length). Commented Jan 2, 2014 at 8:18
  • Nice and simple. However, any value is possible in the byte array. Commented Jan 2, 2014 at 8:21
  • What do you mean by any value? Using something else than ASCII? Commented Jan 2, 2014 at 8:22
  • I mean the byte array is binary. It can contain any value. I only want to view it as a string in order to inspect the fact of changes (debugging purposes). Commented Jan 2, 2014 at 8:25
  • 1
    The debugger display attribute would be a nicer option here, IMO. Commented Jan 2, 2014 at 8:27

4 Answers 4

3

Just wrap the struct into another class to eliminate the fixed pointer issue. This creates the struct inside the memory frame of the new class. Then convert the fixed array to a byte[] to eliminate the GetString() issue. Maybe this helps?

namespace test
{
        [StructLayout(LayoutKind.Explicit, Size = 128)]
        internal unsafe struct Frame
        {
            [FieldOffset(0)]
            public fixed byte Bytes[128];

            [FieldOffset(0)]
            public long Low;

            [FieldOffset(128 - sizeof(long))]
            public long High;                
        }

    internal class NewClass
    {
        public Frame FixedBytesArray;
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            unsafe
            {
                NewClass NewType = new NewClass();
                NewType.FixedBytesArray.High = 12345;
                NewType.FixedBytesArray.Low = 6789;


                fixed (byte* ptr = NewType.FixedBytesArray.Bytes)
                {
                    byte[] bytes = new byte[128];
                    int index = 0;
                    for (byte* counter = ptr; *counter != 0; counter++)
                    {
                        bytes[index++] = *counter;
                    }

                    Console.Write(System.Text.Encoding.ASCII.GetString(bytes, 0, 128));
                }
            }    
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

U mean to create a byte[]? Think this doesn't work because though an array is a reference type all its values are value type. And each value type has its own reference. So an array contains references to their values. But we just supply the first adress of the array and so the compiler doesn't know where to find the values.
I hope you find it useful. It's a really powerful, efficient way of doing what is often impossible, such as casting floats to int, etc.
1
static byte[] GetBytes(string str)
{
    byte[] bytes = new byte[str.Length * sizeof(char)];
    System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
    return bytes;
}

static string GetString(byte[] bytes)
{
    char[] chars = new char[bytes.Length / sizeof(char)];
    System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
    return new string(chars);
}


static byte[] StringToByteArray(string str, int length) 
{
    return Encoding.ASCII.GetBytes(str.PadRight(length, ' '));
}  

1 Comment

Thanks. Console.Write(GetString(frame.Bytes)); doesn't work as I think it's expecting a pointer-compliant method.
1

Nothing of the above works for me, so I created another solution:

StringBuilder sb = new StringBuilder();
fixed (byte* b = fixedByteArray)
{
  for (int i = 0; i < knownLengthOfByteArray; i++)
  {
    sb.Append((char)b[i]);
  }
}
return sb.ToString();

Works for ANSI strings.

Comments

0

The compiler error states it very well: you were trying to fix something that already has been fixed...

You only fix the reference when assigning a pointer. This prevents the reference to be moved to somewhere else in memory (garbage collection) then expected - using MS words, the reference becomes 'pinned'.

In your case, you don't need to fix your struct when defining but when referencing your pointer fix the 'source' by applying the "fixed" statement (this part of your code is correct).

Solution should be to remove the fix statements from your struct. If you like to read some about it: MSDN - fixed Statement (C# Reference)

6 Comments

The fixed statement has subtle advantages, such as automatically creating the structure without my needing to do so explicitly, which overcomes other issues such as the initialization of structs.
And why do you use the same fieldoffset for two different attributes?
I get the double-fixing, but not sure how to resolve it. The byte[] is represented as an array of longs, hence the "duplicate address".
Sorry, just trying to understand. From my point of view you assign no reference to the field but their explicit location in memory. You reserve a block of 128 Byte in memory for your struct. Now you explicitly organize this area by explicitly tellin the compiler where in this memoryblock to create the field. Are you sure you are not overwriting your byte array?
Array starts at pos 0. Another field is created at the same place. So there is nothing beeing overridden? My guess is that you override the byte array (which was fixed in size!) with a Long field which memory area is still fixed but now in location.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.