UnmanagedLibrary.cs 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // Copyright 2015 gRPC authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. using System.Diagnostics.CodeAnalysis;
  15. using System.Runtime.InteropServices;
  16. namespace Unix.Terminal;
  17. /// <summary>
  18. /// Represents a dynamically loaded unmanaged library in a (partially) platform independent manner. First, the
  19. /// native library is loaded using dlopen (on Unix systems) or using LoadLibrary (on Windows). dlsym or GetProcAddress
  20. /// are then used to obtain symbol addresses. <c>Marshal.GetDelegateForFunctionPointer</c> transforms the addresses
  21. /// into delegates to native methods. See
  22. /// http://stackoverflow.com/questions/13461989/p-invoke-to-dynamically-loaded-library-on-mono.
  23. /// </summary>
  24. internal class UnmanagedLibrary
  25. {
  26. public readonly string LibraryPath;
  27. public nint NativeLibraryHandle { get; }
  28. //
  29. // if isFullPath is set to true, the provided array of libraries are full paths
  30. // and are tested for the file existing, otherwise the file is merely the name
  31. // of the shared library that we pass to dlopen
  32. //
  33. public UnmanagedLibrary (string [] libraryPathAlternatives, bool isFullPath)
  34. {
  35. if (isFullPath)
  36. {
  37. foreach (string path in libraryPathAlternatives)
  38. {
  39. if (File.Exists (path))
  40. {
  41. LibraryPath = path;
  42. break;
  43. }
  44. }
  45. if (LibraryPath is null)
  46. throw new FileNotFoundException ($"Error loading native library. Not found in any of the possible locations: {string.Join (",", libraryPathAlternatives)}");
  47. NativeLibraryHandle = NativeLibrary.Load (LibraryPath);
  48. }
  49. else
  50. {
  51. foreach (string lib in libraryPathAlternatives)
  52. {
  53. NativeLibraryHandle = NativeLibrary.Load (lib);
  54. if (NativeLibraryHandle != nint.Zero)
  55. {
  56. LibraryPath = lib;
  57. break;
  58. }
  59. }
  60. }
  61. if (NativeLibraryHandle == nint.Zero)
  62. {
  63. throw new IOException ($"Error loading native library \"{string.Join (", ", libraryPathAlternatives)}\"");
  64. }
  65. }
  66. /// <summary>Loads symbol in a platform specific way.</summary>
  67. /// <param name="symbolName"></param>
  68. /// <returns></returns>
  69. public nint LoadSymbol (string symbolName)
  70. {
  71. return NativeLibrary.GetExport(NativeLibraryHandle, symbolName);
  72. }
  73. public T GetNativeMethodDelegate<T> (string methodName)
  74. where T : class
  75. {
  76. nint ptr = LoadSymbol (methodName);
  77. if (ptr == nint.Zero)
  78. {
  79. throw new MissingMethodException (string.Format ("The native method \"{0}\" does not exist", methodName));
  80. }
  81. return Marshal.GetDelegateForFunctionPointer<T> (ptr); // non-generic version is obsolete
  82. }
  83. }