Arrays in structures

P

Peter Seaman

I understand that structures are value types and arrays and classes are
reference types. But what about arrays as members of structures i.e. as in
C

struct x
{
int n;
int a[100];
}

How is such a structure (e.g. declared in C++ as a field in a class)
accessed in C#?

Peter Seaman
 
J

Jon Skeet [C# MVP]

I understand that structures are value types and arrays and classes are
reference types. But what about arrays as members of structures i.e. as in
C

struct x
{
int n;
int a[100];
}

How is such a structure (e.g. declared in C++ as a field in a class)
accessed in C#?

You can't declare it like that in C# - you'd have to declare it as just

int[] a;

and initialise it in a constructor or similar. The struct itself is
still a value type, but the variable a would contain a reference. It's
exactly the same as if you have a struct containing a string reference
or similar.
 
G

Gawelek

and initialise it in a constructor or similar. The struct itself is
still a value type, but the variable a would contain a reference. It's
exactly the same as if you have a struct containing a string reference
or similar.

Value types live only on stack, but what with struct containing String
variable?
What will happen with this String when for example we leave function ?


Gawel
 
P

Peter Seaman

Thanks, so structures containing directly embedded arrays are not possible
in C#. Such structures are possible in other languages such as C++ and I
often use them. What I am still uncertain about is this: if in C++ such a
structure is made a member of a class, can that member be accessed in C# and
if so how?

Peter Seaman
 
J

Jon Skeet [C# MVP]

Thanks, so structures containing directly embedded arrays are not possible
in C#.

Not as far as I know, no.
Such structures are possible in other languages such as C++ and I
often use them. What I am still uncertain about is this: if in C++ such a
structure is made a member of a class, can that member be accessed in C# and
if so how?

Well, the first thing to do is test whether it is still possible in
*managed* C++ - and then run ildasm on the created assembly and see
what's in it.
 
P

Peter Seaman

Well, the first thing to do is test whether it is still possible in
*managed* C++ - and then run ildasm on the created assembly and see
what's in it.

No doubt this is good advice but unfortunately I only have C# Standard.
Though this product does contain Visual Studio 2003 and compilers for
JScript .NET and VB .NET, it does not allow me to compile C++ .NET as far as
I can see.

It seems to me that arrays and structures and combinations of such
aggregates are pretty fundamental to programming, so on the face of this
inability for structures to contain embedded arrays is a severe restriction.
Is there an easy way round it? How does C# cope with the many such
structures in the WIN32 API e.g. LPWIN32_FIND_DATA?

Peter Seaman
 
J

Jon Skeet [C# MVP]

No doubt this is good advice but unfortunately I only have C# Standard.
Though this product does contain Visual Studio 2003 and compilers for
JScript .NET and VB .NET, it does not allow me to compile C++ .NET as far as
I can see.

Ah. I'm afraid I don't know much managed C++ at all - perhaps another
poster with more experience of it could help us?
It seems to me that arrays and structures and combinations of such
aggregates are pretty fundamental to programming, so on the face of this
inability for structures to contain embedded arrays is a severe restriction.
Is there an easy way round it? How does C# cope with the many such
structures in the WIN32 API e.g. LPWIN32_FIND_DATA?

No idea, I'm afraid :(
 
W

Willy Denoyette [MVP]

Peter Seaman said:
No doubt this is good advice but unfortunately I only have C# Standard.
Though this product does contain Visual Studio 2003 and compilers for
JScript .NET and VB .NET, it does not allow me to compile C++ .NET as far
as
I can see.

It seems to me that arrays and structures and combinations of such
aggregates are pretty fundamental to programming, so on the face of this
inability for structures to contain embedded arrays is a severe
restriction.
Is there an easy way round it? How does C# cope with the many such
structures in the WIN32 API e.g. LPWIN32_FIND_DATA?

Peter Seaman

This is not such a problem in C#, following is a sample using FindNexttFile
and WIN32_FIND_DATA.

using System;
using System.Runtime.InteropServices;
namespace Willys
{

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WIN32_FIND_DATAW
{
public uint dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
// TCHAR array 260 (MAX_PATH) entries, 520 bytes in unicode
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=520)]
public string cFileName;
// TCHAR array 14 TCHAR's alternate filename 28 byes in unicode
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=28)]
public string cAlternateFileName;
}

