JLI Spieleprogrammierung Foren-Übersicht JLI Spieleprogrammierung

 
 FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen 
 medals.php?sid=ffa9c0d7552584efab75902f44e4b9a0Medaillen   RegistrierenRegistrieren   ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin 

Memory Mapped Files

 
Neues Thema eröffnen   Neue Antwort erstellen    JLI Spieleprogrammierung Foren-Übersicht -> Tutorials
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
AFE-GmdG
JLI MVP
JLI MVP


Alter: 44
Anmeldedatum: 19.07.2002
Beiträge: 1374
Wohnort: Irgendwo im Universum...
Medaillen: Keine

BeitragVerfasst am: 14.10.2005, 18:27    Titel: Memory Mapped Files Antworten mit Zitat

Memory Mapped Files sind was ganz tolles:
Damit tut man so, als sei eine Datei Teil des Rams, den ein Programm zur Verfügung hat. Diesen muss man nicht Reservieren oder Freigeben, die passenden Speicheradressen sind einfach direkt auf die Festplatte gelinkt.

Und wofür soll das gut sein?
Wer schonmal mit Dateien gearbeitet hat, weiss, was man normalerweise tun muss, damit man irgendetwas mit den Daten dort drinnen machen kann:
    1. Datei öffnen
    2. Länge der Datei ermitteln (Schitt 1 und 2 lassen sich auch austauschen - je nach verwendeter API)
    3. Puffer reservieren (new, malloc oder Ähnliches)
    4. Datei lesen und in den Puffer kopieren
    5. Mit dem Puffer die gewünschten arbeiten machen
    6. Datei wieder schliessen (Schitt 5 und 6 lassen sich auch austauschen - je nach Anwendungszweck)
    7. Puffer wieder freigeben (Memoryleck verhindern)

Was wäre jetzt, wenn ich einfach einen Speicherbereich meiner Anwendung auf die Festplatte verbiege?
    1. Datei öffnen
    2. Speicher verbiegen
    3. Mit dem Speicher gewünschte Aktion ausführen
    4. Datei schliessen

Sieht nicht nur kürzer aus, ist auch viel schneller.

Schön und gut, wie geht das jetzt?
Das ist das Beste daran, es klingt nicht nur einfach, es ist einfach:

Zuerst öffnet man seine Datei.
CPP:
HANDLE hFile=CreateFile(FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, 0);

Danach erstellt man ein MemoryMap auf die Datei:
CPP:
hMap=CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);

Und zum Schluss verbiegt man den Speicher:
CPP:
pVoid=MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);

pVoid zeigt jetzt auf einen Speichebereich, der sich physikalisch auf der Festplatte befindet und gleichzeitig im 4 GB-Addressbereich der Anwendung.
Nachdem man mit dem Speicher gemacht hat, was man wollte, rämt man wieder auf:
CPP:
UnmapViewOfFile(pVoid);
CloseHandle(hMap);
CloseHandle(hFile);

Das ist alles. Der verbogene Speicherbereich ist nicht länger existent, pVoid zeigt jetzt auf Nichts...

Und weil alles so schön war, noch eine Klasse, die das ganze kapselt.
Für diejenigen unter uns, denen das immer noch zu kompliziert ist, einfacher geht es hiermit nun wirklich nicht mehr:

MMap.h
CPP:
#pragma once

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <string>

#ifdef MMAP_EXPORTS
#define MMAP_API __declspec(dllexport)
#else
#define MMAP_API __declspec(dllimport)
#endif

namespace AFE_GmdG {

   namespace MMAP {

      class MMap
      {
      private:
         HANDLE hFile;
         HANDLE hMap;
         PVOID pVoid;

      public:
         MMAP_API MMap(const char* pFileName, PBYTE* ppPuffer);
         MMAP_API MMap(const wchar_t* pFileName, PBYTE* ppPuffer);
         MMAP_API ~MMap();
      };

   }

}


MMap.cpp
CPP:
#include "MMap.h"

namespace AFE_GmdG {

   namespace MMAP {

      MMap::MMap(const char* pFileName, PBYTE* ppPuffer) : hFile(0), hMap(0), pVoid(0)
      {
         hFile=CreateFileA(pFileName, GENERIC_READ, 0, 0,
            OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, 0);
         if(hFile==INVALID_HANDLE_VALUE) {
            hFile=0;
            throw std::string("Fehler: Datei existiert nicht oder lässt sich nicht öffnen.");
         }
         DWORD FileSize;
         FileSize=GetFileSize(hFile, 0);
         if(!FileSize) {
            CloseHandle(hFile);
            hFile=0;
            throw std::string("Fehler: Datei ist leer.");
         }
         hMap=CreateFileMappingA(hFile, 0, PAGE_READONLY, 0, 0, 0);
         if(hMap==INVALID_HANDLE_VALUE) {
            CloseHandle(hFile);
            hFile=0;
            hMap=0;
            throw std::string("Fehler: Mapping nicht möglich.");
         }
         pVoid=MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
         if(!pVoid) {
            CloseHandle(hMap);
            CloseHandle(hFile);
            hMap=0;
            hFile=0;
            throw std::string("Fehler: Mapping nicht möglich.");
         }
         *ppPuffer=(PBYTE)pVoid;
      }

