[유니티/C#] C++ dll과 데이터 주고받기

프로그래밍/유니티2018. 4. 17. 14:19

안녕하세요. 개발자 드리머즈입니다.


유니티에서 사용되는 C++로 만든 DLL(Plugin)과 데이터 통신이 필요했습니다.

http://thomasmountainborn.com/2017/03/05/unity-and-opencv-part-three-passing-detection-data-to-unity/의 가이드를 참고해서 했으나 잘 되지 않았습니다.

C++에서 변수를 초기화했음에도 유니티에서 전달받은 값이 모두 0으로 나왔습니다.


위의 가이드에서 유니티(C#)과 DLL(C++)의 데이터 전달의 핵심 코드는 아래와 같습니다.



DLL로 만들어지는 C++ 소스 핵심 부분 발췌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Declare structure to be used to pass data from C++ to Mono.
struct Circle
{
    Circle(int x, int y, int radius) : X(x), Y(y), Radius(radius) {}
    int X, Y, Radius;
};
 
extern "C" void __declspec(dllexport) __stdcall Detect(Circle* outFaces, int maxOutFacesCount, int& outDetectedFacesCount)
{
    ...생략...
 
    
    // Draw faces.
    for (size_t i = 0; i < faces.size(); i++)
    {
        ...생략...
        // Send to application.
        outFaces[i] = Circle(faces[i].x, faces[i].y, faces[i].width / 2);
        ...생략...
    }
    ...생략...
 }
cs

C++소스에서 데이터 전달에 사용될 Circle 구조체를 만들고, detect함수에서 인자로 전달받은 Circle 포인터인 outFaces에 접근하여 이를 생성자를 사용하여 초기화 합니다.


유니티 C# 소스 핵심 부분 발췌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Define the structure to be sequential and with the correct byte size (3 ints = 4 bytes * 3 = 12 bytes)
[StructLayout(LayoutKind.Sequential, Size = 12)]
public struct CvCircle
{
    public int X, Y, Radius;
}
 
public class OpenCVFaceDetection : MonoBehaviour
{
    private CvCircle[] _faces;
    ...생략...
    void Start()
    {
        ...생략...
        _faces = new CvCircle[_maxFaceDetectCount];
        ...생략...
    }
 
    void Update()
    {
        ...생략...
        unsafe
        {
            fixed(CvCircle* outFaces = _faces)
            {
                OpenCVInterop.Detect(outFaces, _maxFaceDetectCount, ref detectedFaceCount);
            }
        }
        ...생략...
    }
}
cs

유니티(C#)에서는 C++선언한 Circle과 동일한 구조의 CvCircle 구조체를 만들고 C#에서 CvCircle 배열 _faces를 만듭니다.

15번째 코드에서 _faces 배열에 메모리 할당을 합니다. (값 초기화는 안되는 것 같음)

그리고 Update()함수에서 unsafe와 fixed 블럭?을 사용하여 C#에서 기본적으로 사용하지 못하는 포인터를 C++ 코드에 전달하게 됩니다.


내 코드의 문제점

C++을 대학생 때 잠깐 사용한 것 말고는 사용을 하지 않아 C++ 문법을 잘 몰랐던게 원인이었습니다.


위에 표시한 C++코드의 18번째 라인을 다시 가져오겠습니다.


outFaces[i] = Circle(faces[i].x, faces[i].y, faces[i].width / 2);


여기서 잘보면 Circle의 생성자를 사용하여 객체를 초기화하고 있습니다. 그런데 잘 보면.. new 키워드가 없습니다.

제가 작성한 코드는 아래와 유사했는데 중요한 부분은 new 키워드를 썼다는 것입니다.


outFaces+i = new Circle(faces[i].x, faces[i].y, faces[i].width / 2);


이렇게 C++코드에서 new를 쓰게되면 C++코드 영역에 새로운 메모리가 할당되는 것 같습니다. 그래서 그 새로 할당받은 메모리는 C#에서 할당받은 기존 메모리의 주소와 다르므로.. 초기화가 되지 않는 그 기존 메모리 영역의 변수값들을 찍어봤자 0의 값만 나왔던 것입니다.


혹시 저와 비슷한 증상을 껶는 분들은 new 키워드를 살펴보세요.

new 키워드 없이 생성자만 사용하여 값을 초기화하는 경우에는 이미 할당한 메모리 공간의 값만 초기화 하는 것으로 보입니다. 신기하네요.



작성자

Posted by 드리머즈

관련 글

댓글 영역