NULL이 아닌 포인터로 인해 winapi와 함께 m_pRenderTarget을 사용하는 읽기 액세스 위반
Windows API 용 Microsoft Docs를 따르고 있습니다. 저는 현재 4 장에 있으며 타원을 그리려고합니다. 는 m_pRenderTarget
클래스 App.h.에 선언 함수에서 OnRender(HWND hwnd)
나는 그것을 사용하여 기하학 (타원)을 그리려고합니다. 그러나 다음과 같은 오류가 발생합니다.
예외 발생 : 읽기 액세스 위반. this-> m_pRenderTarget 은 0x38입니다.
약간의 디버깅 후, 나는 그것을 초기화하고 아직 변경하지 않았지만 (적어도 생각하지 않는다) HRESULT App::CreateDeviceResources(HWND hwnd)
함수에서 m_pRenderTarget
어떤 이유로 NULL이 아니라는 것을 알았습니다. 내 생각 엔 이것이 문제라는 것입니다. 참고로 관련 코드는 다음과 같습니다.
#pragma once
#define MAX_LOADSTRING 100
#include "resource.h"
#include "pch.h"
class App
{
public:
App();
~App();
bool Init(HINSTANCE instance, int cmd);
int RunMessageLoop();
HINSTANCE getInstance() { return hInstance; }
private:
HINSTANCE hInstance;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
ID2D1Factory* m_pD2DFactory;
ID2D1EllipseGeometry* m_pEllipseGeometry;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pBlackBrush;
ATOM RegisterClass();
BOOL InitInstance(int);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
HRESULT CreateDeviceIndependentResources();
HRESULT CreateDeviceResources(HWND hwnd);
HRESULT OnRender(HWND hwnd);
};
다음은 구현입니다.
#include "pch.h"
#include "App.h"
App::App()
: m_pRenderTarget(NULL)
{}
App::~App(){}
bool App::Init(HINSTANCE instance, int cmd)
{
hInstance = instance;
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_PRACTICE, szWindowClass, MAX_LOADSTRING);
RegisterClass();
if (!InitInstance(cmd))
return false;
return true;
}
int App::RunMessageLoop()
{
HACCEL hAccelTable;
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PRACTICE));
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM App::RegisterClass()
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRACTICE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL App::InitInstance(int nCmdShow)
{
HRESULT hr = CreateDeviceIndependentResources();
if FAILED(hr)
{
return FALSE;
}
HWND hWnd;
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, NULL, NULL, hInstance, this);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK App::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
App* pApp;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
pApp = (App*)pcs->lpCreateParams;
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, PtrToUlong(pApp));
return TRUE;
}
else
{
pApp = reinterpret_cast<App*>(static_cast<LONG_PTR>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)));
if (!pApp)
return DefWindowProc(hWnd, message, wParam, lParam);
}
int wmld, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(NULL, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, pApp->About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
pApp->OnRender(hWnd);
EndPaint(hWnd, &ps);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK App::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
HRESULT App::CreateDeviceIndependentResources()
{
HRESULT hr;
// Create a Direct2D factory
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
if (SUCCEEDED(hr))
{
// Create an ellipse geometry
const D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(105.0f, 105.0f), 25.0f, 25.0f);
hr = m_pD2DFactory->CreateEllipseGeometry(ellipse, &m_pEllipseGeometry);
}
return hr;
}
HRESULT App::CreateDeviceResources(HWND hwnd)
{
//Notice that this causes HERE to be printed out, indicating that m_pRenderTarget != NULL
if (m_pRenderTarget != NULL)
OutputDebugStringA("\nHERE\n");
else
OutputDebugStringA("\nTHERE\n");
HRESULT hr = S_OK;
if (!m_pRenderTarget) {
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
// Create a Direct2D render target
hr = m_pD2DFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwnd, size), &m_pRenderTarget );
if (SUCCEEDED(hr))
{ // Create a black brush
hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush );
}
}
return hr;
}
HRESULT App::OnRender(HWND hwnd)
{
HRESULT hr;
hr = CreateDeviceResources(hwnd);
if (SUCCEEDED(hr))
{
if (!(m_pRenderTarget->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))
{
m_pRenderTarget->BeginDraw();
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
m_pRenderTarget->FillGeometry(m_pEllipseGeometry, m_pBlackBrush);
hr = m_pRenderTarget->EndDraw();
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
m_pRenderTarget->Release();
m_pRenderTarget = NULL;
m_pBlackBrush->Release();
m_pBlackBrush = NULL;
}
}
}
return hr;
}
제공하는 코드의 양에 대해 사과드립니다. 나는 문서에 쓰여진 것을 정확하게 복사한다고 생각했기 때문에 문제에 대해 완전히 혼란 스럽습니다. 필사에 오류를 범했을 것 같다. 제공 할 수있는 도움에 감사드립니다.
답변
그 이유는 CreateWindow
함수를 사용할 때 추가 매개 변수를 작성하여에 hInstance
의해 획득되어 lpCreateParams
액세스 예외가 발생 했기 때문입니다 .
다음과 같이 코드를 수정하십시오.
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), this);
그리고 그것은 나를 위해 작동합니다.

