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);
Related
i am currently trying to port some piece of code from C# to go where dll's are involved (not sure if this makes any difference).
the actual "code" is not the problem but i run into some "feature" problems
i have the following line:
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINMSG)));
i know that i can transform
Marshal.SizeOf => unsafe.Sizeof also typeof is not a problem
but how can i implement the AllocHGlobal?
or is this not even needed - just create the instance of a struct and assign a pointer to it?
why do i need this:
there is need for communicating directly with dll's and i have to communicate/exchange data with them.
fully working code is available which needs to be turned into go
Done - but untested yet:
Marshal.AllocHGlobal
Marshal.FreeHGlobal
implementation of the stuff already done
import (
...
"github.com/kbinani/win"
...
)
type IntPtr int16
type UIntPtr uint64
const uint64 HIWORDMASK = 0xffffffffffff0000
// AllocHGlobal like Marshal.AllocHGlobal from c#
func MarshalAllocHGlobal(numBytes int) (tw.IntPtr, error) {
pNewMem := win.LocalAlloc( /*LMEM_FIXED*/ 0x0000, numBytes)
if pNewMem == 0 /*IntPtr.Zero*/ {
return nil, fmt.Errorf("OutOfMemoryException")
}
return pNewMem
}
func MarshalFreeHGlobal(hglobal IntPtr) error {
if IsNotWin32Atom(hglobal) {
if 0 /*IntPtr.Zero*/ != win.LocalFree(hglobal) {
return fmt.Errorf("failed to FreeHGlobal")
}
}
return nil
}
func IsNotWin32Atom(IntPtr ptr) bool {
return 0 != uint64(ptr)&HIWORDMASK
}
i also need
Marshal.StructureToPtr
#see https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/marshal.cs,48a38ffe8a227f92
Marshal.PtrToStructure
#see https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/marshal.cs,931fc84766e0e8cb
Updated information.
If you just want to call a Win32 api function, you don't need to .NET specific wrapper for memory management.
There is a pattern to call windows dll functions.
You can see example in windows specific code from the standard library, or at some libs that interact with windows API :
in https://github.com/gofrs/flock, for example, they grouped that code in the flock_winapi.go file
This post makes a decent job at explaining how to write code to call a specific function :
Breaking all the rules: Using Go to call Windows API
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 have made a plugin system that uses reflection to call functions in a plugin. A plugin has to implement the IPlugin interface to be used.
In the application which uses the plugins the plugin instance is created with the following code:
Assembly currentAssembly = Assembly.LoadFrom(startInfo.PluginAssemblyPath);
Type[] types = currentAssembly.GetTypes();
IPlugin pluginInstance = null;
foreach (Type type in types)
{
if (type.FullName == startInfo.PluginTypeName)
{
pluginInstance = (IPlugin)Activator.CreateInstance(type);
}
}
if (pluginInstance == null)
{
throw new Exception("Plugin loader error: Could not instantiate plugin: " + startInfo.ToString()); }
return pluginInstance;
I have made a plugin that uses some unmannaged dll's. When I call the IPlugin interface functions in a test project in the plugin solution everything works fine. But when I call the plugin via the plugin instance made in the code shown above I get the System.AccessViolationException: Attempted to read or write protected memory error when calling functions in the unmannaged dll's.
The unmannaged dll's are c++ dll's made by a third party. I tried enabling native code debugging but i do not have the .pdb files.
I am not sure why this is happening, is this because of the reflection? Or can there be other causes?
Edit:
In the stack I can see the unmannaged function being called:
[MarshalAs(UnmanagedType.LPStr)]
private readonly StringBuilder sb = new StringBuilder(256);
[DllImport("x.dll", EntryPoint = "xLib")]
static extern int _xLib(int a1, int a2, int a3, int a4, int a5, int a6, [Out]StringBuilder str);
The exception is thrown when calling the _xLib function.
Edit: Somewhere in this _xLib function the following function is called:
handle = x_Open();
which is in an other dll and is defined as:
DllExport x_Handle *x_Open();
As soon as anything in the handle is used like:
"%s", handle->x.string
The exception is thrown.
I still do not understand why this is working in the test project and not when I am using it in the app as a plugin.
Maybe you have to pin the StringBuilder to allow unmanaged code to interact with it.
Pinned object is one that is not allowed to move. The garbage collector is normally compacting the memory in that it moves all objects to "one or more clusters". This is to create large chunks of free space.
This basically means if someone else (outside) has a pointer to the memory address of an object, this may point to random content - as the object has moved.
Pinning an object tells the GC to NOT MOVE IT. This is normally useless and ONLY makes sense when working with pointers - like when using PInvoke... and I can see a pointer to a StringBuilder instance in _xlib function
Well after some intensive debugging I found the problem was the handle having some wrong address which caused the violation. The cause of the address being wrong was the x_open function loaded yet another dll with the LoadLibraryA function. This dll was not in the same directory as the executable file so it was not found.
I solved it by adding the directory of this last dll to the environment path.
have been working on this for hours, couldn't get it work :(
below code gives exception "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." this is fixed, see below update
but when i change the return type to int and return a int value, it works. how to make it to return a float value? thanks.
vs2012
c++ code
extern "C" __declspec(dllexport)
float compute_similarity()
{
return 1.01;
}
c# code
[DllImport("demo.exe", EntryPoint = "compute_similarity", CallingConvention = CallingConvention.Cdecl)]
public static extern float compute_similarity();
public void Get()
{
float x = compute_similarity(); // here returns random value
}
=====================================
UPDATE
See comments from David below, the problem was the c# project is targeting x64 and c++ is targeting Win32. After changing the c# project to target to x86, the exception went away.
However the call from c# returns some random value instead of 1.01 expected
I think your problem is that your function is declared in an executable rather than a DLL. Convert your native code into a library project and compile a DLL.
Your code will result in a call to LoadLibrary passing the file name demo.exe. The documentation for LoadLibrary says:
LoadLibrary can also be used to load other executable modules. For example, the function can specify an .exe file to get a handle that can be used in FindResource or LoadResource. However, do not use LoadLibrary to run an .exe file. Instead, use the CreateProcess function.
And so your code is doing exactly what you are told not to do. The call to LoadLibrary will succeed. The subsequent calls to GetProcAddress will succeed. But the module that you loaded is not fit to execute code.
I am trying to call out to a legacy dll compiled from FORTRAN code. I am new to Interop, but I've read some articles on it and it seems like my case should be fairly straightforward.
The method I really want to call has a complex method signature, but I can't even call this simple GetVersion method without getting a protected memory violation.
Here's my DllImport code:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version#4",
CallingConvention=CallingConvention.StdCall)]
public static extern void GetGeoConvertVersion([MarshalAs(UnmanagedType.LPStr, SizeConst=8)]
ref string version);
Here's the FORTRAN code:
SUBROUTINE GetVer( VRSION )
C
!MS$DEFINE MSDLL
!MS$IF DEFINED (MSDLL)
ENTRY Get_Version (VRSION)
!MS$ATTRIBUTES DLLEXPORT,STDCALL :: Get_Version
!MS$ATTRIBUTES REFERENCE :: VRSION
!MS$ENDIF
!MS$UNDEFINE MSDLL
C
CHARACTER*8 VRSION
C
VRSION = '1.0a_FhC'
C
RETURN
END
Here's my unit test that fails:
[Test]
public void TestGetVersion()
{
string version = "";
LatLonUtils.GetGeoConvertVersion(ref version);
StringAssert.IsNonEmpty(version);
}
Here's the error message I get:
System.AccessViolationException
Message: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
Other things I've tried:
Using the default marshalling
Passing a char[] instead of a string (get method signature errors instead)
...snip...
OK, I got it to work, the problem was passing by ref. I'm not sure why, but this works:
...snip...
You need to pass by reference because that is the semantic being used by the FORTRAN code. The client code is passing in a buffer that the FORTRAN code is going to write to in lieu of using a return value.
...snip...
!MS$ATTRIBUTES REFERENCE :: VRSION
...snip...
This attribute in your FORTRAN code specifies that this parameter is passed by reference. That means the FORTRAN code is going to write to this address. If the DllImport doesn't declare it as a ref value also, you will get an access violation.
OK, I got it to work, the problem was passing by ref. I'm not sure why, but this works:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version#4",
CallingConvention=CallingConvention.StdCall)]
public static extern void GetGeoConvertVersion([MarshalAs(UnmanagedType.LPArray)]
byte[] version);
With this test:
[Test]
public void TestGetVersion()
{
//string version = "";
byte[] version = new byte[8];
LatLonUtils.GetGeoConvertVersion(version);
char[] versionChars = System.Text.Encoding.ASCII.GetChars(version);
string versionString = new string(versionChars);
}
Have you tried using a StringBuilder?
Create your String as a StringBuilder and pass that into the dll function.
Im unsure as to what Marashlling statement to use, perhapse the default might work.
Have a look at: Marshal C++ “string” class in C# P/Invoke
Heres a good article the might help as well: Interop Marshalling
I cannot try this solution since I do not have a FORTRAN compiler, but I think this would work for you:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version#4",
CallingConvention=CallingConvention.StdCall,
CharSet=CharSet.Ansi)]
public static extern void GetGeoConvertVersion(StringBuilder version);
Thank you all guys, I've been trying to pass a string from c# to a subroutine from fortran dll and this method was the only working one among lots of others