|
JLI Spieleprogrammierung
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
AFE-GmdG JLI MVP
Alter: 45 Anmeldedatum: 19.07.2002 Beiträge: 1374 Wohnort: Irgendwo im Universum... Medaillen: Keine
|
Verfasst am: 14.10.2005, 18:27 Titel: Memory Mapped Files |
|
|
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 |
|
|
Jonathan_Klein Living Legend
Alter: 37 Anmeldedatum: 17.02.2003 Beiträge: 3433 Wohnort: Siegerland Medaillen: Keine
|
Verfasst am: 14.10.2005, 22:40 Titel: |
|
|
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 |
|
|
AFE-GmdG JLI MVP
Alter: 45 Anmeldedatum: 19.07.2002 Beiträge: 1374 Wohnort: Irgendwo im Universum... Medaillen: Keine
|
Verfasst am: 17.10.2005, 09:23 Titel: |
|
|
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 |
|
|
|
|
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
|