[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
}
class Tester
{
[DllImport("kernel32.dll", EntryPoint="FindFirstFileW", SetLastError=true,
CharSet = CharSet.Unicode)]
public static extern IntPtr FindFirstFileW( string lpFileName, ref
_WIN32_FIND_DATAW lpFindFileData);
static void Main()
{
string file = "*.exe";
_WIN32_FIND_DATAW fndData = new _WIN32_FIND_DATAW();
IntPtr hFile = FindFirstFileW(file, ref fndData);
if(hFile.ToInt32() == -1)
Console.WriteLine("FindFirst: " + Marshal.GetLastWin32Error());
else
Console.WriteLine("Filename: " + fndData.cFileName_260);

}
}
}

Willy.
 
P

Peter Seaman

Thanks for that. Does this technique extend to arrays of other than
bytes/chars? e.g. here is an example from winnt.h of an embedded array of
structures.

typedef struct _LUID_AND_ATTRIBUTES {
LUID Luid;
DWORD Attributes;
} LUID_AND_ATTRIBUTES, * PLUID_AND_ATTRIBUTES;

typedef struct _PRIVILEGE_SET {
DWORD PrivilegeCount;
DWORD Control;
LUID_AND_ATTRIBUTES Privilege[ANYSIZE_ARRAY];
} PRIVILEGE_SET, * PPRIVILEGE_SET;


Peter Seaman
 
W

Willy Denoyette [MVP]

Peter Seaman said:
Thanks for that. Does this technique extend to arrays of other than
bytes/chars? e.g. here is an example from winnt.h of an embedded array of
structures.

typedef struct _LUID_AND_ATTRIBUTES {
LUID Luid;
DWORD Attributes;
} LUID_AND_ATTRIBUTES, * PLUID_AND_ATTRIBUTES;

typedef struct _PRIVILEGE_SET {
DWORD PrivilegeCount;
DWORD Control;
LUID_AND_ATTRIBUTES Privilege[ANYSIZE_ARRAY];
} PRIVILEGE_SET, * PPRIVILEGE_SET;


Peter Seaman

Sure, here's how to ....

[StructLayout(LayoutKind.Sequential)]
public struct LUID {
public int LowPart;
public int HighPart;
}

[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES {
public LUID Luid;
public int Attributes;
}


[StructLayout(LayoutKind.Sequential)]
public struct _PRIVILEGE_SET {
public int PrivilegeCount;
public int Control;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=1)] // ANYSIZE_ARRAY = 1
public LUID_AND_ATTRIBUTES [] Privileges;
}

However if you consider doing this from C# you might as well just use
Managed C++.

Willy.
 
P

Peter Seaman

Thanks for this which does answer my question. Does the use of these
unmanaged types imply extra overhead (or less overhead) or unsafe code?

Peter Seaman
 
P

Peter Seaman

Sorry, I still don't understand this. When I declare as indicated and
execute

_PRIVILEGE_SET ps = new _PRIVILEGE_SET;

ps.Privileges[0] = 0;

I get

"An unhandled exception of type 'System.NullReferenceException' occurred
....Additional information: Object reference not set to an instance of an
object."

But if I say ps.Privileges = new LUID_AND_ATTRIBUTES[1]; I assume I get a
separate array outside the structure, is that correct?

Peter Seaman
 
W

Willy Denoyette [MVP]

Peter Seaman said:
Sorry, I still don't understand this. When I declare as indicated and
execute

_PRIVILEGE_SET ps = new _PRIVILEGE_SET;

ps.Privileges[0] = 0;

I get

"An unhandled exception of type 'System.NullReferenceException' occurred
...Additional information: Object reference not set to an instance of an
object."

But if I say ps.Privileges = new LUID_AND_ATTRIBUTES[1]; I assume I get a
separate array outside the structure, is that correct?

Peter Seaman
ps.Privileges denotes a structure array! you can't set an element (a stuct)
to 0, you can only set values to the members of the struct
ps.Privileges[0].Luid = ...
ps.Privileges[0].Attributes= ...

Willy.
 
P

Peter Seaman