이러한 종류의 문제는 다음 중 하나를 나타냅니다.
- 클래스 포인터가 파괴되거나 초기화되지 않거나 다른 방식으로 유효하지 않습니다.
- 스택 또는 힙 손상 ( 예 : 버퍼 오버플로 또는 기타 정의되지 않은 동작)이 있습니다.
이제 버퍼 오버플로의 후보 인 클래스에 나타나는 문자열을 살펴 보겠습니다. 아니요, 잘 초기화하고있는 것 같습니다.
좋아, 더 정적 인 분석. 문제가 발생한 곳에서 거꾸로 작업하십시오.
- 누가 전화 했어
CreateDeviceResources
? 그것은OnRender
- 누가 전화 했어
OnRender
? 그것은이었다WM_PAINT
핸들러입니다. - 그 호출은 매우 간단합니다.
pApp->OnRender(hWnd);
- 그래서
pApp
유효합니까? 어디에서 초기화 되었습니까? - 창 긴 포인터에 저장됩니다. 언제 저장됩니까?
그리고 이것은 나를 다음 줄로 인도합니다.
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, PtrToUlong(pApp));
좋아요, 뭐가 비린내 죠? 글쎄요, PtrToULong
제가 사용 해본 함수 (또는 매크로)는 아니지만 그 이름에 따르면 64 비트로 프로그래밍하는 데 익숙하고 ULONG
Windows API 의 유형 이 32 라는 것을 알고 있기 때문에 즉시 의심됩니다. 비트.
그래서 나는 가서 문서를 확인하고 그것이 사실임을 충분히 확신합니다. 뿐만 아니라 64 비트로 이식 할 때 이것이 실제로 프로그램을 중단시키는 일반적인 원인에 대한 여러 글을 발견했습니다.
디버거를 연결 한 경우 이미 여기에 도착했을 수 있으며 액세스 위반이 발생하면 중단됩니다. 그런 다음 호출 스택을 볼 수 있습니다. 그런 다음 pApp
클래스 의 다른 내용을 확인하고 전체 내용이 손상되었음을 확인할 수 있습니다.
이 시점에서 "hey I know this is sketchy"로 이동하고 선을 다음과 같이 변경합니다.
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pApp));
이제 프로그램을 컴파일하고 다시 시도하십시오. 나는 그것이 효과가 있다고 확신합니다. 너무 많이, 그 내기를 기반으로 전체 답변을 작성했습니다.
이 특정 조사 경로에 도달 할 수있는 또 다른 방법 this
은 App
생성자 에서 의 값을 인쇄하고 문제 가 OnRender
발생하면 다시 확인하는 것입니다.