네이티브 C 코드, C++/CLI 및 C#을 사용하는 .NET 응용 프로그램에서 힙 손상 문제가 발생합니다. 이것은 처음으로 잡초에 실제로 들어가는 처음입니다.C++/CLI에서 관리되지 않는 포인터를 래핑 할 때 힙 손상이 발생했습니다.
응용 프로그램의 구조는 GUI 및 전반적인 제어 흐름을위한 C#, 네이티브 C 함수 래핑을위한 C++/CLI 및 데이터 처리를위한 네이티브 C 함수입니다. 이러한 네이티브 C 함수는 일반적으로 배열에 대한 네이티브 포인터 (예 : int *) 및 차원을 입력으로 받아들입니다. C++/CLI는 이러한 저수준 함수를 상위 수준 결합 처리 함수로 랩핑하고 C#은 상위 수준 함수를 호출합니다.
가끔은 C# 수준에서 관리되지 않는 메모리를 할당 한 다음 동일한 소포를 다른 C++/CLI 함수에 전달해야합니다.
내 C# 및 C++/CLI 계층을 통해 이러한 배열을 자유롭게 전달하기 위해 관리되는 포인터 주위에 씬 (thin) 래퍼 클래스를 만들었습니다.
template <typename T>
public ref class ContiguousArray
{
public:
ContiguousArray<T>(int size)
{
_size = size;
p = (T*) calloc(_size,sizeof(T));
}
T& operator[](int i)
{
return p[i];
}
int GetLength()
{
return _size;
}
~ContiguousArray<T>()
{
this->!ContiguousArray<T>();
}
!ContiguousArray<T>()
{
if (p != nullptr)
{
free(p);
p = nullptr;
}
}
T* p;
int _size;
};
// Some non-templated variants of ContiguousArray for passing out to other .NET languages
public ref class ContiguousArrayInt16 : public ContiguousArray<Int16>
{
ContiguousArrayInt16(int size) : ContiguousArray<Int16>(size) {}
};
내가 몇 가지 방법이 래퍼 클래스를 사용하십시오 C++/CLI 계층에서 정의 ContiguousArray라는이 래퍼는, 다음과 같이 보입니다.
사용 사례 1 (C++/CLI)
{
// Create an array for the low level code
ContiguousArray<float> unmanagedArray(1024);
// Call some native functions
someNativeCFunction(unmanagedArray.p, unmanagedArray.GetLength());
float* unmanagedArrayPointer = unmanagedArray.p;
anotherNativeCFunction(unmanagedArrayPointer, unmanagedArray.GetLength());
int returnCode = theLastNativeCFunction(unmanagedArray.p, unmanagedArray.GetLength());
return returnCode;
} // unmanagedArray goes out of scope, freeing the memory
사용 사례 2 (C++/CLI)
{
// Create an array for the low level code
ContiguousArray<float>^ unmanagedArray = gcnew ContiguousArray<float>(1024);
cliFunction(unmanagedArray);
anotherCLIFunction(unmanagedArray);
float* unmanagedArrayPointer = unmanagedArray->p;
int returnCode = nativeFunction(unmanagedArrayPointer, unmanagedArray->GetLength());
return returnCode;
} // unmanagedArray goes out of scope, the garbage collector will take care of it at some point
사용 사례 3 (C 번호)
{
ContiguousArrayInt16 unmanagedArray = new UnmanagedArray(1024);
cliFunction(unmanagedArray);
unmanagedArray = anotherCLIFunctionThatReplacesUnmanagedArray(unmanagedArray); // Unmanaged array is possibly replaced, original gets collected at some point
returnCode = finalCLIFunction(unmanagedArray);
// Do something with return code like show the user
} // Memory gets freed at some point
나는이 래퍼 클래스를 사용하여 관리되지 않는 메모리를 처리하는 것에 매우주의를 기울이고 있다고 생각했지만 힙 손상 및 액세스 위반 문제가 계속 발생합니다. 내 신청서에. ContiguousArray 객체가 유효한 범위 외부의 관리되지 않는 메모리에 대한 기본 포인터를 보관하지 않습니다.
이론상 힙 손상을 일으킬 수있는 세 가지 유스 케이스에 문제가 있습니까? ContiguousArray 구현에서 뭔가 중요한 것이 누락 되었습니까? 나는 가비지 수집가가 지나치게 열중하고 관리 대상을 정리하기 전에 실제로 처리하기에 충분하지 않을까 걱정됩니다.
사례 1 : 닫는 중괄호까지 파이널 라이저를 호출하지 않을 것이라고 확신합니까? .NET에서 객체가 더 이상 사용되지 않으며 내부 메모리에 대한 포인터가있는 동안 정리가 가능하다고 결정할 수 있습니까? GC :: KeepAlive를 스택 객체에 사용해야합니까?
사례 2 : 세 번째 함수 호출 전에 개체가 삭제되지 않도록하려면 GC :: KeepAlive가 끝에 있어야합니까? 내가 대신 쓴다면 여전히 필요합니까? nativeFunction (unmanagedArray-> p, unmanagedArray-> GetLength());
사례 3 : 여기에 이상한 것을 볼 수는 없지만 뭔가 누락 된 것일 수 있습니까?
http://www.pinvoke.net을보십시오. 모든 원시 dll의 샘플을 제공합니다. – jdweng
내가 호출하는 C 함수는 Windows DLL을 빌드하지 않고 컴파일하는 코드입니다. 나는 네이티브 함수를 호출하기 위해 pinvoke를 사용하고 싶지는 않지만, 고성능은이 함수를 랩핑하는 것이다. –
분명히 잘못된 코드입니다. 파이널 라이저는 네이티브 코드가 실행되는 동안 실행할 수 있습니다. 프로그램의 다른 스레드가 가비지 수집을 트리거 할 때 발생합니다. GC :: KeepAlive() 사용하여 해결 방법이 있지만 훨씬 더 나은 있습니다 : deterministically 배열을 파괴하십시오. C# 코드에서'use unmanagedArray;'를 사용하거나 스택 의미를 사용하십시오. –