I have a win32 API library .dll file for some hardware that I need to access in c#. I believe using DllImport to create a wrapper class for the .dll that makes it usable in my C# uwp application.
The first function I believe have done correctly
(C code)
ADIOLIB_API
aDIO_Error WINAPI aDIO_General_Enum(aDIO_Enum_Info* DeviceList,
uint8 ListSize)
(C# code)
[DllImport("aDIOLib.dll")] static extern aDIO_Error aDIO_General_Enum(ref aDIO_Enum_Info DeviceList, Byte ListSize);
public static aDIO_Error aDIOGeneralEnum(ref aDIO_Enum_Info DeviceList, Byte ListSize)
{
return aDIO_General_Enum(ref DeviceList, ListSize);
}
public struct aDIO_Enum_Info
{
UInt32 dev_num;
aDIO_Intrfc_Avail availability;
UInt32 base_address;
UInt32 irq;
bool has_wake;
}
aDIO_Error is just a UInt32 error code, so that part has been handled as well.
When I moved on to the next function to create a wrapper for it I ran into this issue. So the function header is like this:
ADIOLIB_API
aDIO_Error WINAPI aDIO_General_Open(aDIO_Enum_Info DeviceInfo,
aDIO_Handle* Handle)
After thinking the aDIO_Handle is probably another struct that I have to replicate in C#, I looked at the declaration for it and found this:
typedef struct
{
/**
Holds a critical section object used to control access to all library
state and functionality.
*/
CRITICAL_SECTION global_lock;
/**
Holds a handle to the aDIO device file. (Used to send IOCTLs to the
driver.)
*/
HANDLE dev_file;
/**
Holds device/board information retrieved from the driver.
*/
aDIO_Intrfc_Board_Info dev_info;
/**
Holds a critical section object used to control access to the error
logging function and the last error message.
*/
CRITICAL_SECTION log_section;
/**
Holds the last un-retrieved error message.
*/
char last_error_msg[ADIO_ERROR_MAX_MSG_LENGTH];
/**
Holds a pointer to the structure holding the state of the Interrupt
module.
*/
void* int_state;
} aDIO_Descriptor, *aDIO_Handle;
Both the HANDLE and CRITICAL_SECTION types are from external dependencies (minwinbase.h and winnt.h) in which this dll has like 100s of external header files. How am i supposed to make a DllImport for this aDIO_General_Open function without recreating all the header files in the process.
Any help would be much appreciated.
Please ask me questions to clarify if you have any! I am new to this DllImport stuff
I have problem with Boost.Interprocess (v1.66) library which I use in my C/C++ library which I use in C# through Marshalling (calling C native code from C#).
I found the problem if I was using Boost.Interprocess named_semaphore for sync between processes. (in open_or_create mode)
If I use my C/C++ lib with another native C/C++ code everything works fine (under newest Windows 10, Linux (4+ kernel) and even Mac OS X (>=10.11)).
The problem occurred under Windows - with C# I have C wrapper around C++ code. If I use Marshalling with simple own-build EXE --> Everything works! But If I use The same C# code (with the same C lib) in the third party application as a DLL plugin I got segfault from get_bootstamp in named_semaphore.
So I have third-party C# SW for which I create plugins (C# DLL). In that plugin I use my C library through marshalling. Marshalling work fine in test C# project (which just call C functions from C lib) but same code segfault in third-party SW.
C Library workflow:
Init all necessary C structures
Start desired TCP server (native C/C++ app) using Boost.Process
Wait for server (through named_semaphore) <-- segfault
Connect to the server...
C# code has same workflow.
Found the problem
The problem occured in boost::interprocess::ipcdetail::get_booststamp (which is called in named_semaphore). here:
struct windows_bootstamp
{
windows_bootstamp()
{
//Throw if bootstamp not available
if(!winapi::get_last_bootup_time(stamp)){
error_info err = system_error_code();
throw interprocess_exception(err);
}
}
//Use std::string. Even if this will be constructed in shared memory, all
//modules/dlls are from this process so internal raw pointers to heap are always valid
std::string stamp;
};
inline void get_bootstamp(std::string &s, bool add = false)
{
const windows_bootstamp &bootstamp = windows_intermodule_singleton<windows_bootstamp>::get();
if(add){
s += bootstamp.stamp;
}
else{
s = bootstamp.stamp;
}
}
If I debug to the line
const windows_bootstamp &bootstamp = windows_intermodule_singleton<windows_bootstamp>::get()
booststamp.stamp is not readable. The size is set to 31, capacity is set to some weird value (like 19452345) and the data is not readable. If i step over to
s += bootstamp.stamp;
the segfault occured!
Found the reason
I debug once more and set debug point to the windows_bootstamp constructor entry and I got no hit so the stamp is not initialized (I guess).
Confirmation
If I change get_bootstamp to
inline void get_bootstamp(std::string &s, bool add = false)
{
const windows_bootstamp &bootstamp = windows_intermodule_singleton<windows_bootstamp>::get();
std::string stamp;
winapi::get_last_bootup_time(stamp);
if(add){
s += stamp;
}
else{
s = stamp;
}
}
Recompile my lib and exe - everything works fine (without any problem).
My question is - what I am doing wrong? I read Boost.Interprocess doc really thoroughly but there are no advice/warnings about my problem (yeah there is "COM Initialization" in Interprocess doc but it not seems helpfull).
Or it's just a bug in Boost.interprocess and I may report it to Boost bug tracker?
Notice - if I start server manually (before I run C# code) It works without segfaults
I need to expose some existing .NET logic (i.e. assembly MyManaged.dll) to native code, so I've decided to create C++/CLI bridge. I've created C++/CLI project and added an reference to MyManaged.dll. Long story in short - it works - I've succeeded to access everything that should be accessible form native code.
But big issue is that my implementation leaks memory. After days of testing and researching I've narrowed problem down to System::String <-> const wchar_t conversion. Finally I've created a trivial C++/CLI project that demonstrates (reproduces) the issue:
#define EXPORTED __declspec(dllexport)
System::String^ ToManaged(const wchar_t* unmanagedString)
{
return gcnew System::String(unmanagedString);
}
const wchar_t* ToUnmanaged(System::String^ managedString)
{
return (wchar_t*) System::Runtime::InteropServices::Marshal::StringToHGlobalUni(managedString).ToPointer();
}
EXPORTED const wchar_t* __stdcall GetString(const wchar_t* dummy)
{
return ToUnmanaged(ToManaged(dummy));
}
(If it isn't obvious from the previous code - I'm pretty new in C++/CLI)
As I've mentioned the code works but accumulates memory consumption so there's definitely a leak in System::String <-> const wchar_t conversion.
My question is obvious: how to implement string conversion without the leak.
Thanks!
UPDATE: Please ignore negative voter - as you can see he even refused to explain what is wrong here. Different people have different motives... One thing is for sure: solution provided here works perfectly, without any memory leak.
I've found the solution (based on Overview of Marshaling in C++ and marshal_context::marshal_as). So the following should be changed:
#include <msclr\marshal.h>
System::String^ ToManaged(const wchar_t* unmanagedString)
{
return msclr::interop::marshal_as<System::String^>(unmanagedString);
}
gcroot<msclr::interop::marshal_context^> context;
const wchar_t* ToUnmanaged(System::String^ managedString)
{
msclr::interop::marshal_context^ unpacked = context;
if (unpacked != nullptr)
delete unpacked;
context = gcnew msclr::interop::marshal_context();
return context->marshal_as<const wchar_t*>(managedString);
}
NOTE: Here I've implemented very clumsy handling of marshal_context instance - when the next call arrives, the result from the previous call is deleted. This implementation would fall apart in multi-threading scenario, so you should implement a better one having the following in mind:
marshal_context instance can be used for multiple calls, but it should be deleted every now and then (to free the memory from previously marshaled strings);
As soon as marshal_context is deleted - all const wchar_t* crated by using it are also deleted. It means that you should not delete context immediately after using it, but you need to provide enough time for calling code to actually get the resulting string.
You need to free the pointer from StringToHGlobalUni after you've used it. Use Marshal.FreeHGlobal or LocalFree.
While not built to your exact api, I think this addresses the memory
HRESULT GetString(BSTR* p_bstrResult, unsigned long* ulErrCode)
{
HRESULT hr = S_OK;
try
{
System::String ^systemstring = gcnew System::String("");
DotNetObject::o = gcnew DotNetObject:: DotNetObjectComponent();
*ulErrCode = (unsigned long)o->GetString(systemstring);
pin_ptr<const wchar_t> wch = PtrToStringChars(systemstring);
_bstr_t bstrt(wch);
*p_bstrResult = bstrt.GetBSTR(); // native client babysits
delete systemstring;
}
catch(Exception ^ ex)
{
}
return hr;
}
I'm calling C++ COM interface from C# code using a dll.
At the C++ side, i have a WCHAR* global variable which is updated through a method with a BSTR parameter.
The problem is that, when i first call the C++ wrapper method from C# to change the variable, everything works fine, but at the moment that i call another C++ wrapper method from C#, unexplainably the WCHAR* global variable points to a different memory position and its value gets corrupted.
Some code:
//THE C# side:
capture.filename = PATH + "\\" + DIRECTORY_NAME + "\\";
capture.MaxMinutesPerFile = MAX_MINUTE_PER_FILE;
"capture" is an object of the C++ wrapper class (i think it is autogenerated when building the C++ code to a DLL. Not my code).
"filename" property calls a "put_FileName" C++ method and "MaxMinutesPerFile" a "put_MaxMinutesPerFile" method.
//C++ code
WCHAR *m_bstFileName = L"None";
(...)
STDMETHODIMP CCaptureMF::put_FileName(BSTR PathName)
{
EnterCriticalSection(&m_critsec);
HRESULT hr = S_OK;
m_bstFileName = PathName;
LeaveCriticalSection(&m_critsec);
return hr;
}
STDMETHODIMP CCaptureMF::put_MaxMinutesPerFile(LONG Minutes)
{
MaxMinutes= Minutes;
return S_OK;
}
So, after calling "put_FileName", "m_bstFileName" is updated correctly with the "PathName" value, but just after calling "MaxMinutesPerFile" (or any other interface wrapper method), "m_bstFileName" gets corrupted pointing to a different memory position and fulfilled with strange data.
Thank you.
EDIT:
To make a buffer of "m_bstFileName" and then copy the "PathName" data, i used the following code, taking in mind that "m_bstFileName" size can change at runtime:
m_bstFileName = (wchar_t*)malloc(sizeof(PathName));
wcscpy(m_bstFileName, PathName);
That code works fine, but the rest of the program behaves bad. I´m not sure why, i should investigate more, but for now, could you analyze that pice of code and tell me if it is correct or if i should implement it in other way?
SOLUTION:
Ok, following your recomendations i have finally implemented the following code, which works perfect for the whole application:
CComBSTR m_bstFileName = L"None";
(...)
STDMETHODIMP CCaptureMF::put_FileName(BSTR PathName)
{
EnterCriticalSection(&m_critsec);
HRESULT hr = S_OK;
m_bstFileName = PathName;
if (g_pCapture)
{
g_pCapture->SetPath(m_bstFileName);
}
LeaveCriticalSection(&m_critsec);
return hr;
}
If you think that this can be implemented better, just tell.
Thank you for your help!
In the most basic case you'll need to make a buffer to do a string copy into. Same operation could be accomplished via assignment with cstring, ccombstr, std::string, etc. depending on the framework you're using.
You need to copy the string to m_bstFileName, not just assign it. Use something like
strcpy(m_bstFileName, PathName);
I have an older app (ca. 2005) which accepts dll plugins. The app was originally designed for Win32 C plugins, but I have a working C# dll template. My problem: I need to do some one-time initialization, which in a Win32 C dll would be done in DllMain:
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
[one-time stuff here...]
}
Is there a C# equivalent of this? There is no "DllMain" in the C# template I have. I tried a literal C# interpretation, but no go: the dll works but it won't trigger the DllMain function.
public static bool DllMain(int hModule, int reason, IntPtr lpReserved) {
[one time stuff here...]
}
Give your class a static constructor and do your initialization there. It will run the first time anybody calls a static method or property of your class or constructs an instance of your class.
I've had to interact with a legacy application probably in the same situation as you have. I've found a hacky way to get DllMain functionality in a CLR assembly. Luckily it isn't too hard. It requires an additional DLL but it doesn't require you to deploy an additional DLL so you can still have the "put a DLL in that directory and the app will load it" paradigm.
First, you make a simple regular C++ DLL that looks like the following:
dllmain.cpp:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "resource.h"
extern void LaunchDll(
unsigned char *dll, size_t dllLength,
char const *className, char const *methodName);
static DWORD WINAPI launcher(void* h)
{
HRSRC res = ::FindResourceA(static_cast<HMODULE>(h),
MAKEINTRESOURCEA(IDR_DLLENCLOSED), "DLL");
if (res)
{
HGLOBAL dat = ::LoadResource(static_cast<HMODULE>(h), res);
if (dat)
{
unsigned char *dll =
static_cast<unsigned char*>(::LockResource(dat));
if (dll)
{
size_t len = SizeofResource(static_cast<HMODULE>(h), res);
LaunchDll(dll, len, "MyNamespace.MyClass", "DllMain");
}
}
}
return 0;
}
extern "C" BOOL APIENTRY DllMain(HMODULE h, DWORD reasonForCall, void* resv)
{
if (reasonForCall == DLL_PROCESS_ATTACH)
{
CreateThread(0, 0, launcher, h, 0, 0);
}
return TRUE;
}
Note the thread creation. This is to keep Windows happy because calling managed code within a DLL entrypoint is a no-no.
Next, you have to create that LaunchDll function the code above references. This goes in a separate file because it will be compiled as a managed C++ unit of code. To do this, first create the .cpp file (I called it LaunchDll.cpp). Then right click on that file in your project and in Configuration Properties-->C/C++-->General change the Common Language RunTime Support entry to Common Language RunTime Support (/clr). You can't have exceptions, minimal rebuild, runtime checks and probably some other things I forgot about but the compiler will tell you about. When the compiler complains, track down what settings you much change from the default and change them on the LaunchDll.cpp file only.
LaunchDll.cpp:
#using <mscorlib.dll>
// Load a managed DLL from a byte array and call a static method in the DLL.
// dll - the byte array containing the DLL
// dllLength - the length of 'dll'
// className - the name of the class with a static method to call.
// methodName - the static method to call. Must expect no parameters.
void LaunchDll(
unsigned char *dll, size_t dllLength,
char const *className, char const *methodName)
{
// convert passed in parameter to managed values
cli::array<unsigned char>^ mdll = gcnew cli::array<unsigned char>(dllLength);
System::Runtime::InteropServices::Marshal::Copy(
(System::IntPtr)dll, mdll, 0, mdll->Length);
System::String^ cn =
System::Runtime::InteropServices::Marshal::PtrToStringAnsi(
(System::IntPtr)(char*)className);
System::String^ mn =
System::Runtime::InteropServices::Marshal::PtrToStringAnsi(
(System::IntPtr)(char*)methodName);
// used the converted parameters to load the DLL, find, and call the method.
System::Reflection::Assembly^ a = System::Reflection::Assembly::Load(mdll);
a->GetType(cn)->GetMethod(mn)->Invoke(nullptr, nullptr);
}
Now for the really tricky part. You probably noticed the resource loading in dllmain.cpp:launcher(). What this does is retrieve a second DLL that has been inserted as a resource into the DLL getting created here. To do this, create a resource file by doing the
right click-->Add-->New Item-->Visual C++-->Resource-->Resource File (.rc) thing. Then, you need to make sure there is a line like:
resource.rc:
IDR_DLLENCLOSED DLL "C:\\Path\\to\\Inner.dll"
in the file. (Tricky, huh?)
The only thing left to do is to create that Inner.dll assembly. But, you already have it! This is what you were trying to launch with your legacy app in the first place. Just make sure to include a MyNamespace.MyClass class with a public void DllMain() method (of course you can call these functions whatever you want to, these are just the values hardcoded into dllmain.cpp:launcher() above.
So, in conclusion, the code above takes an existing managed DLL, inserts it into a resource of an unmanaged DLL which, upon getting attached to a process, will load the managed DLL from the resource and call a method in it.
Left as an exercise to the reader is better error checking, loading different DLLs for Debug and Release, etc. mode, calling the DllMain substitute with the same arguments passed to the real DllMain (the example only does it for DLL_PROCESS_ATTACH), and hardcoding other methods of the inner DLL in the outer DLL as pass through methods.
Also not easy to do from C# you can have a per module initializers
Modules may contain special methods called module initializers to initialize the module itself.
All modules may have a module initializer. This method shall be static, a member of the module, take no parameters, return no value, be marked with rtspecialname and specialname, and be named .cctor.
There are no limitations on what code is permitted in a module initializer. Module initializers are permitted to run and call both managed and unmanaged code.
Even though C# doesn't directly support module initialization we can implement it using reflection and static constructors. To do this we can define a custom attribute and use it find classes that need to be initialized on module loading:
public class InitOnLoadAttribute : Attribute {}
private void InitAssembly(Assembly assembly)
{
foreach (var type in GetLoadOnInitTypes(assembly)){
var prop = type.GetProperty("loaded", BindingFlags.Static | BindingFlags.NonPublic); //note that this only exists by convention
if(prop != null){
prop.GetValue(null, null); //causes the static ctor to be called if it hasn't already
}
}
}
static IEnumerable<Type> GetLoadOnInitTypes(Assembly assembly)
{
foreach (Type type in assembly.GetTypes())
{
if (type.GetCustomAttributes(typeof(InitOnLoadAttribute), true).Length > 0){
yield return type;
}
}
}
public MyMainClass()
{
//init newly loaded assemblies
AppDomain.CurrentDomain.AssemblyLoad += (s, o) => InitAssembly(o.LoadedAssembly);
//and all the ones we currently have loaded
foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies()){
InitAssembly(assembly);
}
}
on classes that we need to initialize immediately we add that code to their static constructor (which will be run once even if the property getter is accessed multiple times) and add the custom attribute we added to expose this functionality.
[InitOnLoad]
class foo
{
private static bool loaded { get { return true; } }
static foo()
{
int i = 42;
}
}