Activiz에서 vtk 의 객체를 사용하면서 GC의 동작을 예상하지 않고 구현을 하게 되면 발생할 수 있는 에러이다.
이전에도 정리한 글이 있었으나, 의도를 제대로 파악하기 쉽지 않은 것 같아서 다시 정리한다.
VTK는 command, event 를 기반으로 renderingwindow 를 제어한다.
그런데 renderwindow 의 renderwindowinteractor를 사용하여 이벤트가 발생 할 때에 참조할 vtk 객체가 Garbage collector에 의해서 메모리가 해제가 되버리는 경우에는 아래와 같이 에러가 발생한다.
구현하면서 발생했던 실제 코드를 통해서 분석해보자.
아래는 WPF에서 windowformhost를 이용하여 renderwindow를 생성하고 button 클릭할때 실행되는 코드이다.
private void SetInteractorStyleEvent_Click(object sender, RoutedEventArgs e)
{
vtkDICOMImageReader dcmReader = vtkDICOMImageReader.New();
dcmReader.SetDirectoryName(@"C:\Users\richard\GitHub\DATA\patdcm");
dcmReader.Update();
var spacing = dcmReader.GetPixelSpacing();
Console.WriteLine($"Dicom space : {spacing.Select(d => d.ToString())}");
vtkRenderWindow renderWindow = _renderControl3.RenderWindow;
vtkRenderer renderer = renderWindow.GetRenderers().GetFirstRenderer();
renderer.SetBackground(0, 0, 0);
imageViewer = vtkImageViewer2.New();
imageViewer.SetRenderWindow(renderWindow);
imageViewer.SetRenderer(renderer);
iImgView = imageViewer.GetRenderWindow().GetInteractor();
imageViewer.SetInputConnection(dcmReader.GetOutputPort());
var dcmImgStyle = new DcmImageInteractorStyle(); // 에러 발생의 이유..
dcmImgStyle.SetImageViewer(imageViewer);
var renWinStyleAft2 = iImgView.GetInteractorStyle();
Console.WriteLine($"{renWinStyleAft2}");
vtkObject baseObj = imageViewer.GetInput();
var ss = baseObj.ToString();
imageViewer.Render();
}
전체적으로 볼때는 전혀 문제가 되지 않는 코드이며, 우선 코드의 동작을 간단하게 설명한다면..
DcmImageInteractorStyle 클래스는 내가 구현한 dicom image를 slice 제어하는 interactor style 클래스이다.
style은 imageViewer를 입력받아서 내부에서 renderinwindow의 interactor에 style를 등록하고 마우스 휠 이벤트를 이용해서 dicom 이미지의 slice를 제어한다.
말했다시피 처음 동작에서는 문제가 되지 않는다.
하지만, 위 코드를 버튼 이벤트로 실행되는 코드이기 대문에 내부변수는 GC에 의해서 메모리 해제가 된다.
그러나 해당 style은 WPF의 control에 등록되어 있는 renderwindow의 interactor에 등록되어 있기 때문에 메모리가 해제 된것을 모르고 마우스 이벤트 핸들러를 실행하게 된다.
그런 이유때문에 이미 해제된 메모리에 접근을 하게되면서 에러가 발생한다.
이와 같은 이유로 vtk에서는 메모리 에러가 발생하기 쉽다.
그럼 해결 방안은 무엇인가??
말그래도 GC에서 해당 변수를 메모리 해지 하지 않도록 필드로 선언하면 된다.
DcmImageInteractorStyle dcmImgStyle;
private void SetInteractorStyleEvent_Click(object sender, RoutedEventArgs e)
{
...생략...
dcmImgStyle = new DcmImageInteractorStyle();
...생략...
}