Click here to Skip to main content
15,915,324 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello,
I am facing some issues in passing arguments from managed code to unmanaged code. I am having a C++ dll and i want to call few functions of it from a C# project (VS2010). C++ dll is not a native assembly or COM component. So i am using DllImport - P\Invoke.

C++ function details
typedef unsigned long UINT32;
typedef UNIT32 ABC_RESULT; // the return code from a ABC function
typedef UINT32 ABC_HOBJECT; // basic object handle
typedef ABC_HOBJECT ABC_HCONTEXT; // context object handle

ABC_RESULT Abc_Context_Create(
ABC_HCONTEXT* phContext  // out
);

phContext - Receives the handle to the created context object.

C# signature -
public class Abc1api
{
[DllImport("Abc1.dll")]
public static extern UInt32 Abc_Context_Create(UIntPtr phContext);
}

And i am calling it as below -
UInt32 result = 0;
UIntPtr hContext = new UIntPtr(sizeof(UInt32));
result = Abc1api.Abc_Context_Create(hContext);


I am able to build the project but while running i am getting below error -
A call to PInvoke function 'ABCTool!ABCTool.Abc1api::Abc_Context_Create' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

I tried IntPtr hContext instead of UIntPtr but result is same.

So I modified C# signature as mentioned below -
public class Abc1api
{
[DllImport("Abc1.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern UInt32 Abc_Context_Create([MarshalAs(UnmanagedType.U4), Out()] IntPtr phContext);
}


And I am calling it as below -
UInt32 result = 0;
IntPtr hContext = new IntPtr(sizeof(UInt32));
result = Abc1api.Abc_Context_Create(hContext);

When i run it, the error is - Cannot marshal 'parameter #1': Invalid managed/unmanaged type combination (Int/UInt must be paired with SysInt or SysUInt).

I tried UIntPtr but same result. Also tried allocating IntPtr hContext using Marshal i.e.
IntPtr hContext = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(UInt32))); but getting same error as above.

I could not find out the reason of this error. Can any one help me in defining C# signature and how can I pass the arguments so that I can get the required hContext from unmanaged code and use it?

Thank you,
Satish.
Posted
Updated 10-Mar-20 7:04am
v2

Since you know your way around C++, why not solve the issue using mixed mode C++/CLI? In this article I demonstrate using ACE with C++ CLI[^], where ACE is a regular native C++ library compiled into a DLL. One of the nice things is that you can forget about c++ name mangling, it's all taken care of for you.

I guess this is an easier solution :) - Expose the functionality of your c++ library as CLI - call native dll from cli code.

Regards
Espen Harlinn
 
Share this answer
 
v2
Firstly, you need to export the function with __declspec(dllexport) for it to show up in the DLL's exported functions (there are also other methods avaliable for this such as a .def file)

C++ will "mangle" function names when it exports them in order to export the type of arguments and return type. To avoid this, the exported functions need to be defined inside a extern "C" {} block which exports them as a C function without this mangling.

To check if the names are mangled get a program such as DependancyWalker[^] and open the C++ DLL in it.
What you are looking for is over the right hand side in the middle which shows the exported functions.
You need to cehck to see if the function names contain return and parameter types or have jumbled up names.

typedef unsigned long UINT32;
typedef UNIT32 ABC_RESULT; // the return code from a ABC function
typedef UINT32 ABC_HOBJECT; // basic object handle
typedef ABC_HOBJECT ABC_HCONTEXT; // context object handle

extern "C" {
	__declspec(dllexport) ABC_RESULT Abc_Context_Create(ABC_HCONTEXT * phContext) {
		//The code, you may need to call another function from here if you are wanting to do something only valid in C++
	}
}
 
Share this answer
 
Comments
SatishKagathara 19-Jan-11 6:25am    
Thanks for replying. The function is already exported with __declspec(dllexport) and defined inside a extern "C" {} block correctly as you mentioned.

I had checked the C++ dll exported functions using Dependancy Walker. Exported functions list show only name of function.

i.e.
Ordinal Hint Function Entry Point
6 (0x0006) 5 (0x0005) Abc_Context_Connect 0x0010F550
8 (0x0008) 7 (0x0007) Abc_Context_Create 0x0010EDD0
etc...

Let me know whether they are correct or it should show arguments & return types.
C
// int or uint
typedef unsigned long UINT32;
// int or uint
typedef UNIT32 ABC_RESULT; // the return code from a ABC function
// int or uint
typedef UINT32 ABC_HOBJECT; // basic object handle
// int or uint
typedef ABC_HOBJECT ABC_HCONTEXT; // context object handle
// returns int or uint and passes out a pointer (reference) to a int or uint that is the context
ABC_RESULT Abc_Context_Create(ABC_HCONTEXT* phContext  // out);

Therefore I would declare it:
C#
public class Abc1api
{
    [DllImport("Abc1.dll")]
    public static extern UInt32 Abc_Context_Create(out UInt32 hContext);
}

or
C#
[DllImport("Abc1.dll")]
public static extern Int32 Abc_Context_Create(out Int32 hContext);


Hope this helps!
 
Share this answer
 
In dllimport you must use the correct EntryPoint (be careful with mangle) and you should also use CallingConvention:=CallingConvention.Cdecl
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900