I have a C# program that loads an unmanaged C++ DLL with LoadLibrary and later unloads it with FreeLibrary. The application has been functioning normally and I'm able to load, unload, and file-level replace the DLL at will from within the C# application.
For some (new) reason, now when I FreeLibrary the DLL, it successfully unloads from C# (I get a positive return value from FreeLibrary), but the *.dll file is still locked by the file-system and I'm unable to update it newer versions (which was the whole reason for using dynamic loading).
The application uses Boost ASIO with Beast websockets, and the recent change I made was to switch to SSL. I figure there's something wrong with how I'm invoking or closing the SSL connection that's keeping the DLL locked by the filesystem. But since FreeLibrary returns success (and subsequent calls to FreeLibrary hang), I am unsure why the DLL file would still be locked.
Here's how I load the DLL in C# (snippets):
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern bool FreeLibrary(IntPtr hModule);
IntPtr Handle;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void Initdb();
Initdb InitdbFunction;
Main
Handle = LoadLibrary("Perks.dll");
if (Handle == IntPtr.Zero) {
int errorCode = Marshal.GetLastWin32Error();
throw new Exception(string.Format("Failed to load library (ErrorCode: {0})", errorCode));
}
IntPtr funcaddr;
funcaddr = GetProcAddress(Handle, "Initdb");
InitdbFunction = Marshal.GetDelegateForFunctionPointer(funcaddr, typeof(Initdb)) as Initdb;
Unload function
if (FreeLibrary(Handle))
{
Console.WriteLine("FreeLibrary true");
}
else
{
Console.WriteLine("FreeLibrary false");
}
Handle = IntPtr.Zero;
My new implementation of Boost ASIO and Beast websockets in the unmanaged DLL (which is probably causing the problem somehow) is here C++ (snippets)
std::string wsm_SERVER_URL;
boost::asio::io_service wsm_ios;
boost::asio::io_service::work wsm_work{ wsm_ios };
boost::asio::ip::tcp::resolver wsm_r{ wsm_ios };
boost::asio::ip::tcp::socket wsm_sock{ wsm_ios };
std::unique_ptr<beast::websocket::stream< boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>> wsm_client;
boost::asio::ssl::context wsm_ctx{ boost::asio::ssl::context::sslv23 };
Initialize
wsm_client = std::make_unique<beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>>(wsm_ios, wsm_ctx);
Use
wsm_client = std::make_unique<beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>>(wsm_ios, wsm_ctx);
boost::asio::ip::tcp::resolver::iterator iter = wsm_r.resolve(boost::asio::ip::tcp::resolver::query{ wsm_SERVER_URL, "https" });
const boost::asio::ip::tcp::endpoint EP = iter->endpoint();
boost::system::error_code EC;
wsm_client->next_layer().next_layer().connect(EP, EC);
wsm_client->next_layer().set_verify_mode(boost::asio::ssl::verify_none);
wsm_client->next_layer().handshake(boost::asio::ssl::stream_base::client);
wsm_client->handshake(wsm_SERVER_URL, srv_url);
Deactivate (called in C++ just before C# does the FreeLibrary portion)
wsm_client->next_layer().next_layer().close();
wsm_client->next_layer().next_layer().cancel();
wsm_client->close(beast::websocket::close_code::normal);
wsm_client.reset();
wsm_ios.stop();