Laden...

Speicher in struct kopieren mit unsafe code - wie gehts ?

Erstellt von gelöschtem Konto vor 13 Jahren Letzter Beitrag vor 13 Jahren 4.114 Views
Gelöschter Account
vor 13 Jahren
Speicher in struct kopieren mit unsafe code - wie gehts ?

Hallo Community,

Ich versuche gerade etwas Code von VB6 zu C# zu portieren.


   Dim lngAttribPointer as Long
   Dim typAttr As TYPEATTR
   TInfo.GetTypeAttr lngAttribPointer 
   CopyMemory typAttr , ByVal lngAttribPointer , LenB(typAttr )

Die Funktion GetTypeAttr gibt mir die Adresse einer Struktur
zurück. Dann wird der Speicher der Struktur kopiert und meine
Struktur Variable gefüllt.


   IntPtr TypeAttributesPointer;
   TYPEATTR typAttr;
   TInfo.GetTypeAttr(out TypeAttributesPointer);
   // copy memory here to typAttr

Jetzt muss ich irgendwie den Speicher in das struct rüberkopieren, ich vermute
mittels unsafe Code aber ich steig nicht so recht durch wie. Ich habe unsafe Code
bisher immer vermieden. Kann mir vielleicht jemand auf die Sprünge helfen?

3.511 Beiträge seit 2005
vor 13 Jahren

Hallo,

ein Struct in den Speicher kopieren geht auch ohne "unsafe". Verwende dazu die Klasse Marshal mit der Methode StructureToPtr.

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

Gelöschter Account
vor 13 Jahren

Hey Khalid,

Ich muss den Thread leider nochmal aufwärmen.
Das mit StructureToPtr war der richtige Hinweis obwohl natürlich die umgekehrte Funktion für mich das richtige war.
Ich habe jetzt nur noch das Problem das es auch Arrays gibt und ich weiss nicht genau wie eine Lösung dafür aussehen könnte.
Der Code Schreiber holt hier die Adresse eines Structs per Win32 Api Funktion.
Das Struct is Element 0 in einem Array. Er geht dann im Speicher einfach um die Grösse eines Structs weiter zur nächsten Speicheradresse und kopiert den jeweiligen Struct Speicher zu sich rüber.
Bester C++ Style eigentlich.

Der VB Code sieht dafür in etwa so aus.



Dim lngPointer as Long
lngPointer = Win32GetStructIrgendwasFunction()

Dim typAttr as MyStructType ' in vb heissen structs natürlich types

CopyMemory typAttr , ByVal lngPointer , LenB(typAttr )
CopyMemory typAttr , ByVal lngPointer + LenB(typAttr) * 1,  LenB(typAttr )
CopyMemory typAttr , ByVal lngPointer + LenB(typAttr) * 2,  LenB(typAttr )
CopyMemory typAttr , ByVal lngPointer + LenB(typAttr) * 3,  LenB(typAttr )

Natürlich im echten Code in einer Schleife.
Ich habe versucht meinen Code umzubauen und einfach in ein Array
zu casten.


typAttr = (MyStructType[] )Marshal.PtrToStructure(lngPointer , typeof(MyStructType ));

aber das klappt natürlich nicht.

Sollte ich auch mal versuchen meinen InPtr neu zu berechnen oder gibts
eine alternative Funktion für solch einen Fall ?

1.361 Beiträge seit 2007
vor 13 Jahren

Hi,

dann arbeite doch auch mit einer Schleife:

public static void Copy<T>(IntPtr source, T[] destination, int startIndex, int length)
{
	int sizeOfStruct = Marshal.SizeOf(typeof(T));
	for(int i=startIndex; i<startIndex+length; i++)
	{
		IntPtr currentItemPtr = new IntPtr(source.ToInt64() + i * sizeOfStruct);
		destination[i] = (T)Marshal.PtrToStructure(currentItemPtr, typeof(T));
	}
}

Man müsste natürlich noch ein paar Checks einbauen. Ptr != Zero, array != null, grenzen vielleicht weglassen... etc.

beste Grüße
zommi

Gelöschter Account
vor 13 Jahren

hallo zoomi

also dann berechne ich die adresse entsprechend auch neu, okay.
ich wusste nicht ob das schick ist in einer managed umgebung
oder es da vielleicht ne andere technik gibt.
das er das vielleicht peilt und mir ne Security Exception serviert
hatte ich auch schon überlegt. wenns doch so einfach ist find ich das super 🙂
dankdir!

Gelöschter Account
vor 13 Jahren

Eine Fräge noch:

Hier müsste ich doch auf die Plattform prüfen.
Denn das source.ToInt64() ist doch nur auf 64Bit Systemen oder?

1.361 Beiträge seit 2007
vor 13 Jahren

Hi nochmal,

