.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 디버깅 화면에서 예외를 표시하도록 해서 문제를 찾도록 했다.

  1. LoadLibrary가 실패하여 catch 블록으로 진입합니다.
  2. fileExists가 True이므로 Java.Lang.JavaSystem.Load(libPath)를 실행합니다.
  3. 이 함수는 경로를 명시했으므로 검색 과정 없이 해당 파일을 즉시 로드합니다.
  4. 로드가 성공하면 이후 GetKeyData(DllImport) 호출 시 이미 메모리에 로드된 라이브러리를 사용하여 정상 작동합니다.
  5. 만약 여기서도 실패한다면, 에러 메시지가 dlopen failed: library "libc++_shared.so" not found와 같이 구체적인 의존성 누락 메시지로 바뀔 것입니다.
  • NativeLibrary.Load(libPath):
    이 함수는 C#(.NET)에서 직접 안드로이드 시스템의 dlopen 함수를 호출하여 해당 경로의 .so 파일을 메모리에 로드합니다. Java 런타임(ART)의 클래스 로더를 거치지 않으므로 NullPointerException이 발생하지 않습니다.
  • DllImport 연결: 
    NativeLibrary.Load로 라이브러리가 메모리에 성공적으로 로드되면, 이후 호출되는 GetKeyData(DllImport)는 이미 로드된 라이브러리 핸들을 자동으로 찾아 사용하게 됩니다.

LIST