Arrays in structures

P

Peter Seaman

The problem with embedded arrays and variable length structures
is related to the Interop marshaller, not C#.

What about my original example: a fixed length structure containing a fixed
 
P

Peter Seaman

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

What about my example of a FIXED length structure containing a FIXED
length array?

Peter Seaman
 
J

Jeffrey Bouley

Peter,
I am embedding an arraylist within a struct in C# that is then is wrapped
for a COM-base client... My code looks like this:

#region Build structs

//Build structs for processing feature classes.

//This enables method overloading.


public struct stmatch

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

}


public struct stmatchwhere

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

public string WhereClause;

}


public struct stmatchsubtype

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

}


public struct stmatchall

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

public string WhereClause;

}


public struct stnomatch

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string outSubFields;

public string inSubFields;

}


public struct stnomatchsubtype

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string outSubFields;

public string inSubFields;

public string sSubType;

}


public struct stnomatchwhere

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string outSubFields;

public string inSubFields;

public string WhereClause;

}


public struct stnomatchall

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string outSubFields;

public string inSubFields;

public string sSubType;

public string WhereClause;

}

#endregion



//Now add arraylists to each struct...

// Setup list of feature classes (tables) with associated array of arcfm
tables

SortedList slFeatureClasses = new SortedList();

ArrayList alArcFMTables = new ArrayList();


//Add arraylist of arcfm tables to sorted list of sde tables

alArcFMTables.Add("gis_elec.abandonedelectriclinesegment");

alArcFMTables.Add("gis_elec.conduitsystem");

alArcFMTables.Add("gis_elec.priohelectriclinesegment");

alArcFMTables.Add("gis_elec.priugelectriclinesegment");

alArcFMTables.Add("gis_elec.secohelectriclinesegment");

alArcFMTables.Add("gis_elec.secugelectriclinesegment");

