Java 프로젝트에서 JNA(Java Native Access)를 활용하여 C# dll 코드에 접근하고 있습니다. 외부라이브러리에서 C#, C++ 코드만 지원해 주었기 때문입니다. 그 중에 아래와 같은 코드가 있는데 Java 프로젝트에서 DLL 함수를 호출하면 Invalid memory access 오류가 발생합니다. 코드를 살펴보아도 특별한 문제가 없어 보입니다. 차근히 원인을 찾아서 해결해 봅니다.
java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:418)
at com.sun.jna.Function.invoke(Function.java:361)
Java 코드에서 문제가 발생했으면 호출 스택을 통해서 어느 파일, 어느 코드에서 문제가 발생했는지 쉽게 찾을 수 있었겠지만 라이브러리로 로드한 C# dll 코드에서 문제가 생겼습니다. 원시적인 방법이지만 C# dll 코드를 Binary Search 하듯 중간 중간 찍어가며 문제의 코드 라인을 찾아갑니다. 문제의 범위를 좁혀나가다 보니 아래 RSM이라는 외부 라이브러이 객체 생성 중에 문제가 발생합니다.
public bool Connect(int id, string targetIp) {
this.id = id;
probe = new RSM(); // <--- 문제가 발생한 코드
probe.URL = targetIp;
return true;
}
어느 코드에서 죽는다는 것은 알았지만 왜 해당 객체를 생성하는 중에 죽는지 아무런 힌트가 제공되지 않습니다. 구글링을 하다 보니 유사한 여러 이야기들이 있어서 여러가지를 시도해 봅니다. JNA 버전도 최신 버전으로 올려보고, DLL 코드의 호출 컨밴션 관련 설정도 바꾸어 보지만 문제가 해결되지 않습니다. 한참을 헤메인 이 후 아차 싶은게 있습니다. 제가 예외를 캐치해 볼 생각을 안했습니다. 다급히 문제가 된 코드를 예외 캐치 구문으로 감싸서 코드를 다시 실행해 보니 아래와 같이 문제를 한정할 수 있는 정보를 제공해 줍니다. 예외 메시지를 읽어보면 RSM 이라는 객체를 제공하는 VisionAPI 라는 라이브러리 모듈 로드 중 라이브러리 파일을 찾지 못해서 예외가 발생했다고 합니다.
Exception : System.IO.FileNotFoundException: 파일이나 어셈블리 'VisionAPI, Version=2022.9.1.10, Culture=neutral, PublicKeyToken=null' 또는 여기에 종속되어 있는 파일이나 어셈블리 중 하나를 로드할 수 없습니다. 지정된 파일을 찾을 수 없습니다.
파일 이름: 'VisionAPI, Version=2022.9.1.10, Culture=neutral, PublicKeyToken=null'
그러고 보니 제가 직접 개발한 DLL만 Java 프로젝트와 연결시켜 주고, DLL 내부에서 사용하는 외부 라이브러리 DLL 파일은 가져오지 않아서 문제가 되었습니다. 문제를 찾고보면 이렇게 간단한 실수에서 비롯된 경우가 많습니다. 문제가 된 외부 라이브러리 DLL 파일을 실행파일 경로로 가져와서 문제가 해결되었습니다.
그런데 사실 이것을 한 번에 적용할 수 있었던 것은 아닙니다. 문제의 해결책 후보는 3가지 였습니다. 가장 간단하게는 DLL 파일을 실행파일 경로에 넣어주는 방법이 있었고 DLL을 운영체제 시스템 폴더에 넣어주거나 또는 시스템 환경 변수에 DLL 있는 현재있는 경로를 추가해 주는 방법도 있었습니다. 그중에 가장 단순한 방법인 실행 파일 경로에 DLL 파일을 복사해 주는 작업을 해주기로 했는데 벽을 만났습니다. 이런 질문들이 떠올랐습니다. ‘Java 프로젝트를 빌드하면 생성되는 실행파일은 뭐지? Java 프로젝트는 빌드하면 jar 파일이 생성되는데 jar 파일이 실행파일인가?’ 살짝 얼굴이 붉어지는 질문입니다. 잠깐의 삽질 이후에 jar 파일은 실행파일이 아니며 java.exe 파일의 인자로 넘기는 존재임을 기억합니다. JDK를 설치하고 프로젝트를 생성할 때 설정해준 JDK 설치 경로의 bin 폴더에 있는 java.exe 파일이 현재 우리 프로그램의 실행파일입니다. JDK bin 경로에 라이브러리 파일을 복사해주닌 진짜 문제가 해결되었습니다. 실제로 배포할 때는 다른 방법을 써야겠습니다.
java.exe -jar test.jar
제일 처음에 Java 프로젝트에서 드러났던 ‘Invalid memory access’라는 표면적인 예외는 사실 C#에서 던진 진짜 예외가 Java 프로젝트까지 제대로 처리되지 못해서 발생했던 메시지였습니다. 실제 문제는 라이브러리 파일을 찾지 못했던 것이었습니다. 트러블슈팅을 하다보면 이렇게 실제 문제는 표면적인 문제에 가려져 있는 경우가 많은 것 같습니다. 표면적인 예외 이면에 있는 진짜 이유를 찾는 노력이 필요한 것 같습니다.