Search Unity

native android .a plugin fails with "dll not found"

Discussion in 'Android' started by SavedByZero, Jun 4, 2021.

  1. SavedByZero

    SavedByZero

    Joined:
    May 23, 2013
    Posts:
    124
    Hello,

    I successfully compiled an OpenCV plugin for Windows using Visual Studio, but when I changed the platform to ARM and exported it as a .a, my android phone throws a "dll not found exception", even though it's in the Plugins folder right next to the dll (and was formerly in an "Android" subfolder in there, with no more success).
    According to these documents, Unity supports .a files for Android native plugins: https://docs.unity3d.com/2018.4/Documentation/Manual/AndroidNativePlugins.html

    Things I've tried:
    -using the Inspector to set the .a file to Android platform. I tried both ARM64 and ARM7 architecture, and my build publishes for both.
    -throwing every other openCV .a library next to it in the folder and setting it the same way, even though I wouldn't think I would have to since myPlugin.a should have all of that bundled.
    -unzipping the apk. Oddly, I only see some other unrelated .so files for another part of the code; my .a files are nowhere to be seen in the lib folder. Why is this?
     
  2. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,905
    .a extension files are static libraries, which end up as part of shared library. In Unity case, they will end up as part of libil2cpp.so. Thus it's normal that you don't see them in apk.

    Note: static libraries are only supported when Il2Cpp is used, they're not supported with Mono.

    To access functions from static libraries, you do
    Code (CSharp):
    1. [DllImport("__Internal", EntryPoint = "YourFunctionName")]
    2. private static extern <returntype> <SomeFunctioName>(<params>);
    Hope that helps
     
    Last edited: Jun 4, 2021
  3. SavedByZero

    SavedByZero

    Joined:
    May 23, 2013
    Posts:
    124
    Thanks, unfortunately adding that "EntryPoint =" to my DllImport calls didn't fix the problem.
    I exported the plugin from Visual Studio using C++. For Android, I changed the build target to ARM and file to static library (which got me the .a, after I changed the extension from .lib), after installing the ARM toolkit. I'm wondering if I missed something along the way, even though I didn't get any errors. In any case, does the unity/c# code at least look right when compared to the native code?

    I would try dynamic shared library as a target, though the project for some reason won't let me switch to that.

    My c++ code:

    Code (CSharp):
    1.  
    2. struct Circle
    3. {
    4.     Circle(int x, int y, int radius) : X(x), Y(y), Radius(radius) {}
    5.     int X, Y, Radius;
    6. };
    7.  
    8. CascadeClassifier _faceCascade;
    9.  
    10. String _windowName = "OpenCV";
    11.  
    12. VideoCapture _capture;
    13.  
    14. int _scale = 1;
    15.  
    16. extern "C" int __stdcall Init(int& outCameraWidth, int& outCameraHeight, String cascadeFileName)
    17. {
    18.     if (!_faceCascade.load(cascadeFileName))
    19.         return -1;
    20.  
    21.     _capture.open(0);
    22.  
    23.     if (!_capture.isOpened())
    24.         return -2;
    25.  
    26.     outCameraWidth = _capture.get(CAP_PROP_FRAME_WIDTH);
    27.     outCameraHeight = _capture.get(CAP_PROP_FRAME_HEIGHT);
    28.     return 0;
    29. }
    30.  
    31. extern "C" void __stdcall Close()
    32. {
    33.     _capture.release();
    34. }
    35.  
    36. extern "C" void __stdcall SetScale(int scale)
    37. {
    38.     _scale = scale;
    39. }
    40.  
    41. extern "C" void __stdcall Detect(Circle* outFaces, int maxOutFacesCount, int& outDetectedFacesCount)
    42. {
    43.     Mat frame;
    44.     _capture >> frame;
    45.  
    46.     if (frame.empty())
    47.         return;
    48.  
    49.     std::vector<Rect> faces;
    50.  
    51.     Mat grayscaleFrame;
    52.  
    53.     cvtColor(frame, grayscaleFrame, COLOR_BGR2GRAY);
    54.     Mat resizedGray;
    55.     resize(grayscaleFrame, resizedGray, Size(frame.cols / _scale, frame.rows / _scale));
    56.     equalizeHist(resizedGray, resizedGray);
    57.  
    58.     _faceCascade.detectMultiScale(resizedGray, faces);
    59.  
    60.     for (size_t i = 0; i < faces.size(); i++)
    61.     {
    62.         Point center(_scale * (faces[i].x + faces[i].width / 2), _scale * (faces[i].y + faces[i].height / 2));
    63.         ellipse(frame, center, Size(_scale * faces[i].width/2, _scale * faces[i].height / 2), 0, 0, 360, Scalar(0,0,255), 4, 8, 0);
    64.  
    65.         outFaces[i] = Circle(faces[i].x, faces[i].y, faces[i].width / 2);
    66.         outDetectedFacesCount++;
    67.  
    68.         if (outDetectedFacesCount == maxOutFacesCount)
    69.             break;
    70.     }
    71.  
    72.     imshow(_windowName, frame);
    73. }
    And my unity c# code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.Runtime.InteropServices;
    5.  
    6. internal static class OpenCVWrapper
    7. {
    8.     [DllImport("ConfigureOpenCV", EntryPoint="Init")]
    9.     internal static extern int Init(ref int outCameraWidth, ref int outCameraHeight, ref string cascadeFileName);
    10.  
    11.     [DllImport("ConfigureOpenCV", EntryPoint="Close")]
    12.     internal static extern int Close();
    13.  
    14.     [DllImport("ConfigureOpenCV", EntryPoint="SetScale")]
    15.     internal static extern int SetScale(int downscale);
    16.  
    17.     [DllImport("ConfigureOpenCV", EntryPoint="Detect")]
    18.     internal unsafe static extern void Detect(CvCircle* outFaces, int maxOutFacesCount, ref int outDetectedFacesCount);
    19.  
    20.     [StructLayout(LayoutKind.Sequential, Size = 12)]
    21.     public struct CvCircle
    22.     {
    23.         public int X, Y, Radius;
    24.     }
    25.  
    26.  
    27.  
    28.  
    29.  
    30. }
    31.  
     
    Last edited: Jun 4, 2021
  4. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,905
    If you're still using static library, it should be

    Code (CSharp):
    1. [DllImport("__Internal"...
    and not

    Code (CSharp):
    1. [DllImport("ConfigureOpenCV"...
    your C++ code is strange, it has a String class, I assume this is not std::string? Is that C++/CLI, if so, you cannot use it on Android. C# string should marshal to const char * in C++.

    Also you're mentioning that you're compiling your library with Visual Studio, I am not sure what compiler you're using, but it should come from Android NDK. I think you need to use gradle to compile a static library for Android.

    Also, calling convention __stdcall is not needed.
     
  5. SavedByZero

    SavedByZero

    Joined:
    May 23, 2013
    Posts:
    124
    Most of that c++ actually came from an AR book for Unity 2018 by Jesse Glover, I only modified one small part to allow me to pass in my own haar cascade file names. But yeah, I'm finding that book to be kind of a mess in terms of completeness and errata.

    Oddly, I made a fresh Visual Studio C++ solution targeted for Android/C++/dynamic shared libraries (the platform toolset is clang 3.6, and now I can put out .so files), and I've almost recreated it, except now this solution doesn't come equipped with iostream.h or stdio.h for some reason...
     
  6. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,905
    Oh so you've install Android Support for Visual Studio, I haven't used it, but yes, I guess it should work fine for your case. No idea about headers though, I assume stdio.h should definitely exist.
     
  7. SavedByZero

    SavedByZero

    Joined:
    May 23, 2013
    Posts:
    124
    Oh, if I use __Internal in the DllImport, I get crazy errors involving Il2cpp failing to run properly. It begins with this:

    Code (CSharp):
    1. Failed running C:\Program Files\Unity\Hub\Editor\2018.4.20f1\Editor\Data\il2cpp/build/il2cpp.exe --convert-to-cpp --emit-null-checks --enable-array-bounds-check --dotnetprofile="unityaot" --compile-cpp --libil2cpp-static --platform="Android" --architecture="ARMv7" --configuration="Release" --outputpath="C:\Users\mjpg7\niantic-playmatics\nianticprototype\client\Tangram\Temp\StagingArea\assets\bin\Data\Native\armeabi-v7a\libil2cpp.so" --cachedirectory="C:\Users\mjpg7\niantic-playmatics\nianticprototype\client\Tangram\Assets\..\Library\il2cpp_android_armeabi-v7a/il2cpp_cache" --additional-include-directories="C:\Program Files\Unity\Hub\Editor\2018.4.20f1\Editor\Data\PlaybackEngines\AndroidPlayer/Tools\bdwgc/include" --additional-include-directories="C:\Program Files\Unity\Hub\Editor\2018.4.20f1\Editor\Data\PlaybackEngines\AndroidPlayer/Tools\libil2cpp/include" --tool-chain-path="C:/Users/mjpg7/Downloads/android-ndk-r16b-windows-x86_64/android-ndk-r16b" --map-file-parser="C:\Program Files\Unity\Hub\Editor\2018.4.20f1\Editor\Data\Tools\MapFileParser\MapFileParser.exe" --directory="C:\Users\mjpg7\niantic-playmatics\nianticprototype\client\Tangram\Temp\StagingArea\assets\bin\Data\Managed" --generatedcppdir="C:\Users\mjpg7\niantic-playmatics\nianticprototype\client\Tangram\Temp\StagingArea\Il2Cpp\il2cppOutput"
    Nobody here could posssibly diagnose the specifics, but is this known to happen with __internal?
     
  8. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,905
    This is only a title of the error, you need to show the contents of the error. But agian "__Internal" is only needed if you're trying to access a function located in a static library, if it's in a shared library, you must use shared library's name.
     
  9. SavedByZero

    SavedByZero

    Joined:
    May 23, 2013
    Posts:
    124
    So what it seems to be doing is throwing a bunch of errors for another library that's from a partner's common code base unrelated to the area of the code I'm working with. I'm getting:
    Bulk_Blot_2.cpp:15967: error: undefined reference to 'Init'
    Bulk_Blot_2.cpp:15996: error: undefined reference to 'SetScale'
    Bulk_Blot_2.cpp:15985: error: undefined reference to 'Close'

    and some others. All of these are in Temp\StagingArea\Il2Cpp\il2cppOutput

    Why would it be taking the methods from the code from my own plugin and applying them to other (possibly static) plugins in other parts of the code?
     
    Last edited: Jun 5, 2021
  10. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,905
    Those errors mean, that C# code converted to C++ was trying to call those methods, but couldn't find their implementation.
     
    SavedByZero likes this.
  11. bhupiister

    bhupiister

    Joined:
    Dec 13, 2019
    Posts:
    42
    Tomas1856 likes this.
  12. bhupiister

    bhupiister

    Joined:
    Dec 13, 2019
    Posts:
    42
    @Tomas1856 One more bug is that, if you are adding static or shared library (.so or .a) , even if CPU is selected in the inspector, it will not be included into the build apk unless you select the CPU again and click on apply. Unity Editor version 2019.4.28f1
     
  13. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,905
    Could you please submit a bug with exact details on how to reproduce this issue? Thank you
     
    bhupiister likes this.
  14. bhupiister

    bhupiister

    Joined:
    Dec 13, 2019
    Posts:
    42
    I will definitely try to report a bug on this.. There was one more thing i was confused and could not move forward with.. Do you have experience with webgl or can you tag someone with experience on this ?

    I am compiling OpenCascade library for webgl Unity.
    So far I am successful to use this library on Android and Windows, for Webgl its bit tricky.
    1) Unity uses old version of emscripten like 1.38.11 and this version is unable to build this library properly
    2) New version of emscripten build .a files instead of .bc files and i am unable to find how to implement .a files in unity and if I put them in plugin folder and call DllImport with __Internal , it is unable to find.

    I somewhere read that unity, with its new release, is going to use latest version of emscripten. So even if I use new version of emscripten, it will generate .a files. I think its because new versions of emscripten uses upstream.

    Can anyone help me on how to successfully compile this library with emscripten ?
     
  15. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,905
    I would suggest creating a new thread in https://forum.unity.com/forums/webgl.84/
     
  16. bhupiister

    bhupiister

    Joined:
    Dec 13, 2019
    Posts:
    42