Direct3D Problem
-
BTW, um noch explizitere Infos dazu zu geben - aus der DX Doku:
COM objects enforce stricter encapsulation than C++ objects. You cannot just create the object and call any public method. A COM object's public methods are grouped into one or more interfaces. To use a method, you must create the object and obtain the appropriate interface from the object. An interface typically contains a related set of methods that provide access to a particular feature of the object. For example, the IDirect3DCubeTexture9 interface contains methods that enable you to manipulate cube texture resources. Any methods that are not part of an interface are not accessible.
COM objects are not created in the same way as C++ objects. There are several ways to create a COM object, but all involve COM-specific techniques. The MicrosoftDirectX
API includes a variety of helper functions and methods that simplify creating most of the DirectX objects.
You must use COM-specific techniques to control the lifetime of the object.
COM objects do not need to be explicitly loaded. COM objects are typically contained in a DLL. However, you do not need to explicitly load the DLL or link to a static library in order to use a COM object. Each COM object has a unique registered identifier that is used to create the object. COM automatically loads the correct DLL.
-
Fazit ist aber, dass die DLL aufgrund der Initialisierungen, die während D3DCreate9 gemacht werden, Zeit braucht, sich zu deinitialisieren. Da dies wahrscheinlich asynchron geschieht, wird ihr durch FreeLibrary diese Zeit nicht gewährt, was sie zum Absturz bringt.
-
Ich denke mal das liegt daran dass Direct3DCreate9() in der lib liegt und nicht in der DLL .
Aber Direct3DCreate9 ist doch eine Funktion der DLL. Gib mal folgendes auf der Kommandozeile im Verzeichnis von d3d9.dll ein:
dumpbin /EXPORTS d3d9.dll
(dumpbin gehört zu MSVC gibt aber auch andere Programme dafür).WIESO sollte folgender Code das zum Absturz bringen?!?!
Laut RenéG würde sich die DLL freigeben, sobald der Referenzzähler auf 0 geht, dh es ist kein gültiger Code mehr für Direct3DCreate9 da.
However, you do not need to explicitly load the DLL
Das klingt interessant. Soll das nun heissen, sobald ich eine DLL hab, die auf COM basiert, darf ich mit Load- und FreeLibrary nicht arbeiten und muss die DLL implizit binden ? Wenn dem so ist, machen das dann alle falsch in dessen Projekten ich ein explizites Binden gesehen hab ?
Fazit ist aber, dass die DLL aufgrund der Initialisierungen, die während D3DCreate9 gemacht werden, Zeit braucht, sich zu deinitialisieren. Da dies wahrscheinlich asynchron geschieht, wird ihr durch FreeLibrary diese Zeit nicht gewährt, was sie zum Absturz bringt.
Sollte man nicht meinen, dass mit d3d->Release() alle Ressourcen freigegeben wurden ? Und selbst wenn du Recht hast und Windows danach noch gewisse Dinge zu deinitialisieren hat, sollte Windows das nicht dann in FreeLibrary berücksichtigen ?
[ Dieser Beitrag wurde am 03.07.2003 um 16:52 Uhr von groovemaster2002 editiert. ]
-
Wenn dem so ist, machen das dann alle falsch in dessen Projekten ich ein explizites Binden gesehen hab?
Gut, ich hab bisher überall nur implizites Binden gesehen. Und wenn Du diese Projekte ausführst, stürzen die auch ab?
-
Original erstellt von groovemaster2002:
Laut RenéG würde sich die DLL freigeben, sobald der Referenzzähler auf 0 geht, dh es ist kein gültiger Code mehr für Direct3DCreate9 da.
Ja, aber da steht doch nix von LoadLibrary oder so...!?!
Wenn mit D3DCreate COM die DLL läd' und bei Release wieder entlädt, dann lädt sie die mit dem nächsten D3DCreate-Aufruf halt wieder... da geht doch nix BOOOOM?!?!Sollte man nicht meinen, dass mit d3d->Release() alle Ressourcen freigegeben wurden ? Und selbst wenn du Recht hast und Windows danach noch gewisse Dinge zu deinitialisieren hat, sollte Windows das nicht dann in FreeLibrary berücksichtigen ?
Plenker!Naja, dann probier' doch mal ein Sleep(20000) als Test vor dem bösen FreeLibrary...!??
Wär' doch mal interessant.
Mit dem impliziten hab' ich mich nämlich auch noch net befasst...
-
Direct3D macht einen ganz schön konfus, hab mir das mal angeschaut.
1. Direct3D basiert auf COM, somit dachte ich, die COM-Kenntnisse hier anwenden zu können -> falsch
2. 'Direct3DCreate9' ist eine Funktion innerhalb einer COM-DLL. D.h. die DLL muss gelade sein, bevor die Funktion aufgerufen wird.
3. COM-Schnittstellen existieren innerhalb der DLL, mit diesen wird aber nicht der Referenzcount der DLL verändert.Es wäre mal angebracht, ein kleines Testprogramm zu schreiben, welches die DLL dynamisch lädt, den Fullscreen-Modus initialisiert und dann alles wieder ordnungsgemäss rückgängig macht und das mal zu debuggen.
Wer übernimmt das?
-
Endlich jemand der mich versteht (*schluchz*). Ich werde das von dir angesprochene Testprogramm mal schreiben und dann hier posten...
-
So, hier hab ich jetzt das Programm (getestet mit MSVC 6):
#include <windows.h> #include <d3d9.h> #define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = NULL; } } typedef IDirect3D9* (WINAPI *Func_Direct3DCreate)(UINT SDKVersion); Func_Direct3DCreate Direct3DCreate; HMODULE D3D_DLL; HWND MainWindow; bool Active; LPDIRECT3D9 D3D; LPDIRECT3DDEVICE9 D3D_Device; D3DPRESENT_PARAMETERS D3D_PP; // D3D initialisieren bool D3D_Init() { // "Verbindung" zur d3d9.dll herstellen D3D_DLL = LoadLibrary("d3d9.dll"); if (!D3D_DLL) { MessageBox(NULL, "Fehler LoadLibrary()", NULL, MB_OK); return(0); } // Adresse von Direct3DCreate9 ermitteln Direct3DCreate = (Func_Direct3DCreate) GetProcAddress(D3D_DLL, "Direct3DCreate9"); // D3D Instanz erzeugen D3D = Direct3DCreate(D3D_SDK_VERSION); if (!D3D) return(false); // Device erzeugen D3D_PP.BackBufferWidth = 800; // 800x600 32bit Farbtiefe sollte jede Graka unterstuetzen D3D_PP.BackBufferHeight = 600; D3D_PP.BackBufferFormat = D3DFMT_X8R8G8B8; D3D_PP.BackBufferCount = 1; D3D_PP.MultiSampleType = D3DMULTISAMPLE_NONE; D3D_PP.MultiSampleQuality = 0; D3D_PP.SwapEffect = D3DSWAPEFFECT_DISCARD; D3D_PP.hDeviceWindow = MainWindow; D3D_PP.Windowed = FALSE; // wir wollen Fullscreen D3D_PP.EnableAutoDepthStencil = TRUE; D3D_PP.AutoDepthStencilFormat = D3DFMT_D16; // 16bit Z-Buffer sollte auch jede 3D Graka koennen D3D_PP.Flags = 0; D3D_PP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; D3D_PP.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // mit vsync HRESULT hr = D3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, MainWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &D3D_PP, &D3D_Device); if (hr != D3D_OK) return(false); // alles ok return(true); } // D3D deinitialisieren void D3D_Render() { D3D_Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0); D3D_Device->BeginScene(); D3D_Device->EndScene(); D3D_Device->Present(NULL, NULL, NULL, NULL); } // D3D deinitialisieren void D3D_Cleanup() { SAFE_RELEASE(D3D_Device); SAFE_RELEASE(D3D); // d3d9.dll freigeben if (D3D_DLL) FreeLibrary(D3D_DLL); } // alle benoetigten Ressourcen der Anwendung freigeben void Cleanup() { /* // sofern d3d9.dll hier freigegeben wird, treten keine Probleme auf if (D3D_DLL) FreeLibrary(D3D_DLL); */ } LRESULT CALLBACK WindowProcMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_KEYDOWN: switch (wParam) { case VK_SPACE: if (D3D_Device) { D3D_PP.Windowed = !D3D_PP.Windowed; D3D_Device->Reset(&D3D_PP); } break; } break; case WM_CLOSE: D3D_Cleanup(); DestroyWindow(hWnd); PostQuitMessage(0); return(0); } return(DefWindowProc(hWnd, uMsg, wParam, lParam)); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // Fensterklasse registrieren WNDCLASS wc; wc.style = 0; wc.lpfnWndProc = WindowProcMain; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = TEXT("D3D_Test"); ATOM at = RegisterClass(&wc); if (at == 0) { MessageBox(NULL, "Fehler RegisterClassEx()", NULL, MB_OK); return(0); } // Hauptfenster erzeugen MainWindow = CreateWindow( wc.lpszClassName, TEXT("D3D Test"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE, 0, 0, 640, 480, NULL, NULL, hInstance, NULL); if (!MainWindow) { MessageBox(NULL, "Fehler CreateWindowEx()", NULL, MB_OK); return(0); } // D3D initialisieren if (!D3D_Init()) { MessageBox(NULL, "Fehler D3D_Init()", NULL, MB_OK); D3D_Cleanup(); DestroyWindow(MainWindow); return(0); } // Message Loop der Anwendung MSG msg; bool Active = true; while (Active) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Nachrichten des Hauptfensters handeln if (msg.message == WM_QUIT) Active = false; else { TranslateMessage(&msg); DispatchMessage(&msg); } } else { // D3D Rendern D3D_Render(); } } // Anwendung beenden Cleanup(); return(msg.wParam); }
Am wichtigsten sind hierbei die Funktionen D3D_Cleanup und Cleanup. D3D_Cleanup wird aufgerufen, wenn das Fenster geschlossen wird, also das Fenster eine WM_CLOSE Nachricht erhält. Bei einem Vorlageprojekt des DirectX 9 AppWizard in MSVC wird das übrigens genauso gemacht (soweit ich das beim Überfliegen der etlichen Quellcodedateien mitbekommen habe). Cleanup wird kurz bevor man WinMain verlässt aufgerufen, hier wird aber vorerst noch nix gemacht. Startet man jetzt die Anwendung und verlässt diese (Alt+F4) sobald man im Fullscreen Mode ist, stürzt das Programm ab.
Also zurück zur Funktion D3D_Cleanup. Hier wird die d3d9.dll mit folgender Sequenz freigegeben:if (D3D_DLL) FreeLibrary(D3D_DLL);
Kommentiert man diese jetzt aus und führt das ganze in der Funktion Cleanup durch (schon vorbereitet, einfach Kommentarzeichen entfernen), so kommt es zu keinem Absturz.
[ Dieser Beitrag wurde am 04.07.2003 um 14:58 Uhr von groovemaster2002 editiert. ]
-
Womit wir nun doch noch den Fehler gefunden haben, es liegt nämlich gar net an FreeLibrary, sondern an Deiner Messageloop.
Du hast sicherlich übersehen, dass nach der WM_CLOSE-Message noch andere Nachrichten folgen, z.B. WM_DESTROY sowie WM_NCDESTROY. Während diese Nachrichten an der Messageloop empfangen werden, rufst Du noch D3D_Render() auf, obwohl das schon gar net mehr da ist.
-
Ok, hab jetzt mal den Aufruf von D3D_Render durch folgendes ersetzt:
if (D3D_Device) D3D_Render();
Jetzt sollte er wirklich nur rendern, sofern ein gültiges Device vorliegt. Leider treten immer noch die gleichen Probleme auf. Der Message Loop ist übrigens so aufgebaut, dass entweder Windows Nachrichten behandelt werden ODER gerendert wird, niemals beides gleichzeitig.