Yes of course. I did actually say ps.Privileges[0].Attributes = 0; but
mis-typed the code above.

PRIVILEGE_SET ps = new _PRIVILEGE_SET;
ps.Privileges[0].Attributes = 0;

Gives the null reference exception.

I remain unclear on how these embedded arrays are supposed to work. Does
ps.Privileges = new LUID_AND_ATTRIBUTES[1] give an array inside the
structure on the stack or outside the structure on the heap?

Peter Seaman
 
W

Willy Denoyette [MVP]

Peter Seaman said:
Yes of course. I did actually say ps.Privileges[0].Attributes = 0; but
mis-typed the code above.

PRIVILEGE_SET ps = new _PRIVILEGE_SET;
ps.Privileges[0].Attributes = 0;

Peter,

Not exactly sure what you want to use the Privileges for, but following is a
small sample that illustrates how you check security privileges using
embedded structs in C# (whithout using usafe code).
But again I would never use C# for this, this is realy the domain of the
Managed Extensions for C++ .

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace Willys
{
[StructLayout(LayoutKind.Sequential)]
public struct _PRIVILEGE_SET
{
public uint PrivilegeCount;
public uint Control;
public LUID_AND_ATTRIBUTES Privilege_1; // Only one struct anyway, so no
array declaration
}

[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public uint HighPart;
}


public sealed class StructInspector
{
// dump serialized object or struct
public static void DisplayStruct(Object o)
{
const int bytesPerLinen = 16;
int totalBytes = Marshal.SizeOf(o);
Console.WriteLine("Size of object: " + totalBytes);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocCoTaskMem(totalBytes);
Marshal.StructureToPtr(o, ptr, false);
byte[] bytes = new byte[bytesPerLinen];
for (int i = 0; i < totalBytes; i += bytesPerLinen )
{
for (int j = 0;j < bytesPerLinen ; j++)
{
if (i + j < totalBytes)
{
bytes[j] = Marshal.ReadByte(ptr, i+j);
Console.Write("{0:x2} ", bytes[j]);

} else
{
Console.Write(" ");
}
}

Console.Write(" ");
for (int j = 0;j < bytesPerLinen && i + j < totalBytes ; j++ )
{
if (bytes[j] < 32)
Console.Write(".");
else
Console.Write(Convert.ToChar(bytes[j]));
}
Console.WriteLine("");
}
}
finally
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(ptr);
}
}
}
}
class Tester
{

[DllImport("advapi32.dll", EntryPoint="PrivilegeCheck", SetLastError=true)]
public static extern bool PrivilegeCheck( IntPtr ClientToken, ref
_PRIVILEGE_SET RequiredPrivileges, ref int pfResult);

[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public extern static bool LookupPrivilegeValue(string lpSystemName,
string lpName, ref LUID pLuid);

public const int PRIVILEGE_SET_ALL_NECESSARY = 1;
static void Main()
{
int result = 0;
IntPtr hToken;
// Setting up the PRIVILEGE_SET to contain "SeImpersonatePrivilege"
_PRIVILEGE_SET ps = new _PRIVILEGE_SET();
ps.PrivilegeCount = 1;
ps.Control = PRIVILEGE_SET_ALL_NECESSARY; // or 0 if any of
if (!LookupPrivilegeValue(null, "SeImpersonatePrivilege", ref
ps.Privilege_1.Luid))
{
Console.WriteLine("<LookupPrivilegeValue> Win32 Error {0}",
Marshal.GetLastWin32Error());

}
StructInspector.DisplayStruct(ps);

WindowsIdentity.Impersonate(WindowsIdentity.GetCurrent().Token);
hToken = WindowsIdentity.GetCurrent().Token;
Console.WriteLine("Token used: " + hToken);
if(!PrivilegeCheck(hToken, ref ps, ref result))
Console.WriteLine("<PrivilegeCheck> Win32 Error {0}",
Marshal.GetLastWin32Error());
Console.WriteLine("Privilege set '{0}'", Convert.ToBoolean(result));
StructInspector.DisplayStruct(ps);
}
}
}

Willy.
 
P

Peter Seaman

Sorry this is not helpful because you have now removed the array. My
reaction is Wow!!! I am afraid my enthusiasm for C# is somewhat diminished.