dooferweise gibt es keine Marshal.Copy(...)-Übleradung, die ein Array eines beliebigen Werttyps befüllt. Deshalb der Workaround.

Es gibt aber noch einen zweiten Workaround mittels Buffer.BlockCopy(...):

public static void Copy<T>(IntPtr source, T[] destination, int count)
{
	int sizeOfStruct = Marshal.SizeOf(typeof(T));
	int totalBytes = sizeOfStruct * count;
	//copy all bytes into managed Array
	byte[] managedBuffer = new byte[totalBytes];
	Marshal.Copy(source, managedBuffer, 0, totalBytes);
	//copy all bytes to destination Array
	Buffer.BlockCopy(managedBuffer, 0, destination, 0, totalBytes);
}

Das könnte sogar schneller sein, benötigt aber noch das zusätzliche Array als Zwischenspeicher. (Time vs. Place)

beste Grüße
zommi

//Nachtrag:
Ok, wie sich herausgestellt hat, funktioniert BlockCopy leider nicht mit Struct-Arrays, denn

Das Objekt muss ein einfaches Array sein.

Gelöschter Account
vor 13 Jahren

Da jammert er leider rum das destination ein einfaches Array sein muss.
Mir würde die langsamere Methode sogar reichen, ums nochmal aufzugreifen, ist die plattform unabhängig ? Ich mach mir wegen diesem ToInt64() etwas Sorgen wie gesagt.

1.361 Beiträge seit 2007
vor 13 Jahren

Hi nochmal,

Hier müsste ich doch auf die Plattform prüfen.
Denn das source.ToInt64() ist doch nur auf 64Bit Systemen oder?

Nein, das geht auf beidem. Ist gerade die Plattform-unabhängige Variante von beidem.

Zeiger-Arithmetik auf nem 32Bit System verlangt (mindestens) 32Bit-Pointer. Auf nem 64Bit-System mindestens 64Bit-Pointer.

Aber da ja 32-Bit Zahlen ohne Verlust auch als 64-Bit Zahlen darstellbar sind, ist man mit 64-Bit auf der sicheren Seite.

Insofern brauchst du keine Überprüfung einbauen, und in einem der Fälle auf ToInt32 und im anderen auf ToInt64 umschwenken.

Einzig die ToInt64-Variante ist auf einem nativen 32-Bit System unnötig langsam. (weil da 32-Bit Operationen natürlich schneller sind als 64-Bit Operationen - aber unmerklich) Allerdings würde die extra Überprüfung auch wieder Zeit fressen. Und auf jeden Fall fiele das unter "premature optimization"!

Also kurz: ToInt64 ist die plattformunabhängige Lösung.

beste Grüße
zommi

S
248 Beiträge seit 2008
vor 13 Jahren

Hi,

ab .Net 4 (glaube ich) verfügt der Typ IntPtr auch über den IntPtr +(IntPtr,int) Operator. Damit würde die Verwendung von .ToInt64() wegfallen.

spooky

Gelöschter Account
vor 13 Jahren

@ zommi
Eine Exception zu zitieren muss man auch erstmal schaffen. 8)
Also soweit läuft alles super bis auf eine einzige Sache.
Es geht hier darum Enum Konstanten aus COM TypeLibs auszulesen BTW,
konkret gesagt die Zahl zur Konstante.


Dim sVal as String
Dim vVal As Variant
Dim desc As VARDESC
desc = GetVarDesc()
CopyMemory vVal, ByVal desc.lpVarValue, 16
sVal = CStr(vVal)

Diese Code hier funktioniert aber nur solange vVal ein Variant
ist. Mache ich irgendwas anderes draus stürzt die komplette VB IDE ab.
Entweder direkt beim Copy oder in der Zeile danach beim umwandeln in einen
String. Der Variant wird nach dem CopyMemory als Variant/Long angezeigt.

Mein Versuch in C#


int res =(int)Marshal.PtrToStructure(varDesc.desc.lpvarValue, typeof(int));

res ist jetz immer 3 ich kanns auf int16 int32 ushort oder sonstwas ändern.
immer das gleiche. res = 3.

Ich vermute der VB Code funktioniert so garnicht aber der Lesemechanismus des
Varianttypen in VB korrigiert das irgendwie.

ich hab mal das hier probiert


 byte[] managedBuffer = new byte[16];
 Marshal.Copy(varDesc.desc.lpvarValue, managedBuffer, 0, 16); 

Der Debugger sagt:
managedBuffer {byte[16]}
[0] 3 byte
[1] 0 byte
[2] 0 byte
[3] 0 byte
[4] 108 byte
[5] 110 byte
[6] 252 byte
[7] 121 byte
[8] 248 byte
[9] 239 byte
[10] 255 byte
[11] 255 byte
[12] 0 byte
[13] 0 byte
[14] 0 byte
[15] 0 byte

Muss ich das verstehen ? 🙁