Java 코드로 C# dll 호출하기 (JNA 활용)

주싱·2022년 11월 1일
1

더 나은 도구

목록 보기
3/13
post-custom-banner

들어가며

특정 외부 시스템을 제어할 일이 생겼는데 C++/C# 언어만 지원합니다. 저희는 Spring Boot 기반 Java 언어를 사용하고 있습니다. 그래서 외부 시스템을 제어하는 C# dll을 생성하고 저희 프로젝트에서는 JNA (Java Native Access)를 사용해 C# dll과 연결해 주기로 합니다. 결과적으로 설정은 간단하지만 몇 가지 삽질한 부분들이 있어서 애를 먹었습니다. 다음에 똑같은 실수를 할지 모르는 저를 위해 절차를 정리해둡니다. 내용은 크게 C# dll 생성 부분과 Java JNA 연결 부분으로 나누어 집니다.

1. C# dll 생성

Visual Studio에서 Class Library (.NET Framework) 템플릿 프로젝트를 생성하고 DLL 파일을 생성합니다.

Class Library (.NET Framework) 템플릿 프로젝트를 생성

  • File > New > Project 메뉴를 선택합니다.
  • ‘Create a new Project’ 창에서 다음 옵션을 선택하고 ‘Class Library (.NET Framework) 프로젝트를 생성합니다.
    • Language - C#
    • Platform - Windows
    • Type - Library

※ 주의사항

  • Class Library 템플릿 선택 시 .NET Core 기반 템플릿이 생성되게 되는데 다음 단계에서 dll export를 위해 설정할 ‘UnmanagedExports’ 패키지와 호환되지 않음으로 주의해야 합니다.
This package may not be fully compatible with your project.

UnmanagedExports 의존 패키지 추가

  • Tools > NuGet Package Manager > Manage NuGet Packages for Solution 메뉴를 선택합니다.
  • Browse 탭에서 ‘UnmanagedExports’ 패키지 검색 후, 설치하려는 프로젝트를 선택하고 설치합니다.

※ 주의 검색 시 Package source에서 All 설정합니다.

컴파일 설정

  • Project Properties > Build > Platform target > x64로 설정합니다.

Export DLL 코드

  • using 키워드로 RGiesecke.DllExport를 추가합니다.
  • Export하려는 함수 선언 위에 [DllExport] 태그를 붙여줍니다.
  • 이제 빌드를 통해 DLL을 생성할 수 있습니다.
using RGiesecke.DllExport;

public class Controller
{
    [DllExport]
    public static string hello()
    {
        return "Hello Dll";
    }
}

Export DLL 확인

생성된 DLL에서 원하는 함수가 정상적으로 Export 되었는지 확인하기 위해서는 Dependency Walker라는 툴을 사용하곤 했는데 최신 버전에서 제대로 동작하지 않는 문제가 있습니다. 조금 찾아보니 동일한 기능을 제공하고 GitHub에 공개된 lucasg/Dependencies 라는 오픈소스가 있습니다. 해당 파일을 다운로드 받아실행하고 Dll을 열어보면 원하는 함수가 DLL에 Export 되어있는 것을 확인할 수 있습니다.

2. Java JNA 연결

Spring Boot 기반 Java 프로젝트에서 JNA를 활용하여 C# dll을 호출합니다.

DLL 복사

Visual Studio에서 빌드한 dll 파일을 Java (Spring Boot) 프로젝트 resources 디렉토리에 복사합니다.

※주의 - 이때 빌드된 dll 파일 이름을 변경하면 이후에 dll을 정상적으로 사용할 수 없습니다.

Library 인터페이스 정의

JNA에서 제공하는 Library 인터페이스를 확장한 dll 인터페이스를 정의합니다.

import com.sun.jna.Library;

public interface TestLibrary extends Library {
    String hello();
}

DLL과 연결하기

이제 실제 dll을 로드하고 dll과 브리지 역할을 할 클래스를 정의합니다. 해당 클래스는 스프링 빈으로 등록하고 빈 생성 후에 dll을 로드하도록 합니다.

import com.sun.jna.Native;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
public class TestLibBridge {
    private TestLibrary library;

    @PostConstruct
    public void load() {
        library = Native.load("/Test.dll", TestLibrary.class);
    }

    public String hello() {
        return library.hello();
    }
}

테스트 코드

테스트코드로 기능을 확인합니다.

import spring.jna.TestLibBridge;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(classes = {
    TestLibBridge.class,
})
public class DllTest {
    @Autowired
    TestLibBridge bridge;
    
    @Test
    void hello() {
        assertThat(bridge.hello()).isEqualTo("Hello Dll");
    }
}

서로 근본이 다른 두 가지가 만나는 것은 역시 까다롭고 어렵네요.

profile
소프트웨어 엔지니어, 일상
post-custom-banner

0개의 댓글