.NET/MAUI
MAUI 안드로이드 앱에서 c++로 제작한 *.so 파일을 찾지 못하는 문제
leo21c
2026. 1. 15. 17:53
c++ dll을 제작해서 윈도우에서 작동하는 것은 dll을 import 해서 정상적으로 작동하는 것까지 확인을 했다.
따라서 DllImport를 사용하는 방식에서 문제가 발생할 것 같지는 않았다.
표준 C 라이브러리로 특별한 기능을 가지고 있는 것도 아니고 함수 하나를 호출해서 그 값을 가지고 오도록 했다.
C++ 소스
#include <cstring> // memcpy 사용을 위해 추가
extern "C" {
/// <summary>
/// [중요] visibility("default") 속성을 추가하여 외부(C#)에서 함수를 찾을 수 있게 함
/// </summary>
__attribute__((visibility("default"))) void GetData(unsigned char* buffer, int len)
{
int copyLen = 8;
unsigned char data[8] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
};
if (len < copyLen) copyLen = len;
// 메모리 복사
memcpy(buffer, data, copyLen);
}
}
계속 함수를 찾지 못해서 visibility("default") 속성을 추가했다. 외부(C#)에서 함수를 찾을 수 있게 한다고 한다.
__attribute__((visibility("default")))
MAUI에서 사용하는 cs 파일에는 아래와 같이 예외 처리를 추가했다.
원인을 찾지 못해서 다양한 방법으로 검사를 하게 했고 최종적으로는 절대경로에서 LoadLibrary를 하도록 한 것이다.
//LibName: 안드로이드는 lib와 .so를 자동 생략하고 찾음
//c++ 안드로이드 dll을 빌드하면 lidTestForAndroidLib.so 파일 이름으로 출력이 된다.
private const string LibName = "TestForAndroidLib";
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl)]
private static extern void GetData([In, Out] byte[] buffer, int len);
try
{
// 안드로이드에서 명시적으로 로드하여 링킹 에러 등 구체적인 원인 파악
// "lib" 접두사와 ".so" 확장자를 제외한 이름 사용
Java.Lang.JavaSystem.LoadLibrary(LibName);
}
catch (Java.Lang.UnsatisfiedLinkError ule)
{
// 디버깅 정보 추가: 프로세스 아키텍처(32/64bit) 및 단말기 지원 ABI 확인
bool is64Bit = IntPtr.Size == 8;
string supportedAbis = string.Join(", ", Android.OS.Build.SupportedAbis);
// 라이브러리 설치 경로 및 파일 존재 여부 확인
string nativeLibDir = Android.App.Application.Context.ApplicationInfo.NativeLibraryDir;
string libPath = System.IO.Path.Combine(nativeLibDir, "lidTestForAndroidLib.so");
bool fileExists = System.IO.File.Exists(libPath);
if (fileExists)
{
try
{
// 파일이 존재하므로 시스템 검색 경로를 무시하고 절대 경로로 직접 로드 시도
// Java.Lang.JavaSystem.Load(libPath); // Java 계층 크래시 발생으로 제거
NativeLibrary.Load(libPath); // .NET Native API를 사용하여 직접 로드 (dlopen)
}
catch (Exception loadEx)
{
// 절대 경로 로드조차 실패하면 의존성 문제일 가능성이 높음 (예: libc++_shared.so 누락)
throw new InvalidOperationException($"[Native Load Error] Absolute Load failed: {loadEx.Message}. Path: {libPath}. Original: {ule.Message}", ule);
}
}
else
{
// 파일 자체가 없는 경우
throw new InvalidOperationException($"[Native Load Error] {ule.Message}. Process 64-bit: {is64Bit}, Supported ABIs: {supportedAbis}. Path: {libPath}, Exists: {fileExists}. (Hint: If using Fast Deployment, ensure <Abi> metadata is set in .csproj)", ule);
}
}
위와 같이 해서 실제 안드로이드에서 실행을 했을 때 MSVS 디버깅 화면에서 예외를 표시하도록 해서 문제를 찾도록 했다.
- LoadLibrary가 실패하여 catch 블록으로 진입합니다.
- fileExists가 True이므로 Java.Lang.JavaSystem.Load(libPath)를 실행합니다.
- 이 함수는 경로를 명시했으므로 검색 과정 없이 해당 파일을 즉시 로드합니다.
- 로드가 성공하면 이후 GetKeyData(DllImport) 호출 시 이미 메모리에 로드된 라이브러리를 사용하여 정상 작동합니다.
- 만약 여기서도 실패한다면, 에러 메시지가 dlopen failed: library "libc++_shared.so" not found와 같이 구체적인 의존성 누락 메시지로 바뀔 것입니다.
- NativeLibrary.Load(libPath):
이 함수는 C#(.NET)에서 직접 안드로이드 시스템의 dlopen 함수를 호출하여 해당 경로의 .so 파일을 메모리에 로드합니다. Java 런타임(ART)의 클래스 로더를 거치지 않으므로 NullPointerException이 발생하지 않습니다. - DllImport 연결:
NativeLibrary.Load로 라이브러리가 메모리에 성공적으로 로드되면, 이후 호출되는 GetKeyData(DllImport)는 이미 로드된 라이브러리 핸들을 자동으로 찾아 사용하게 됩니다.
LIST