I only wanted to know how to use the simple structure in my original
message - a structure containing an integer followed by an array of
integers. This is a common construct in my world and I only quoted
"Privileges" as an example of a similar structure in the Win32 API.

Can anyone answer the simple question asked for the second time in my last
message: if you declare an embedded array (by using MarshalAs) how do you
reference the elements of the array and what happens when you "new" it?

Peter Seaman
 
W

Willy Denoyette [MVP]

Yes, I removed the array declaration because the size is 1 (ANYSIZE_ARRAY)
in the sample.
The problem with embedded arrays and variable length structures is realated
to the Interop marshaller, not C#.
The current version of the marshaller cannot handle them, to solve this you
have a number of options:
- Use Managed C++ which, as I told you, is THE interop language in .NET,
there are way too many opportunities to make mistakes while porting C/C++
header files to C# P/Invoke signatures.
- Pass managed pointers pointing to native memory (alloacted with
Marshal.AllocHGlobal or Marshal.AllocCoTaskMem) the API's and use
Marshal.StructureToPtr and Marshal.PtrToStructure to access the struct
fields before and after the call ( this is why I included StructInspector()
in the sample to get you started).
- Use unsafe code blocks and pointers.
- Flatten the structure and use arryays of blittable types to replace the
embedded struct array's, here's a sample.

note: non of the options (except MC++) solve the problem of marshalling
variable length structs.).

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace Willys
{

[StructLayout(LayoutKind.Sequential)]

public struct _PRIVILEGE_SET
{
public uint PrivilegeCount;
public uint Control;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] // 2 privilege
structs flatten to be 6 int's !!!!
public int[] Privilege_1;
}

[StructLayout(LayoutKind.Sequential)]

public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}

[StructLayout(LayoutKind.Sequential)]

public struct LUID
{
public uint LowPart;
public uint HighPart;
}

public sealed class StructInspector
{
.......// see previous posting

}
class Tester
{

[DllImport("advapi32.dll", EntryPoint="PrivilegeCheck", SetLastError=true)]
public static extern bool PrivilegeCheck( IntPtr ClientToken, ref
_PRIVILEGE_SET RequiredPrivileges, ref int pfResult);

[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public extern static bool LookupPrivilegeValue(string lpSystemName, string
lpName, ref int pLuid);

public const int PRIVILEGE_SET_ALL_NECESSARY = 1;
static void Main()
{
int result = 0;
IntPtr hToken;
uint privCount = 2;
// Setting up the PRIVILEGE_SET to contain "SeImpersonatePrivilege" and
"SeChangeNotifyPrivilege"
_PRIVILEGE_SET ps = new _PRIVILEGE_SET();
ps.PrivilegeCount = privCount;
ps.Privilege_1 = new int[privCount * 3]; // allocate 2 * 3 int's each
privilege takes 3 int's)
ps.Control = PRIVILEGE_SET_ALL_NECESSARY ; // or 0 if any of
if (!LookupPrivilegeValue(null, "SeImpersonatePrivilege",ref
ps.Privilege_1[0]))
Console.WriteLine("<LookupPrivilegeValue> Win32 Error {0}",
Marshal.GetLastWin32Error());
if (!LookupPrivilegeValue(null, "SeChangeNotifyPrivilege",ref
ps.Privilege_1[3]))
Console.WriteLine("<LookupPrivilegeValue> Win32 Error {0}",
Marshal.GetLastWin32Error());
StructInspector.DisplayStruct(ps);

WindowsIdentity.Impersonate(WindowsIdentity.GetCurrent().Token);
hToken = WindowsIdentity.GetCurrent().Token;
Console.WriteLine("Token used: " + hToken);
if(!PrivilegeCheck(hToken, ref ps, ref result))
Console.WriteLine("<PrivilegeCheck> Win32 Error {0}",
Marshal.GetLastWin32Error());
Console.WriteLine("Privilege set '{0}'", Convert.ToBoolean(result));
StructInspector.DisplayStruct(ps);
}
}
}

Willy.
 
P

Peter Seaman

The problem with embedded arrays and variable length structures is
realated
to the Interop marshaller, not C#. The current version of the marshaller
cannot handle them,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top