c#에서 cpp dll 디버깅

안녕하세요.

첫번쨰:
c#에서 cpp dll 호출해서 사용중입니다.

비주얼 스튜디오 설정을 변경해서 cpp에서 브레이크 포인트는 잡힙니다,

그런데 c#에서 dll 함수 ctrl+F12 하면 구현으로 이동이 안됩니다.
구체적으로 var cppResult = ForTest(100000000);
여기의 ForTest에서 ctrl+F12해서 cpp구현으로 이동하고 싶습니다.
답변감사합니다.

using System.Runtime.InteropServices;

namespace FunctionsDllTest
{
    class Program
    {
        [DllImport("FunctionsDllD.dll")]
        public static extern double ForTest(int length);

        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
            var cppResult = ForTest(100000000);
            Console.WriteLine("CPP 결과 : " + cppResult + "ms");

            var csResult = CSharpForTest(100000000);
            Console.WriteLine("CS 결과 : " + csResult + "ms");
        }

        public static double CSharpForTest(int length)
        {
            DateTime startTime = DateTime.Now; // 시작 시간 기록
            int j = 0;
            for (int i = 0; i < length; i++)
            {
                j++;
            }
            DateTime endTime = DateTime.Now; // 종료 시간 기록

            TimeSpan elapsedTime = endTime - startTime; // 경과 시간 계산

            double elapsedMilliseconds = elapsedTime.TotalMilliseconds; // 경과 시간을 밀리초로 얻음

            return elapsedMilliseconds;
        }
    }
}

#include "pch.h"
#include "Functions.h"
#include <chrono>
#include <ratio>

double ForTest(int length)
{
	auto start_time = std::chrono::high_resolution_clock::now();
	int j = 0;
	for (int i = 0; i < length; i++)
	{
		j++;
	}
	auto end_time = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double, std::milli> elapsed_time = end_time - start_time;

	double elapsed_ms = elapsed_time.count();

	return elapsed_ms;
}

두번쪠
외부 dll 을 다시 dll 랩핑하고 c#에서 호출해서 쓰는 경우는 어떻게 다를까요?
브레이크 포인트도 안잡힙니다.

이렇게 된 이유는 외부 dll이 cpp 에서 사용하던 dll입니다.
네임 맹글링이 되어있어서 cpp로 다시 dll 랩핑하고 c#에서 사용합니다.

ctrl+F12는 아니지만, 해당 행에 중단점 걸어도 안들어가지나요?

첫번쨰 경우는 cpp 중단점으로 갑니다.
두번쨰 경우는 cpp 중단점으로 안갑갑니다.
소스를 찾으라고 파일탐색기가 뜨네요. 파일 찾아도 이거 아니라고,

코드만 봤을 때는, ForTest가 DLL 외부 function table에 등록되지 않은 것처럼 보입니다. C#에서 DLL에 노출된 함수를 사용하려면, 다음과 같이 헤더부나 본문에 선언이 되어있어야 할 것 같습니다.

#ifdef __cplusplus
extern "C" {
#endif

__declspec(dllexport) double ForTest(int length);

#ifdef __cplusplus
}
#endif

extern "C"를 붙이지 않으면, VC++ 컴파일러가 VC++ 컴파일러끼리만 알아볼 수 있도록 이름을 겹치지 않게 변조 (mangling)해서 함수 테이블에 내보냅니다. 이렇게 되면, C#에서 VC++ mangling 프로토콜을 그대로 재현해야 해서 참조하기 매우 힘들어지기 때문에, 다른 mangling을 하지 말고 있는 그대로 내보내라는 뜻에서 extern "C"를 붙이게 됩니다.

그리고 C# 측에서도 가능한한 자세히 DllImportAttribute를 써주시는게 좋습니다.

[DllImport("FunctionsDllD.dll", EntryPoint="ForTest", ExactSpelling = true, CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
public static extern double ForTest(int length);

C# 역시 DllImport를 붙일 때 정확한 속성을 지정해주지 않을 경우 재량껏 이름을 변형하는 동작이 있어, 그렇게 하지 않도록 명시적으로 지정해주는 것이 혼란을 예방하는데 좋습니다. (다소 거추장스러울 수는 있지만 유익합니다.)

그리고 생성된 DLL이 실제로 C#에서 쓸 수 있는 export를 내보내고 있는지 확인하려면 GitHub - lucasg/Dependencies: A rewrite of the old legacy software "depends.exe" in C# for Windows devs to troubleshoot dll load dependencies issues. 같은 도구를 사용해서 DLL 파일을 점검해보시는 것을 강력히 추천합니다. DependenciesGui.exe를 이용해서 다음과 같이 DLL 내역을 보실 수 있게 되어있습니다.

이 도구 이외에도 Dependency Walker를 쓰실 수도 있습니다. (다만 이 도구는 예전 Visual Studio에 기본 내장되어있던 도구였으나 Windows OS가 DLL 체계를 리팩토링하면서 제대로 동작하지 않는 문제가 발생하기도 합니다.)

5개의 좋아요

답변 감사합니다.

extern “c” 는 헤더 파일에서 했습니다

c# 에서 dll 참조 하는 함수를 눌렀을떄
dll 참조 하는 함수의 구현으로 바로 가게 설정하고 싶어요
그리고 c#에서 dll프로젝트를 참조 하고 빌드 하면
c# 실행 파일 생성 하는 폴더에 dll까지 빌드되서 들어오는데
dll 파일명이 c# 프로젝트 명.dll 이렇게 되는데
dll프로젝트명.dll 이렇게 하고 싶은데 어떻게 해야 하나요?

C#의 P/Invoke 선언을 C/C++ 측 심볼로 연결해서 탐색 이동하는 기능이 따로 있지는 않은 것으로 알고 있습니다. 다만 디버깅할 때 PDB 파일을 정확히 불러올 수 있도록 만든다면 스택 역추적을 할 때 도움을 받으실 수는 있습니다.

C# 프로젝트의 출력 파일명 변경은 프로젝트 설정에서 어셈블리 이름 속성을 바꾸는 것으로 목적을 달성할 수 있을겁니다.