DLL Speicher Problem
-
Hallo!
Ich möchte einen Engine programmieren.. Für den benutzer des Engine soll eine DLL und ein Interface zur Verfügung stehen:
/// <summary> /// S7 Game Engine namespace. /// </summary> namespace S7Engine { typedef /// <summary> /// Represents a S7 resulting value. /// </summary> HRESULT TS7RESULT; const /// <summary> /// Success result. /// </summary> TS7RESULT S7SUCCESS = HRESULT(0); /// <summary> /// Failed result. /// </summary> TS7RESULT S7FAILED = HRESULT(-1); /// <summary> /// Represents a S7 engine. /// </summary> class IS7Interface { public: /// <summary> /// Destructor. /// </summary> virtual ~IS7Interface() {}; /// <summary> /// Device initialisation. /// </summary> virtual TS7RESULT InitDevice() = 0; }; };Soweit so gut. Ich exportiere mit der DLL zwei Methoden:
extern "C" { __declspec(dllexport) void CreateS7Device(S7Engine::IS7Interface*& device) { device = new S7Engine::TS7Engine(); } __declspec(dllexport) void ReleaseS7Device(S7Engine::IS7Interface* device) { if (device != 0) delete device; } }Der Benutzer soll also über die DLL die CreateS7Device aufrufen und damit dann einen Zeiger auf den Engine zurückbekommen, der das Interface implementiert. Hierdurch könnte er dann die Methoden des Engine verwenden (Im Beispiel nur die Init device).
TS7Engine implementiert das Interface:
class TS7Engine: public IS7Interface { public: virtual TS7RESULT InitDevice(); };Ich erhalte eine DLL beim Builden.
Nun möchte ich diese verwenden:HINSTANCE hInstLibrary = 0; void CreateDLLAccess(std::string name, S7Engine::IS7Interface*& s7) { hInstLibrary = LoadLibrary(L"S7 Engine.dll"); if (hInstLibrary) { CreateS7Device _CreateDev = (CreateS7Device)GetProcAddress(hInstLibrary, "CreateS7Device"); if (_CreateDev) { _CreateDev(s7); } } }Diese Methode ruft die Methode aus der DLL auf und sollte mir einen Zeiger auf den erstellten engine zurück liefern.
Der Code CreateS7Device des engines wird auch ausgeführt, nur erhalte ich eine Speicher exception:Eine nicht behandelte Ausnahme des Typs "System.AccessViolationException" ist in mscorlib.dll aufgetreten.
Zusätzliche Informationen: Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist.
Wenn ich in der CreateS7Device folgendes mache, gibt es keine Exception:
__declspec(dllexport) void CreateS7Device(S7Engine::IS7Interface*& device) { return ; }Sprich ich darf anscheinend nicht den Wert des Zeigers verändern... Das muss ich ja aber!
Wie komme ich denn raus aus der Situation. Dieses Designstil habe ich in einem Buch gelesen, dieses habe ich aber leider nicht mehr.
Infos:
Visual Studio 2010
.Net Framework 4Danke!
Edit::
Mir ist gerade aufgefallen, dass die Exception nicht erscheint,
WENN ICH ALS 64BIT ANWENDUNG KOMPILIERE.
Was soll das denn???
-
C++ kennt keine DLLs - ich bin mir allerdings noch nicht sicher, ob du im CLI-Board oder in der WinAPI besser aufgehoben wärst.
-
1. Dein Code ist C++, C++/CLI kommt erst ins Spiel, weil es mit dem /clr Schalter kompiliert wurde.
2. Genau diesen Schalter würde ich ausschalten - warum benötigst Du hier .NET? Und wenn Du .NET benötigst - warum dann nicht gleich ein .NET Assemlby?Mein Tipp - entweder .NET ausschalten (/clr wegnehmen) oder dann auf .NET setzten und das ganze z.B. in C# machen.
Zu deinem Problem - benutz den Debugger - der sagt üblicherweise WO die Access Violation aufgetreten ist.
BTW: vor dem delete muss nicht auf 0 geprüft werden, dass kann delete selbst.
**Edit:**Mir ist erst jetzt dein Edit aufgefallen - mein gesagtes behält aber Gültigkeit.
-
Danke erstmal für die Antworten.
Huh da steigt ja keiner mehr durch..
Ich möchte einen DirectX-Game-Engine samt zugehörigem Editor entwickeln.
Der Engine soll als seperate DLL Vorliegen.Also wenn ich das richtig verstanden habe empfiehlt ihr mir für die Oberfläche C# und für den Engine c++, oder? (DirectX-Header ist ja in c++).
Was ist mit dem komischen clr schalter?
Gruß
Björn
-
Der /clr Schalter ist ein Compiler Flag, welcher dir die Nutzung des .NET Frameworks / Runtime ermöglicht.
Edit: http://msdn.microsoft.com/en-us/library/k8d11d4s.aspxAlso wenn ich das richtig verstanden habe empfiehlt ihr mir für die Oberfläche C# und für den Engine c++, oder? (DirectX-Header ist ja in c++).
Nein. Meine Empfehlung ist es dich zu entscheiden, ob managed oder nicht.
AFAIK gibts DirectX auch in einer managed Version. Falls Du die C# Schiene fahren möchtest, kannst Du auch mal XNA angucken.
-
Du solltest Deinen dll relevanten Header stark vereinfachen, und am Besten nur C-Typen anbieten:
//s7_engine_dll_entry.h #ifndef __include_guard #define __include_guard extern "C" { //nur deklaration struct s7_interface; typedef struct s7_interface * s7_interface_handle; is7_interface_handle create_s7_instance(void); void destry_s7_instace(s7_interface_handle handle); } #endif __include_guardWenn Du die dll immer dynamisch laden willst (mittels
LoadLibrary), braucht es dasdll_exportZeugs nicht. Ansonsten: Hinzufügen.Die Arbeit wird dann in der Implementierung gemacht:
//s7_engine_dll_entry.cpp #include "s7_engine_dll_entry.h" struct s7_interface { virtual init() = 0; //hier ggf Ctor definieren }; class s7_engine : public s7_interface { public: virtual init(){ ... } }; is7_interface_handle create_s7_instance(void) { return new s7_interface(); } void destry_s7_instace(s7_interface_handle handle) { delete handle; }ALternativ kannst Du auch die Implementierung in die zurückgegebene Struktur wrappen (ich finde das eigentlich besser).
-
Hallo nochmal,
ich kriege dieses 64/32 bit Problem nicht in den Griff, mir ist das schleierhaft.
Ich laden mit LoadLibrary.
Interface: S7 Interface.h
/// <summary> /// Represents a S7 engine interface. /// </summary> struct IS7Interface { public: /// <summary> /// Device initialisation. /// </summary> virtual TS7RESULT InitDevice(HWND hWnd) = 0; virtual TS7RESULT RenderDevice() = 0; };Engine Header: (S7 Engine.h)
#ifndef _S7ENGINE_H_ #define _S7ENGINE_H_ #include "S7 Interface.h" #include "D3DX11.h" #include "D3D11.h" class TS7Engine: public IS7Interface { public: virtual TS7RESULT InitDevice(HWND hWnd); virtual TS7RESULT RenderDevice(); private: ID3D11Device* gDevice; ID3D11RenderTargetView* gRenderTarget; IDXGISwapChain* gSwapChain; ID3D11DeviceContext* ImmCont; }; extern "C" { __declspec(dllexport) IS7Interface* CreateS7Device() { return new TS7Engine(); } __declspec(dllexport) void ReleaseS7Device(IS7Interface* device) { if (device != 0) delete device; } } #endifEngine Implementierung: S7 Engine.cpp
#define DEBUG #include "S7 Engine.h" #include <D3DX11.h> #include <D3D11.h> //#include <D3DX10.h> #include <DXUT.h> TS7RESULT TS7Engine::InitDevice(HWND hWnd) { // Swap Chain Desc füllen (Konfiguration) DXGI_SWAP_CHAIN_DESC dsc = {0}; dsc.BufferCount = 1; dsc.BufferDesc.Width = 1024; dsc.BufferDesc.Height = 768; dsc.BufferDesc.RefreshRate.Denominator = 1; // 60Hz dsc.BufferDesc.RefreshRate.Numerator = 60; dsc.Windowed = true; dsc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 32Bit + Alpha dsc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // Als Render Target benutzen dsc.OutputWindow = hWnd; // Unser Fenster übergeben dsc.SampleDesc.Count = 1; // Qualität des Samplings = 1 dsc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // Pointertausch statt kopieren // Device erzeugen if(FAILED(D3D11CreateDeviceAndSwapChain(NULL /*Standardadapter*/, D3D_DRIVER_TYPE_HARDWARE /* Hardware */, NULL /* Kein Softwaremodul */, 0, // Keine Flags NULL, NULL, D3D11_SDK_VERSION, // SDK-Version benutzen &dsc, // Konfigurationsstrutktur &gSwapChain, // Swap Chain &gDevice, NULL, &ImmCont))) // Device return S7FAILED; // Create a render target view ID3D11Texture2D *pBackBuffer; if( FAILED( gSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (LPVOID*)&pBackBuffer ) ) ) return FALSE; HRESULT hr = gDevice->CreateRenderTargetView( pBackBuffer, NULL, &gRenderTarget ); pBackBuffer->Release(); if( FAILED( hr ) ) return FALSE; ImmCont->OMSetRenderTargets( 1, &gRenderTarget, NULL ); D3D11_VIEWPORT vp; vp.Width = (FLOAT)1024; vp.Height = (FLOAT)768; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; ImmCont->RSSetViewports( 1, &vp ); return S7SUCCESS; } TS7RESULT TS7Engine::RenderDevice() { // // Clear the backbuffer // float ClearColor[4] = { 0.0f, 0.125f, 0.6f, 1.0f }; // RGBA ImmCont->ClearRenderTargetView( gRenderTarget, ClearColor ); gSwapChain->Present( 0, 0 ); return S7SUCCESS; }Ich kompiliere ohne /clr.
In meinem Aufrufenden Projekt sieht es so aus:
// S7 WorldEditor.cpp: Hauptprojektdatei. #include "stdafx.h" #include "Form1.h" using namespace S7WorldEditor; IS7Interface* dll = NULL; void MyEventHandler(Object^ o, EventArgs ^) { dll->RenderDevice(); } [STAThreadAttribute] int main(array<System::String ^> ^args) { // Aktivieren visueller Effekte von Windows XP, bevor Steuerelemente erstellt werden Application::EnableVisualStyles(); Application::SetCompatibleTextRenderingDefault(false); dll = CreateDLLAccess(L"S7 Engine.dll"); Form^ frm = gcnew Form1(); HRESULT res = dll->InitDevice((HWND)frm->Handle.ToPointer()); Application::Idle += gcnew EventHandler(MyEventHandler); // Hauptfenster erstellen und ausführen Application::Run(frm); return 0; }mit dem DLL-Loader:
#ifndef _S7DLLACCESS_H #define _S7DLLACCESS_H #include <Windows.h> #include "..\..\Source\S7 Engine\S7 Interface.h" HINSTANCE hInstLibrary = 0; #define CREATES7DEVICEDLLNAME "CreateS7Device" #define RELEASES7DEVICEDLLNAME "ReleaseS7Device" typedef /// <summary> /// Interface creation. Exported method. /// </summary> IS7Interface* (*CreateS7Device)(); /// <summary> /// Interface delete. Exported method. /// </summary> void (*ReleaseS7Device)(IS7Interface*); IS7Interface* CreateDLLAccess(LPCWSTR name) { hInstLibrary = LoadLibrary(name); if (hInstLibrary) { CreateS7Device _CreateDev = (CreateS7Device)GetProcAddress(hInstLibrary, CREATES7DEVICEDLLNAME); if (_CreateDev != NULL) return _CreateDev(); } return NULL; } #endifSo nun zur Situation:
Wenn ich alles beides als 64 bit kompiliere, funktioniert es and anstandslos.
Bei 32 bit schmeisst die Methode D3D11CreateDeviceAndSwapChain in S7 Engine.cpp einen Fehler zurück (also als Result). Im DX Log steht:DXGI Warning: IDXGIFactory::CreateSwapChain: Warning:
DXGI_SWAP_CHAIN_DESC.OutputWindow is not a valid window handle...
DXGI Error: Swapchains may not target a window that is not owned by the
calling process.Offensichtlich wird das handle bei dem 64 bit kompilieren richtig übergeben, bei 32 bit falsch... WIESO UM ALLES IN DER WELT?
Ich besitzte ein 64 bit System.
Jemand eine Idee?
Gruß
BjörnEdit: Ich habe das ermittelte Handle in der Hauptanwendung mal überprüft... es ist richtig, es hat den selben Wert wie hiermit ermittelt: http://www.codeguru.com/cpp/misc/samples/article.php/c1489
Edit2: OK das aufrufende Programm ermittelt das Handle richtig...
Aber: Es kommt bei 32bit ein falscher Wert in der DLL an. Bei 64bit ist er richtig. Warum das so ist weiß ich nicht.