Fragensteller
Übergabe an Funktion

Allgemeine Diskussion
-
Hallo,
ich stehe mal wieder vor einer neuen Herausvorderung der ich momentan scheinbar noch nicht gewachsen genug bin, ich möchte gerne Daten in einer Funktion ausführen, jedoch bekomme ich leider die Daten nicht in dem Passenden Format zu der Funktion geliefert.
Hier mal die Headzeile von der Funktion:
static unsafe Reaction sub_5AB8A0(FactionTemplateEntry* faction1, FactionTemplateEntry* faction2)
Was ich bisher heraus gefunden habe ist, dass dieses * einen IntPtr interpretiert, FactionTemplateEntry ist in diesem Fall mein Struct mit verschiedenen Angaben.
Nun muss ich mit Hilfe meines DBC readers die passende dbc auslesen und dann den Eintrag an die Funktion weitergeben damit diese damit arbeiten kann.
Ich habe da nun schon endliche versuche und nahezu alle möglichen konstellationen ausprobiert, jedoch waren die wenigsten davon vom kompilierer gefressen worden und ausgeführt.
Hier mal der letzte Ansatz:
public unsafe static DBC<FactionTemplateEntry*> GetFactions(uint unit) { const int FactionTemplate = 0xBBA310; DBC<FactionTemplateEntry*> Factiontemp = new DBC<FactionTemplateEntry*>((IntPtr)Memory.BaseAddress + FactionTemplate); return Factiontemp; }
Ohne die "*" wäre es funktionstüchtig, jedoch dann wären die Daten nicht komform zu der Funktion an die ich sie übermitteln möchte, hoffe jemand weiß hier Rat.
Gruß Günther- Typ geändert Robert BreitenhoferModerator Montag, 10. Dezember 2012 09:29 Keine Rückmeldung des Fragenstellender
Alle Antworten
-
Hallo Günther,
da der gezeigte Code nur wenig hergibt - abseits davon das es in C# keine Pointer gibt und man unsafe Methoden nicht von außen zugänglich (public) machen würde:
Was willst Du mit der Geschichte erreichen?
Wer oder was ist DBC?
Und warum brauchst Du eine Speicheradresse (wenn ich Parameter richtig interpretiere)?
Gruß Elmar
P. S: Haben die Klassen mit TrinityCore zu tun, weil Google etwas dazu ausspuckt?- Bearbeitet Elmar BoyeEditor Samstag, 24. November 2012 09:15 P. S.
-
Hallo Elmar,
Die Pointer werden benötigt da ich den Speicher auslese, was eine DBC ist kannst du hier erfahren: http://www.pxr.dk/wowdev/wiki/index.php?title=DBC
Mit TrinityCore hat es relativ wenig zu tun, glaube das es auch in C++ Programmiert wurde und von dem her nicht für mich verwendbar wäre.
Wenn du noch weitere Informationen über Code brauchst schreib dies bitte.
- Bearbeitet Super_Günther Samstag, 24. November 2012 09:34
-
Hallo Günther,
naja WOW und Co. sind nicht so mein Ding. Aber anscheinend gibt es mehrere Ansätze, da das FactionTemplate auf Trinity Core verweist.
Zum richtigen Spielen / interessanteren Programmieren:
Damit C# mit C++ Strukturen austauschen kann, brauchst Du eine passende C# struct.
Die wiederum kann via Marshal.StructureToPtr-Methode an die C++ Methode übergeben werden. Liefert Dir eine C++ Methode einen Zeiger auf eine Struktur zurück, ginge der Weg umgekehrt über Marshal.PtrToStructure.Das wird umfassender beschrieben unter: Marshallen von Daten mit Plattformaufruf
(und bitte, bitte arbeite das sorgfältig durch, sonst scheppert es häufiger, als es Spaß macht ;)
Ein Beispiel wie das aussieht - ohne Bezug auf Deine Strukturen:
http://stackoverflow.com/questions/3939867/passing-a-structure-to-c-api-using-marshal-structuretoptr-in-c-sharpDa ich aus dem Wiki auf die Schnelle nicht schlau werde:
Wo kommt die Funktion sub_5AB8A0 her, die Du oben angeführt hast?Mit etwas mehr Infos zu der verwendeten Bibliothek könnte ich evtl. mehr Hilfestellung geben.
Gruß Elmar
-
Hallo Elmar,
danke für die Verweise auf den Struct, aber dieser besteht schon längst.
Hier mal ein Link zu dem kompletten Code soweit wie ich ihn habe: http://paste2.org/p/2012286
Also wo die sub herkommt ist eigl. total unrelevant, aber da du Interesse ran hast gebe ich dir gerne eine Antwort darauf, diese sub ist die Funktion in dem Spiel welche man mit hilfe eines Disassemblers wie IDA heraus finden kann, alles was man dann noch machen muss ist diese Funktion übersetzen in die gewünschte Programmiersprache.
-
Hallo Günther,
nein, das ist nicht irrelevant. Denn mit einer C++ Funktionsadresse alleine kommst Du nicht weiter.
Damit .NET damit etwas anfangen kann benötigst Du einen Einsprungpunkt, der durch ein DLLImportAttribute dekoriert werden muss. Dort muss unter anderem die DLL hinterlegt werden, in der die Funktion definiert ist. Auch andere Dinge wie die Aufrufkonvention können eine Rolle spielen.
Handelt es sich dabei um keinen exportierten Einsprungpunkt müssest Du erst eine Wrapper DLL (in C++) erstellen. Siehe u. a.:
Calling an unmanaged dll from .NET (C#)
Den gezeigten C++ Code würde ich in C# umschreiben, das geht schneller.
Und eine Struct mit fixed taugt nicht für den Managed Bereich - da bleibt noch was zu tun.
Gruß Elmar
- Bearbeitet Elmar BoyeEditor Samstag, 24. November 2012 12:27 fixed Hinweis
-
Also ich bezweifel sehr stark das der Urheber von dem Code bei Paste.bin eine wrapper class benutzt hat um den Code den er gepostet hat zu benutzen, ich benutze einige dieser Structs um gewisse Daten auszulesen, von daher verstehe ich nicht wieso ich nun eine wrapper class benutzen sollte, obwohl es auch ohne geht ;)
Oder liegt das alleine darin da die Funktion die angeforderten Daten im IntPtr format benötigt? (FactionTemplateEntry*)
- Bearbeitet Super_Günther Samstag, 24. November 2012 12:41
-
Hi Günter,
vielleicht ein wenig zum Hintergrund.
Bei verwalteten Code kümmert sich der Garbage Collector (GC) um die Verwaltung des Speichers. Dabei geht er auch und verschiebt Objekte/Struktur, damit der Speicher besser ausgenutzt wird.
Bei nicht verwalteten Code muss man sich selber um die Speicherverwaltung kümmern.
Wenn man nun einen Zeiger im nicht verwalteten Code hat, der auf ein Objekt/Struktur im Verwalteten Code hat, könnte es gesehen das der GC das Objekt verschiebt. Der Zeiger im nicht Verwalteten Code würde also auf eine falsche Speicherstelle zeigen. Was dann z.B. dazu führen könnte, dass man sich die Daten kaputt schreibt.
Mann muss also dafür sorgen, dass Objekt an der gleichen stelle bleibt (Pinnen) oder die Zeiger passend verschoben werden. Darum kümmert sich z.B. der Mashaller.
Da du nun immer zusätzlichen Code schreiben musst, um von Verwalteten Code auf nicht verwalteten Code zuzugreifen. Ist es sinnvoll einen Wrapper zu schreiben. Aus deinem Verwallteten Code greifst du nun immer auf den Wrapper zu und brauchst dich nicht überall darum zu kümmern, das korrekt auf den nicht verwalteten Speicher zugreifen.
Elma hat da aber mehr Ahnung als ich. Lies dir mal die Links durch.
MFG
Björn
-
Gut dann werde ich dies mal zu herzen nehmen und versuchen umzusetzen, leider werde ich aus dem Link von Elmar nicht so ganz schlau, was ich nun genau in die *.dll packen sollte und wie ich das dann korrekt aufrufe damit es dann letzt endlich zum Ziel führt.
Ich denke selbst danach haben wir die Daten nicht passend im FactionTemplateEntry* format, oder täusche ich mich dort?
Gruß Günther
-
Hallo Günther,
ich kann Dir keinen nun auch keinen Crash Kurs in .NET Interop verpassen.
Die von Dir gelieferten Informationen sind zu gutem Teil vage und mehrere Interpretationen möglich (vor allem für jemanden mich, der nicht nur .NET kennt).
Wenn der Code auf paste.bin nicht von Dir stammt: Dort hat dann jemand einen größeren Teil in unsafe C# nachprogrammiert. Etwas was man aber tunlichst unterlassen sollte - insbes. wenn man selbst noch nicht viel (oder gar nicht) in C++ programmiert hat.
Am besten erläuterst Du, wozu Du die Auswertung der Fraktionen brauchst und wo Du Deine Eingangsdaten - also in etwa wer zu welcher Fraktion gehört - gespeichert hast bzw. wegbekommst.
Denn der Code auf pastebin ist in 2 Minuten auf "sauberes" C# umgestellt, vorausgesetzt man hat die beidenFactionTemplateEntry Einträge als C# und nicht als C++ Struktur.
Was soweit die Bruchstücke reichen, wohl darauf hinausläuft:
Wo kommt Memory.BaseAddress her und worauf zeigt es.Gruß Elmar
-
Hallo Elmar,
ich möchte mit Hilfe dieser Funktion zwei verschiedene Einheiten mit einander vergleichen,ob diese mir gegenüber Neutral - Freundlich - Hasserfüllt sind.
Die Eingangsdaten erhalte ich in dem ich die Einheit in meiner Nähe mit Hilfe eines Offsets auslese und natürlich meine eigene, dabei kommt z.B. der Wert der Einheit = 12345 raus und bei mir 55555.
Als nächstes wird dann in der DBC, welche einer großen Datenbank gleicht, die Spalte heraus gesucht in der die ID (Eingangsdaten Nummer) vor kommt.
Sobald diese dann vorhanden ist liest man die weiteren Zeilen mit aus (die Variablen aus dem Struct).
Diese Daten übergibt man dann zu der Funktion, diese überprüft ob bestimmte gegebenheiten gegeben sind, und danach wird dann ausgewertet ob diese Einheit mit gegenüber die oben genannte Einstellung hat.
Die Memory.BaseAddress ist die BaseAddress von dem Prozess an diesen ich mich angeheftet habe (attached).
Ich hoffe dies macht es alles ein wenig klarer ;)
-
Hallo Günther,
was da passieren soll, ist seit langem klar und spielt hier nicht einmal eine Rolle. Und ich programmiere lange genug, um Code lesen zu können.
Was aber immer noch fehlt, sind brauchbare Informationen darüber wie Du auf die Daten zugreifst. Zwar kann ich mir denken, dass Du da eine Bibliothek verwendest. Das (veraltete) Wiki gibt nichts dazu her.
Ich habe mir mittlerweile einige WOW Bibliotheken angeschaut und komplex ist das (für mich) nicht - kurz gefasst: Man lese ein paar Bytes aus und quetsche sie in eine Struktur. Aber ohne die konkrete Implementation zu kennen - u. a. die von Dir verwendete DBC Klasse - gibt es reichlich Möglichkeiten, was falsch zu machen.
Und solange Du daraus ein Geheimnis machst, warum auch immer, kann man keine brauchbare Hilfestellung geben. Wenn Du dazu keine weiteren Infos liefern willst, so wirst Du Dich alleine durchbeißen müssen.
Gruß Elmar
-
Hallo Elmar,
kein Ahnung was da noch fehlen sollte, das einzigste was mir noch einfallen würde ist wie ich die Einheit Auslese und an die FactionId komme.
Dies mache ich so:
public uint Faction { get { try { //UNIT_FIELD_FACTIONTEMPLATE = 0x7 + 0x2B return GetStorageField<UInt32>((uint)Descriptors.eUnitFields.UNIT_FIELD_FACTIONTEMPLATE); } catch { return 0; } } } protected T GetStorageField<T>(uint field) where T : struct { field *= 4; var m_pStorage = Memory.Read<uint>(ObjectPointer + 0x8); return Memory.Read<T>(m_pStorage + field); }
Der ObjectPointer wäre in diesem Falle die Einheit von der ich die FactionId aus dem Speicher lesen möchte.
-
Hallo Günther,
leider erzählst Du mir immer Dinge die dafür nicht nötig sind.
Anhand des obigen Codes habe ich mal eine der wohl in vielen Varianten existierenden Speicherzugriffe auf WoW rausgesucht:
http://code.google.com/p/blackrainobjects/source/browse/#svn%2Ftrunk%2FBlackRain%2FCommon%2FObjects
Dort gibt es zwar keine Faction aber das Schema wäre äquivalent wie es dort für WoWContainer uam. durchgezogen wurde.
Erstelle Dir eine entsprechende Klasse und lese die Daten anhand der Offsets die Du im Wiki findest aus. Damit hättest Du eine managed Version für die Faction Daten.
Die bewusste Methode kannst Du dadurch integrieren indem Du den unsafe Code auf "normal" umstellst, was im wesentlichen das streichen von unsafe/fixed sowie von "*" bei FactionTemplateEntry und der Tausch von "->" in einen "." für einen Standard-Zugriff ist.
Das Ganze ohne jede Gewähr, da ich Deine Implementation des Zugriffs nur anhand des Memory.Read oben erraten habe - besser Du hättest dazu etwas gesagt...
Gruß Elmar
-
Hallo Elmar,
Das wäre bestimmt eine gute Idee so Fortzufahren, jedoch komme ich nicht drum herum, um an die Daten zu kommen die DBC Datenbank auszulesen, ich denke daran wird es scheitern da ich dort auch wieder die Rohdaten Typisieren muss, und genau dabei kamen bisher die Fehler, dass z.B. OurMask mit DBC<FactionTemplateEntry> usw. Typisiert ist und es somit dann auch schlecht vergleichen kann mit anderen Werten.
Aber das mit der BlackRain Library um an die gewünschten Daten zu kommen sowas in der Art benutze ich auch, jedoch etwas ausgereifter, da BlackRain nicht sehr performance gerecht arbeitet.
Edit:
Habe es nun so gemacht wie Elmar es vorgeschlagen hat, jedoch gibts hier ein Problem mit dem fixed int wert aus dieser Zeile:for (int i = 0; i < 4; ++i)
{
if (player.enemyFaction[i] == 0)
break;
if (player.enemyFaction[i] == unit.faction)
return Reaction.Hostile;
}Hier die Funktionion in der WoWUnit.cs :
public int enemyFaction
{
get
{
const int FactionTemplate = 0xBBA310;
DBC<FactionTemplateEntry> Factiontemp = new DBC<FactionTemplateEntry>((IntPtr)Memory.BaseAddress + FactionTemplate);
foreach (FactionTemplateEntry Factions in Factiontemp)
{
if (Factions.ID == Faction)
{
return Factions.enemyFaction;
}
}
}
}irgendwie muss ich dort ja angeben wieviel "bytes" er nun durchsuchen soll, jemand ne Idee?
- Bearbeitet Super_Günther Montag, 26. November 2012 04:58
-
Hallo,
was die Bibliothek angeht: Ich hatte da die kleinste "Codewüste" herausgesucht, die ich auf die Schnelle finden konnte. Es ging mir nicht darum, dass Du sie verwendest, sondern nur, dass Du die Gedankengänge leichter nachvollziehen kannst.
Was DBC angeht - da fehlen die länger angefragten Informationen.
Mit den richtigen Attributen versehen, sollte die generische Klasse die Anzahl der Bytes selbst bestimmen können. Hier wären die Arrays der problematische Teil, was mit SizeConst angeben kann, wenn es hier immer 4 Fraktionen sind.
Siehe Standardmäßiges Marshalling für Arrays.
Gruß Elmar
-
Hallo Elmar,
ich denke, dass ich mir mal ein Buch über C# für die Semesterferien besorgen sollte, so kommen wir hier jedenfalls nicht weiter, hier werden nur in meinen Augen unsinnige Code stellen verlangt wie zu der DBC, was zu der sache eigl. wenig beiträgt.
Am einfachsten wäre es ja gewesen wenn jemand mir einfach klipp und klar geschrieben hätte wie ich die Funktion und die Abfrage der Funktion zu schreiben habe und nicht auf einen MSDN Link zu verweisen der jemanden ohne große Grundkenntnisse noch mehr verwirrt als er so schon ist.
Gruß Günther
-
Hallo Günter,
ohne zusätzliche Kenntnisse, mit dehnen du nicht raus rückst, kann dir keiner einfach den Code schreiben.
Z.B. ist der Aufbau der Strucktur wichtig. Du befüllst ja die nur Struktur darüber, was in einem Speicherbereich steht. Mit nachfolgenden Strukturen könntest du beide mit den gleichen Aufruf "befüllen",
Resultat ist das Nachname und Vorname vertauscht sind.
Structure Person Public Vorname As String Public Nachname As String End Structure Structure Person Public Nachname As String Public Vorname As String End Structure
Wie die Strucktur aufgebaut ist, bzw welche Daten wie im Speicher stehen muss irgendwo offiziell Dokumentiert sein sein. Ohne diese Informationen kann man dir nicht Helfen.
Auch wie das befüllen der Struktur genau gemacht wird, weil sich hier auch ein Fehler eingeschlichen haben könnte. z.B. Verweis auf einen falschen Speicherbereich.
Wenn die Daten in der Struktur, korrekt sind. Sollte der Rest kein Problem sein.
MFG
Björn
-
Hallo Günther,
soweit ich mir das zusammenreimen konnte, kann das Problem mit guten Kenntnissen in ein paar Minuten gelöst werden.
Auch wenn Du es nicht sehen willst:
Zuerst: Wir kennen Dein Programm nicht!
Wie bereits angedeutet: WoW interessiert mich die Bohne (und ebenso, ob Du es hackst oder was auch immer Blizzard davon hält).
Das Problem liegt an Deiner "mangelhaften" Mitarbeit.
Dein erstes Posting war technisch gesehen nur "Unfug".
Der Zugriff auf native Umgebungen ist kein Anfängerzeugs und die Links, die ich genannt habe, können ein Grundverständnis, wie Programme (nativ und .NET) arbeiten, nicht ersetzen. (Und mit einem Buch alleine wirst Du das nicht erarbeiten).
Hättest Du zu irgendeinem Zeitpunkt auf meine Rückfragen angemessen reagiert und die von Dir verwendeten - wie ich vermute, anderswo zusammengesuchten - Klassen und Methoden gezeigt, so wäre das Problem in einer guten Viertelstunde gelöst (gewesen).
Von meiner Seite mache ich jetzt Schluss - denn ich habe viel Zeit - insgesamt deutlich mehr als eine Stunde - "verschwendet", um anhand Deiner Andeutungen via Google herauszufinden, was zutreffen könnte.
Gruß Elmar
- Bearbeitet Elmar BoyeEditor Montag, 26. November 2012 16:17
-
****************************************************************************************************************
Dieser Thread wurde mangels weiterer Beteiligung des Fragestellenden ohne bestätigte Lösung abgeschlossen.
Neue Rückfragen oder Ergänzungen zu diesem Thread bleiben weiterhin möglich.
****************************************************************************************************************Robert Breitenhofer, MICROSOFT
Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.