slFeatureClasses.Add(conElCableLine, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.anchorguy");

alArcFMTables.Add("gis_elec.pushbrace");

slFeatureClasses.Add(conUtGuyPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.conduitsystem");

slFeatureClasses.Add(conElDuctBankLine, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.electricstation");

slFeatureClasses.Add(conElSubStationSite, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.generator");

slFeatureClasses.Add(conElGenPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.primarymeter");

slFeatureClasses.Add(conElMeterPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.riser");

slFeatureClasses.Add(conElRiserPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.switch");

alArcFMTables.Add("gis_elec.switchingfacility");

slFeatureClasses.Add(conElSwitchPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.transformer");

slFeatureClasses.Add(conElTransBnkPnt, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.undergroundstructure");

slFeatureClasses.Add(conElJuncPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.streetlight");

slFeatureClasses.Add(conExLightPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.supportstructure");

slFeatureClasses.Add(conUtPoleTowerPoint, alArcFMTables);

This should clear it all up for you :cool:... enjoy.
 
J

Jeffrey Bouley

Peter,
I am embedding an arraylist within a struct in C# that is then is wrapped
for a COM-base client... My code looks like this:

#region Build structs

//Build structs for processing feature classes.

//This enables method overloading.


public struct stmatch

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

}


public struct stmatchwhere

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

public string WhereClause;

}


public struct stmatchsubtype

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

}


public struct stmatchall

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

public string WhereClause;

}


public struct stnomatch

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string outSubFields;

public string inSubFields;

}


public struct stnomatchsubtype

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string outSubFields;

public string inSubFields;

public string sSubType;

}


public struct stnomatchwhere

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string outSubFields;

public string inSubFields;

public string WhereClause;

}


public struct stnomatchall

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string outSubFields;

public string inSubFields;

public string sSubType;

public string WhereClause;

}

#endregion



//Now add arraylists to each struct...

// Setup list of feature classes (tables) with associated array of arcfm
tables

SortedList slFeatureClasses = new SortedList();

ArrayList alArcFMTables = new ArrayList();


//Add arraylist of arcfm tables to sorted list of sde tables

alArcFMTables.Add("gis_elec.abandonedelectriclinesegment");

alArcFMTables.Add("gis_elec.conduitsystem");

alArcFMTables.Add("gis_elec.priohelectriclinesegment");

alArcFMTables.Add("gis_elec.priugelectriclinesegment");

alArcFMTables.Add("gis_elec.secohelectriclinesegment");

alArcFMTables.Add("gis_elec.secugelectriclinesegment");

slFeatureClasses.Add(conElCableLine, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.anchorguy");

alArcFMTables.Add("gis_elec.pushbrace");

slFeatureClasses.Add(conUtGuyPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.conduitsystem");

slFeatureClasses.Add(conElDuctBankLine, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.electricstation");

slFeatureClasses.Add(conElSubStationSite, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.generator");

slFeatureClasses.Add(conElGenPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.primarymeter");

slFeatureClasses.Add(conElMeterPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.riser");

slFeatureClasses.Add(conElRiserPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.switch");

alArcFMTables.Add("gis_elec.switchingfacility");

slFeatureClasses.Add(conElSwitchPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.transformer");

slFeatureClasses.Add(conElTransBnkPnt, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.undergroundstructure");

slFeatureClasses.Add(conElJuncPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.streetlight");

slFeatureClasses.Add(conExLightPoint, alArcFMTables);


alArcFMTables = new ArrayList();

alArcFMTables.Add("gis_elec.supportstructure");

slFeatureClasses.Add(conUtPoleTowerPoint, alArcFMTables);

This should clear it all up for you :cool:... enjoy.
 
J

Jeffrey Bouley

My apologies, copied wrong code...

I set up structs for overloading, then embed complex objects i.e.
sortedlist/arraylist, yikes. Here's a small snippet.

//Build structs for processing feature classes.

//This enables method overloading.


public struct stmatch

{

[MarshalAs(UnmanagedType.Interface)]

public IWorkspace pWorkspace;

public string OutTable;

public string InTable;

public string SubFields;

public string sSubType;

}

const string conElCableLine = "electrical_cable_line";

// Setup list of feature classes (tables) with associated array of arcfm
tables

SortedList slFeatureClasses = new SortedList();

ArrayList alArcFMTables = new ArrayList();

//Add arraylist of arcfm tables to sorted list of sde tables

alArcFMTables.Add("gis_elec.abandonedelectriclinesegment");

alArcFMTables.Add("gis_elec.conduitsystem");

alArcFMTables.Add("gis_elec.priohelectriclinesegment");

alArcFMTables.Add("gis_elec.priugelectriclinesegment");

alArcFMTables.Add("gis_elec.secohelectriclinesegment");

alArcFMTables.Add("gis_elec.secugelectriclinesegment");

slFeatureClasses.Add(conElCableLine, alArcFMTables);



//-----Loop over each SDE feature class.

for ( int i = 0; i < slFeatureClasses.Count; i++)

{

ArrayList myAL = new ArrayList();

//-----Set an arraylist for the current feature class (key)

myAL = (ArrayList) slFeatureClasses.GetByIndex(i);

//-----Declare an arraylist for in and out fields.

ArrayList alInFields = new ArrayList();

ArrayList alOutFields = new ArrayList();

for ( int n = 0; n < myAL.Count; n++)

{

pPro.Message = "Loading " + myAL[n].ToString() + " to " + user +"." +
slFeatureClasses.GetKey(i).ToString() + ".";

switch(slFeatureClasses.GetKey(i).ToString())

{

case conElCableLine:

switch(myAL[n].ToString())

{

case "gis_elec.abandonedelectriclinesegment":



//THIS IS WHERE I USE THE STRUCT-------------------------------

istmatchall = new stmatchall();

istmatchall.InTable = myAL[n].ToString();

istmatchall.OutTable = user +"." + slFeatureClasses.GetKey(i).ToString();

istmatchall.pWorkspace = pWorkspace;

istmatchall.sSubType = "ABANDONED";

istmatchall.SubFields = "shape,instln_id,user_flag";

istmatchall.WhereClause = "WHERE inconduitindicator = 'no'";

//CALL MY OVERLOADED FUNCTION HERE(pass in struct)--------------------

ObjectLoader(istmatchall);

break;

}

}

}

}




Regards,

Jeff

Willy Denoyette said:
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.


Peter Seaman said:
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
 
P

Peter Seaman

Thanks, but where is the structure with a directly embedded array?
ArrayLists are completely different things.

I am only asking for something simple, how to use in C# a fixed extent
single dimension array directly embedded in a simple structure. Such
constructs are widely used and have been available in many other languages
for donkeys years. Everybody seems to be mis-interpreting my question
and/or making things exceedingly complicated!

Let me repeat the question. Consider the C structure

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

Can such a structure be declared in C#, and used for example as a field in a
class, or read from a file? If so, what has to be coded in order to access
the array elements as a[0] a[1]..... a[9]. If it is not possible without
horrendous complications and/or unsafe code please can someone say so.

Peter Seaman
 
J

Jeffrey Bouley

using System;

using System.Runtime.InteropServices;

using System.Diagnostics;

namespace MyNameSpace

{

/// <summary>

/// Summary description for Class2.

/// </summary>

public class myClass

{


public static void Main(string[] Args)

{

myClass o = new myClass();

}


public struct myStruct

{

public string[] sArray;

public myStruct(int i)

{

sArray = new string;

}

}

public myClass()

{

//

// TODO: Add constructor logic here

//

myStruct useStruct = new myStruct(10);

for ( int i = 0; i < 10; i++)

{

useStruct.sArray = System.Convert.ToString(i);

Debug.WriteLine(useStruct.sArray.ToString());

}

}

}

}
 
W

Willy Denoyette [MVP]

Peter Seaman said:
What about my example of a FIXED length structure containing a FIXED
length array?

Peter Seaman

The marshaller cannot handle "embedded arrays of structures" and "variable
length structs". It can only handle embedded arrays of blittable types(int,
char etc) - as I illustrated in a previous post.
If you really want to use C# to pass such managed structures to unmanaged
code, you'll need to help the marshaller by passing as argument to the API,
a pointer to a buffer, that contains a copy of the managed struct fields you
marshalled using the Marshal.WriteXX methods. On return you need to copy the
buffer contents back to the managed structure using the Marshal.ReadXX
methods. But again why would you do this in C#.

Following illustrates this technique, using two privileges.

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 = 2)]
public LUID_AND_ATTRIBUTES[] 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
{
// dump serialized object or struct
public static void DisplayStruct(_PRIVILEGE_SET ps)
{
Console.WriteLine("Privileges: {0}",ps.PrivilegeCount);
Console.WriteLine("Control : {0}",ps.Control);
for(int i = 0; i < ps.PrivilegeCount; i++)
{
Console.WriteLine("----------------");
Console.WriteLine("LUID.Low {0:x2}
",ps.Privilege_1.Luid.LowPart);
Console.WriteLine("LUID.High {0:x2}
",ps.Privilege_1.Luid.HighPart);
Console.WriteLine("Attributes {0:x2} ",ps.Privilege_1.Attributes);

}
}

}
class Tester
{

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

[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public extern static bool LookupPrivilegeValue(string lpSystemName, string
lpName, IntPtr 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 LUID_AND_ATTRIBUTES[2];
ps.Control = PRIVILEGE_SET_ALL_NECESSARY; // or 0 if any of
int privSize = Marshal.SizeOf(ps.Privilege_1[0]);
IntPtr ptr = IntPtr.Zero;
ptr = Marshal.AllocCoTaskMem(privSize * (int)privCount);

Marshal.StructureToPtr(ps.Privilege_1[0], ptr, false);
if (!LookupPrivilegeValue(null, "SeImpersonatePrivilege",ptr))
Console.WriteLine("<LookupPrivilegeValue> Win32 Error {0}",
Marshal.GetLastWin32Error());
ps.Privilege_1[0] =
(LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(ptr,typeof(LUID_AND_ATTRIBUTES));

Marshal.StructureToPtr(ps.Privilege_1[1], (IntPtr)((int)ptr + privSize),
false);
if (!LookupPrivilegeValue(null,
"SeChangeNotifyPrivilege",(IntPtr)((int)ptr + privSize)))
Console.WriteLine("<LookupPrivilegeValue> Win32 Error {0}",
Marshal.GetLastWin32Error());
ps.Privilege_1[1] =
(LUID_AND_ATTRIBUTES)Marshal.PtrToStructure((IntPtr)((int)ptr +
privSize),typeof(LUID_AND_ATTRIBUTES));
Marshal.FreeCoTaskMem(ptr); // Free alocated mem

IntPtr ptrPs = IntPtr.Zero;
ptrPs = Marshal.AllocHGlobal(64); // 64 byte buffer
Marshal.WriteInt32(ptrPs,(int)ps.PrivilegeCount);
Marshal.WriteInt32((IntPtr)((int)ptrPs + 4),(int)ps.Control);
Marshal.StructureToPtr(ps.Privilege_1[0],(IntPtr)((int) ptrPs + 8),
false);
Marshal.StructureToPtr(ps.Privilege_1[1],(IntPtr)((int) ptrPs + 20),
false);

WindowsIdentity.Impersonate(WindowsIdentity.GetCurrent().Token);
hToken = WindowsIdentity.GetCurrent().Token;
Console.WriteLine("Token used: " + hToken);
if(!PrivilegeCheck(hToken, ptrPs, ref result))
Console.WriteLine("<PrivilegeCheck> Win32 Error {0}",
Marshal.GetLastWin32Error());
ps.PrivilegeCount = (uint)Marshal.ReadInt32(ptrPs);
ps.Control = (uint)Marshal.ReadInt32(ptrPs, 4);
ps.Privilege_1[0] =
(LUID_AND_ATTRIBUTES)Marshal.PtrToStructure((IntPtr)((int)ptrPs +
8),typeof(LUID_AND_ATTRIBUTES));
ps.Privilege_1[1] =
(LUID_AND_ATTRIBUTES)Marshal.PtrToStructure((IntPtr)((int)ptrPs +
20),typeof(LUID_AND_ATTRIBUTES));

Marshal.FreeHGlobal( ptrPs ); // Free allocated mem
Console.WriteLine("Privilege's set '{0}'", Convert.ToBoolean(result));
StructInspector.DisplayStruct(ps);
}
}
}

Willy.
 
P

Peter Seaman

I am afraid this is not an embedded array. The array data is outside the
structure on the heap and your example does not therefore achieve what is
required. (One way to verify this is to take a simple (i.e. shallow) copy
of useStruct.)

Peter Seaman
 
P

Peter Seaman

If you really want to use C# to pass such managed structures to
unmanaged code

But I did not say I wanted to do this! All I want is the ability to
declare and use a structure containing a directly embedded array, i.e. an
array whose data is part of the structure not outside it. There are various
consequences, for example, copying the structure would necessarily copy all
the array data as well. I only quoted the Win32 FIND_DATA and PRIVILEGE SET
examples as evidence for my claim that such structures are not unsual.

I conclude that this simple and common requirement is not met in C#, and I
contend that this a defect of the language. Of course you can program round
it in various clumsy ways, just as you can program round not using
structures at all.

Peter Seaman
 
W

Willy Denoyette [MVP]

Ok, I'm getting confused, do you need to pass your structures to unmanaged
code API's?
If your answer is YES, there are no other way's I showed you to do it for
now.

If you DON'T need to pass the structure to unmanaged code, you can declare
your structures with embedded structure array's, however, there is no
reason to do this, in .NET structures are value types and you should only
use them when for data items which have "value" semantics and are "small"
(System.Decimal is a good sample).

If you still want to use structures in C# ( instead of classes) with
embedded arrays of structs, and you don't want to pass these to unmanaged
code, look at (and run) the following sample:

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

[StructLayout(LayoutKind.Sequential)]

public struct _PRIVILEGE_SET
{
public uint PrivilegeCount;
public uint Control;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public LUID_AND_ATTRIBUTES[] 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
{
// dump serialized object or struct
public static void DisplayStruct(_PRIVILEGE_SET ps)
{
Console.WriteLine("Privileges: {0}",ps.PrivilegeCount);
Console.WriteLine("Control : {0}",ps.Control);
for(int i = 0; i < ps.PrivilegeCount; i++)
{
Console.WriteLine("----------------");
Console.WriteLine("LUID.Low {0:x2}
",ps.Privilege_1.Luid.LowPart);
Console.WriteLine("LUID.High {0:x2}
",ps.Privilege_1.Luid.HighPart);
Console.WriteLine("Attributes {0:x2} ",ps.Privilege_1.Attributes);

}
}

}
class Tester
{
static void Main()
{
_PRIVILEGE_SET ps = new _PRIVILEGE_SET();

ps.PrivilegeCount = 2;
ps.Privilege_1 = new LUID_AND_ATTRIBUTES[2];
ps.Control = 2;
ps.Privilege_1[0].Luid.LowPart = 11;
ps.Privilege_1[0].Luid.HighPart = 122;
ps.Privilege_1[1].Luid.LowPart = 33;
ps.Privilege_1[1].Luid.HighPart = 444;
StructInspector.DisplayStruct(ps);
}
}
}

Willy.
 
P

Peter Seaman

Sorry, but your example does not achieve the embedded arrays desired. Run
your code with 3 statements added as shown below and note that the
assignment ps1 = ps does NOT copy the arrrays. Therefore your arrays are
OUTSIDE the structure and indirectly addressed, not INSIDE the structure and
directly addressed as I require and as would be the case in C or C++.

I want to treat a1 a2 a3 in the following structure as an array. I want the
array to be INSIDE the structure just like a1 a2 and a3 are. Then I can
treat the structure as a self-contained unit.

struct

{

int n;

int a1;

int a2;

int a3;

}

At this point I give up and am happy for you to have the last word.

Peter Seaman

----- Your code with 3 statements added -------

_PRIVILEGE_SET ps = new _PRIVILEGE_SET();

_PRIVILEGE_SET ps1 = new _PRIVILEGE_SET(); // New statement 1. ps1
is a separate strcuture.

ps.PrivilegeCount = 2;

ps.Privilege_1 = new LUID_AND_ATTRIBUTES[2];

ps.Control = 2;

ps.Privilege_1[0].Luid.LowPart = 11;

ps.Privilege_1[0].Luid.HighPart = 122;

ps.Privilege_1[1].Luid.LowPart = 33;

ps.Privilege_1[1].Luid.HighPart = 444;

ps1 = ps; // New
statement 2

ps1.Privilege_1[0].Luid.LowPart = 7; // new statement 3


StructInspector.DisplayStruct(ps); // note that
ps.Privilege_1[0].Luid.LowPart is now 7 not 11.
 
W

Willy Denoyette [MVP]

Peter Seaman said:
Sorry, but your example does not achieve the embedded arrays desired. Run
your code with 3 statements added as shown below and note that the
assignment ps1 = ps does NOT copy the arrrays.

*** As expected, it doesn't, it copies the variable ps1 holding a reference
(or address of) to the _PRIVILEGE_SET structure type to the variable ps , so
both ps and ps1 are now pointing to the same structure (the ps struct).
Accessing the fields using ps or ps1 will yield the same result.
But what has this to do whith embedded arrays in structs?

Therefore your arrays are
OUTSIDE the structure and indirectly addressed, not INSIDE the structure
and
directly addressed as I require and as would be the case in C or C++.

*** You can't, simply because structures can be stack allocated and arrays
are allways GC heap allocated in .NET.
when declaring someStruct[] myStruct; in .NET you effectively say that
myStruct holds a reference (kind of opaque pointer type) to an array of type
someStruct allocated in the GC heap.
So the parent structure will contain a reference to a GC heap allocated
array, but you can threat the structure as a single entity and the array
access syntax used is the same as in C . The only difference is that the
array fields are on the heap, but why do you care where they are?
I want to treat a1 a2 a3 in the following structure as an array. I want
the
array to be INSIDE the structure just like a1 a2 and a3 are. Then I can
treat the structure as a self-contained unit.

struct

{

int n;

int a1;

int a2;

int a3;

}

*** Like this?

{
class Tester
{
static void Main()
{
A a = new A(3); // a is a reference to a struct of type A allocated on
the stack.
a.abc[0] = 1; // same syntax as C+C++ to access members
a.abc[1] = 2; // abc points to an array of ints on the heap, abc
is a struct field on the stack
a.abc[2] = 3;
Console.WriteLine("{0} {1} {2}",a.abc[0], a.abc[1], a.abc[2]);
}
}

struct A
{
internal int i;
internal int[] abc;
public A(int v) { // using a struct initializer
i = 0;
abc = new int[v]; // embedded reference abc, pointing to an array of
int's on the GC heap
}
}
}

Or ... without initializer....
static void Main()
{
A a; // struct a is allocated on the stack
a.abc = new int[3]; // abc is pointing to an array of ints on the
heap, abc is a reference fields on the stack
a.i = 123; // this value is on the stack
a.abc[0] = 1; // these values are on the heap.
a.abc[1] = 2;
a.abc[2] = 3;

}
}

struct A
{
internal int i; // struct (both fields) on the stack
internal int[] abc; // abc holds a reference to an array if int's
allocated on the heap
}
}

Here 'abc' defines an array of int's, but this could be an array of structs
as well. But again you should use classes, NEVER EVER use structs for this
UNLESS you have to interop with unmanaged code.
If you really can't live without C style structs, don't use C# or VB.NET and
stay with C/C++ or ME C++.
----------
At this point I give up and am happy for you to have the last word.
*** Thank you for the kind words.
Peter Seaman

----- Your code with 3 statements added -------

_PRIVILEGE_SET ps = new _PRIVILEGE_SET();

_PRIVILEGE_SET ps1 = new _PRIVILEGE_SET(); // New statement 1. ps1
is a separate strcuture.

ps.PrivilegeCount = 2;

ps.Privilege_1 = new LUID_AND_ATTRIBUTES[2];

ps.Control = 2;

ps.Privilege_1[0].Luid.LowPart = 11;

ps.Privilege_1[0].Luid.HighPart = 122;

ps.Privilege_1[1].Luid.LowPart = 33;

ps.Privilege_1[1].Luid.HighPart = 444;

ps1 = ps; // New
statement 2

ps1.Privilege_1[0].Luid.LowPart = 7; // new statement 3


StructInspector.DisplayStruct(ps); // note that
ps.Privilege_1[0].Luid.LowPart is now 7 not 11.

Quite normal, see above (or C# is not C/C++).


Willy.
 
W

Willy Denoyette [MVP]

Sorry, drop previous post, some more info and several errors corrected.

Willy.


*** As expected, it doesn't copy the arrays, it copies all structure members
from ps to ps1, also the member "ps.Privilege_1" holding a reference (see
below) to the LUID_AND_ATTRIBUTES structure array to the "ps1.Privilege_1"
structure member, so
both ps.Privilege_1 and ps1.Privilege_1 are now pointing to the same
structure array (the LUID_AND_ATTRIBUTES struct).
Accessing the fields of the array struct using ps or ps1 will yield the
same result.
If you need to copy a struct you need to implement ICloneable::Clone, here's
a sample...

static void Main()
{
A a;
// A a = new A(3); // a on the stack
a.abc = new int[3]; // so is abc, pointing to an array on the heap
a.i = 123; // on the stack
a.abc[0] = 11;
a.abc[1] = 2;
a.abc[2] = 3;
A b = (A)a.Clone();
b.abc[0] = 7;
Console.WriteLine("{0} {1} {2}",a.abc[0], a.abc[1], a.abc[2]);
Console.WriteLine("{0} {1} {2}",b.abc[0], b.abc[1], b.abc[2]);

}
}

struct A : ICloneable
{
internal int i;
internal int[] abc;
public object Clone()
{
A obj = (A)this.MemberwiseClone();
obj.abc = new int[abc.Length];
return obj;

}
}

But again you shouldn't use struct for this in .NET, use should use classes,
also note that copy constructors don't exist in .NET and operator= semantics
are different from C/C++.


Therefore your arrays are
OUTSIDE the structure and indirectly addressed, not INSIDE the structure
and
directly addressed as I require and as would be the case in C or C++.

*** You can't, simply because structures can be stack allocated and arrays
are allways GC heap allocated in .NET.
when declaring someStruct[] myStruct; in .NET you effectively say that
myStruct holds a reference (kind of opaque pointer type) to an array of type
someStruct allocated in the GC heap.
So the parent structure will contain a reference to a GC heap allocated
array, but you can threat the structure as a single entity and the array
access syntax used is the same as in C . The only difference is that the
array fields are on the heap, but why do you care where they are?
I want to treat a1 a2 a3 in the following structure as an array. I want
the
array to be INSIDE the structure just like a1 a2 and a3 are. Then I can
treat the structure as a self-contained unit.

struct

{

int n;

int a1;

int a2;

int a3;

}

*** Like this?

{
class Tester
{
static void Main()
{
A a = new A(3); // a is a struct of type A allocated on
the stack.
a.abc[0] = 1; // same syntax as C+C++ to access members
a.abc[1] = 2; // abc points to an array of ints on the heap, abc
is a struct field on the stack
a.abc[2] = 3;
Console.WriteLine("{0} {1} {2}",a.abc[0], a.abc[1], a.abc[2]);
}
}

struct A
{
internal int i;
internal int[] abc;
public A(int v) { // using a struct initializer
i = 0;
abc = new int[v]; // embedded reference abc, pointing to an array of
int's on the GC heap
}
}
}
.... or sample above....

Here 'abc' defines an array of int's, but this could be an array of structs
as well. But again you should use classes, NEVER EVER use structs for this
UNLESS you have to interop with unmanaged code.
If you really can't live without C style structs, don't use C# or VB.NET and
stay with C/C++ or ME C++.

----------
At this point I give up and am happy for you to have the last word.
*** Thank you for the kind words.
Peter Seaman

----- Your code with 3 statements added -------

_PRIVILEGE_SET ps = new _PRIVILEGE_SET();

_PRIVILEGE_SET ps1 = new _PRIVILEGE_SET(); // New statement 1. ps1
is a separate strcuture.

ps.PrivilegeCount = 2;

ps.Privilege_1 = new LUID_AND_ATTRIBUTES[2];

ps.Control = 2;

ps.Privilege_1[0].Luid.LowPart = 11;

ps.Privilege_1[0].Luid.HighPart = 122;

ps.Privilege_1[1].Luid.LowPart = 33;

ps.Privilege_1[1].Luid.HighPart = 444;

ps1 = ps; // New
statement 2

ps1.Privilege_1[0].Luid.LowPart = 7; // new statement 3


StructInspector.DisplayStruct(ps); // note that
ps.Privilege_1[0].Luid.LowPart is now 7 not 11.

Quite normal, see above (or C# is not C/C++).


Willy.
 

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