Hallo Ihr 🙂,
nach langer Zeit hab ich mal wieder eine Frage an euch 😛.
Ich bin gerade dabei ein c++ libary nach c# zu portieren.
Diese lib ist "nur" ein Addon für ein grösseres Project, welches auch in c++ geschrieben is. Wie auch immer, dafür gibts einen Wrapper (für das grosse Project (aka Mogre)).
Lange rede kurzer Sinn:
Die Methode die Ich aufrufen möchte, hat folgende Parameter:
void writeData(uint offset, uint length, void* pSource,
bool discardWholeBuffer) ;
In meinem fall ist pSource ein uint[] Array.
Ich hab jetzt schon einige Zeit gegoogled, hier im Forum gesucht, aber irgendwie komme ich partou nicht drauf wie ich mein Array nach void* casten kann. Ich hab Marshal.bla versucht.
Ich hab auch Varianten alla:
void Foo([MarshalAs(UnmanagedType.LPArray)] uint[] Arr);
versucht. Leider vergeblich 😦.
Ich wär euch sehr verbunden wenn Ihr mir hier weiterhelfen könntet 🙂
Gruß Kalleberlin
So zB:
void* voiPtr;
uint[] intArray = new uint[5] {1, 2, 3, 4, 5};
fixed (uint* intPtr = intArray)
{
voiPtr = intPtr;
}
Zum zurückkonvertieren musst du dann natürlich die Länge des Arrays wissen!
EDIT: Natürlich kannst du jetzt nicht einfach hergehen und den void-Zeiger übergeben, weil der Garbage Collector das Array nach verlassen des fixed-Blocks wieder im Speicher hin und her schieben wird.
Wenn du das Objekt dauerhaft Pinnen willst, schau dir die GCHandle Klasse an!
Hallo kyoko12,
vielen Dank für diesen Hinweis 🙂.
Gruß Kalleberlin.
Wäre dann eine dauerhafte Nutzung des Pointers (pinnen) nicht auch unter Verwendung von stackalloc gewährleistet? Also in folgender Form:
void* voiPtr;
uint* intPtr = stackalloc uint[5];
intPtr[0] = 1;
intPtr[1] = 2;
intPtr[2] = 3;
intPtr[3] = 4;
intPtr[4] = 5;
voiPtr = intPtr;
"Indem Sie über dieses ernste Thema lachen disqualifizieren Sie sich selbst."
mrleeh.de
@MrLeeh:
Ja, wäre es natürlich. Aber Kalleberlin sagte ja expizit, er habe ein uint-Array. stackalloc erzeugt kein Array in dem Sinn, sondern nur eine Reihe von Elementen auf dem Stack.
Der Vorteil von dem pinning mittels GCHandle sollte zudem sein, dass man kein unsafe verwenden muss.
(der so ermittelbare IntPtr sollte automatisch in void* marshallbar sein)
beste Grüße
zommi
@zommi:
Wie meinst du das?
Folgender Code kompiliert bei mir nicht:
class Program
{
static void Main(string[] args)
{
GCHandle objHandle;
uint[] intArray = new uint[5] { 1, 2, 3, 4, 5 };
objHandle = GCHandle.Alloc(intArray, GCHandleType.Pinned);
Test.TestMethod(intArray.Length,
objHandle.AddrOfPinnedObject().ToPointer());
}
}
unsafe class Test
{
public static unsafe void TestMethod(int intLength, void* voiPtr)
{
for (int i = 0; i < intLength; i++)
{
Console.WriteLine(*(((uint*)voiPtr) + i));
}
}
}
Ich bekomme:
Zeiger und Puffer fester Größe können nur in einem unsicheren Kontext verwendet werden.
Und das leuchtet mir auch ein: Ich kann ja keinen void* übergeben, wenn ein void* im aktuellen Kontext gar nicht zulässig ist!
Oder habe ich dich falsch verstanden?
Hallo kyoko12,
ok, dann hab ich dich wohl falsch verstanden.
Wenn diese Funktion "writeData" selbst in C# implementiert wird, dann hat sie ja als Parameter ein void* und demzufolge musst du eh im Unsafe-Context arbeiten.
Also bietet GCHandle selbst dort keinen sooo großen nutzen.
(Sobald du den Pointer nicht mehr brauchst, solltest du im Übrigen auch die fixierung aufheben mittels GCHandle.Free)
Denn so musst du die Umwandlung ...ToPointer() ja auch in nen unsafe-Block einbetten.
Ich dachte zuerst, deine writeData funktion wäre in ner externen DLL,
demzufolge dachte ich eher, folgendes geht:
[DllImport(...)]
static extern void writeData(uint offset, uint length, IntPtr pSource, bool discardWholeBuffer);
...
writeData(0, 10, objHandle.AddrOfPinnedObject(), false);
best Grüße
zommi
Was bewirkt denn discardWholeBuffer?
Ansonsten kannst du void* ganz simpel durch byte[] abbilden. Es braucht weder unsafe noch GCHandle-Kram. Letzteres nur dann, wenn die Funktion den Buffer asynchron schreibt, also auch nach Rückkehr noch benutzt.