Deployment
The user-mode library comes in two pieces, both of which must be deployed along with the application:
- A .NET assembly (managed), named callback.CBFSSync.dll (or callback.CBFSSync.NetStd.dll, for .NET Standard).
- A native dynamic library (unmanaged), named cbfssync24.dll, available for both 32-bit (x86) and 64-bit (x64, ARM64) processor architectures.
When deploying the application, copy both the .NET assembly and the native library to the target system and place them next to the application's executable file (on Windows, it has the .exe extension).
Alternatively, the native library may be placed into one of directories, the paths to which are contained in the
- Windows: PATH environment variable, such as C:\Windows\System32 (or C:\Windows\SysWOW64 when deploying a 32-bit application on a 64-bit Windows system)
- Linux: LD_LIBRARY_PATH environment variable
- macOS: DYLD_LIBRARY_PATH environment variable
Windows:
The .NET assembly may be deployed to the Global Assembly Cache (GAC).
Loading of Native Library
Usually, .NET finds the native library when it is placed next to the assembly. But on some systems, it skips the directory of the assembly or even of the main application module. To ensure that this doesn't happen on a user's system when an application keeps the native library next to the .NET assembly, in the .NET Core 3.1 and .NET 5+ applications, you can use the DllImportResolver function as shown in the following snippet:
public static IntPtr CBSyncImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
bool loaded = false;
IntPtr libHandle = IntPtr.Zero;
if (!libraryName.Contains("callback.CBFSSync"))
return IntPtr.Zero;
//Look for the native library next to the .NET assembly
loaded = NativeLibrary.TryLoad(libraryName, assembly, DllImportSearchPath.AssemblyDirectory, out libHandle);
//Look for the native library in the application's main directory
if (!loaded)
loaded = NativeLibrary.TryLoad(libraryName, assembly, DllImportSearchPath.ApplicationDirectory, out libHandle);
//Look for the native library using teh default .NET behavior
if (!loaded)
loaded = NativeLibrary.TryLoad(libraryName, assembly, searchPath, out libHandle);
return loaded ? libHandle : IntPtr.Zero;
}
...
NativeLibrary.SetDllImportResolver(Assembly.GetAssembly(typeof(CBSync)), CBSyncImportResolver);
This code attempts to load the native library in the directory of the managed .NET assembly, and if not found then attempts to find the library in the application's main directory, and if not found then revert to the default .NET behavior.
Only one DllImportResolver function may be set for the assembly, so doing the above for one component is sufficient.
In the .NET Framework 4.x applications, one can use AppDomain.CurrentDomain.AssemblyResolve event for the same purpose.
NuGet Notes
After the NuGet package is added to a project, both the managed .NET assembly and the unmanaged native library will be copied to the project's output directory anytime the project is built. However, the exact files copied to the output directory for the native library will vary based on the project type:
- For .NET Core projects, a runtimes directory will be created in the output directory (if it does not already exist), and versions of the native library for each supported runtime identifer (RID) (e.g., win-x64) will be placed in the appropriate subdirectories. When the .NET Core application is distributed, the entire runtimes directory should be deployed alongside it.
- For other types of projects (.NET Framework, UWP), only the native library version specific to the currently selected platform target (e.g., x64) will be copied to the output directory.
- For .NET Framework projects specifically, please note that the project's platform target (Project > Properties > Build Tab > Platform target) must be set to a real architecture. If it is set to "Any CPU", no native library will be copied to the output directory.
Windows:
The native library may alternatively be installed to the Windows system directory. This approach allows deploying both the 32-bit and 64-bit versions of the native library simultaneously, because each gets placed into the system directory that corresponds to the appropriate processor architecture.