      MMap::MMap(const wchar_t* pFileName, PBYTE* ppPuffer) : hFile(0), hMap(0), pVoid(0)
      {
         hFile=CreateFileW(pFileName, GENERIC_READ, 0, 0,
            OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, 0);
         if(hFile==INVALID_HANDLE_VALUE) {
            hFile=0;
            throw std::string("Fehler: Datei existiert nicht oder lässt sich nicht öffnen.");
         }
         DWORD FileSize;
         FileSize=GetFileSize(hFile, 0);
         if(!FileSize) {
            CloseHandle(hFile);
            hFile=0;
            throw std::string("Fehler: Datei ist leer.");
         }
         hMap=CreateFileMappingW(hFile, 0, PAGE_READONLY, 0, 0, 0);
         if(hMap==INVALID_HANDLE_VALUE) {
            CloseHandle(hFile);
            hFile=0;
            hMap=0;
            throw std::string("Fehler: Mapping nicht möglich.");
         }
         pVoid=MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
         if(!pVoid) {
            CloseHandle(hMap);
            CloseHandle(hFile);
            hMap=0;
            hFile=0;
            throw std::string("Fehler: Mapping nicht möglich.");
         }
         *ppPuffer=(PBYTE)pVoid;
      }

      MMap::~MMap()
      {
         if(pVoid) {
            UnmapViewOfFile(pVoid);
         }
         if(hMap) {
            CloseHandle(hMap);
            hMap=0;
         }
         if(hFile) {
            CloseHandle(hFile);
            hFile=0;
         }
      }

   }

}


Das ganze ist Code, der für eine DLL bestimmt ist und die Verwendung ist Kinderleicht:
CPP:
PBYTE pPuffer;
AFE_GmdG::MMAP::MMap MyMap("Dateiname und Pfad", &pPuffer);
std::cout << pPuffer;


Zum Schluss noch das ganze Projekt.
MMap.rar

Wie immer:
Meinungen und Anregungen erwünscht.
Freigabe zum kopieren des Tutorials auf eigene Webseiten erlaubt, aber bitte mit Hinweis auf und an mich. (Man kann mir ja auch eine Mail schicken, in der man mir mitteilt, dass man mein Tutorial auf seiner Seite verwenden möchte)

AFE-GmdG
_________________
CPP:
float o=0.075,h=1.5,T,r,O,l,I;int _,L=80,s=3200;main(){for(;s%L||
(h-=o,T= -2),s;4 -(r=O*O)<(l=I*I)|++ _==L&&write(1,(--s%L?_<(L)?--_
%6:6:7)+\"World! \\n\",1)&&(O=I=l=_=r=0,T+=o /2))O=I*2*O+h,I=l+T-r;}
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden Website dieses Benutzers besuchen
Jonathan_Klein
Living Legend


Alter: 36
Anmeldedatum: 17.02.2003
Beiträge: 3431
Wohnort: Siegerland
Medaillen: Keine

BeitragVerfasst am: 14.10.2005, 22:40    Titel: Antworten mit Zitat

jo klingt sehr praktisch. Wenn ich dann statt fread die zeiger benutez kann ich machen:
CPP:
int Integer;
void * Dateizeiger;
Interger=*Dateizeiger;
Dateizeiger+=sizeof(void);

oder?
_________________
https://jonathank.de/games/
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden Website dieses Benutzers besuchen
AFE-GmdG
JLI MVP
JLI MVP


Alter: 44
Anmeldedatum: 19.07.2002
Beiträge: 1374
Wohnort: Irgendwo im Universum...
Medaillen: Keine

BeitragVerfasst am: 17.10.2005, 09:23    Titel: Antworten mit Zitat

Ich würde den Pufferzeiger selbst nicht benutzen. Mache einfach folgendes:
CPP:
PBYTE pPuffer; // Ist ein Pointer auf Bytes (unsigned chars)
AFE_GmdG::MMAP::MMap* MyMap=new AFE_GmdG::MMAP::MMap("Dateiname und Pfad", &pPuffer);
struct XYZ {
  int i;
  char[12] bla;
};
XYZ* pXYZ=(XYZ*)(pPuffer+OffsetInBytes);
// Irgendwas mit dem Struct anstellen
// int a=pXYZ->i+7;
delete MyMap; // Jetzt ist ebenfalls pXYZ unültig...

Mit einfachen Variablen wie Ints funktioniert das genausogut, Komplexere Strukturen wie Klassen oder std::strings müssen Deserialisiert werden.
Achtung
Mit meiner Klasse habe ich eine reine Lese-Klasse erstellt, dieser Speicherbereich kann nicht beschrieben werden!
Es reicht aber, um z.B. Texturen oder Soundpuffer zu laden...
_________________
CPP:
float o=0.075,h=1.5,T,r,O,l,I;int _,L=80,s=3200;main(){for(;s%L||
(h-=o,T= -2),s;4 -(r=O*O)<(l=I*I)|++ _==L&&write(1,(--s%L?_<(L)?--_
%6:6:7)+\"World! \\n\",1)&&(O=I=l=_=r=0,T+=o /2))O=I*2*O+h,I=l+T-r;}
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden Website dieses Benutzers besuchen
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    JLI Spieleprogrammierung Foren-Übersicht -> Tutorials Alle Zeiten sind GMT
Seite 1 von 1

 
Gehe zu:  
Du kannst keine Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum nicht antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.


Powered by phpBB © 2001, 2005 phpBB Group
Deutsche Übersetzung von phpBB.de

Impressum