Reputation: 4064
My Question is simple
VB.dll (VB5.0 I guess) includes these methods
Private Declare Function ffr_device_find Lib ".\ffr_32.dll" () As Boolean
Private Declare Function ffr_data_transceive_ex Lib ".\ffr_32.dll" (ByVal sp_sdata As String, ByVal sp_rdata As String) As Boolean
in C#.... ( .NET 4.5 )
[DllImport("FFR_32.dll", CallingConvention = CallingConvention.Cdecl)]
extern public static Boolean ffr_device_find();
[DllImport("FFR_32.dll", CallingConvention = CallingConvention.Cdecl)]
extern public static void ffr_data_transceive_ex([Out] string sp_sdata, [Out] string sp_rdata);
// FYI, I tried [Out], out, and ref but to no avail.
The first one works great,
but the second one spilt this error.
A call to PInvoke function 'ffr_data_transceive_ex' 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.
FYI
This is a working code from VB... ( NOT INNER DLL SOURCES )
Dim st As String
Dim rData As String * 40
st = "4401" & "20202020202020202020202020202020"
Text1.Text = st
Cal_BCC
Call ffr_data_transceive_ex(Text1.Text, rData)
Text2.Text = rData
I don't even understand what Dim rData As String * 40
is about... will it become 0
when rData
is 0
? and become 40
when rData
has 1
? ...
What's wrong with my DllImport methods in C#???
Upvotes: 8
Views: 355
Reputation: 4064
I choose the above answer by Hans Passant
But for those who want an exact solution for this case, I will give you some additional information and some code snippets.
First
Like Hans Passant said, CallingConvention
should be gotten rid of.
Second
Like Hans Passant said, string shouldn't be passed into the function. More accurately, the first parameter can be passed into as string
type. But the second one should be char[]
with explicit length.
I tested this and It throws another error when the length is different.
Working code
extern public static void ffr_data_transceive_ex([Out] string sp_sdata, [Out] char[] sp_rdata);
Necessary Ingredient
// To pass the second parameter.
// Because the dll returns 40 length characters, this should be specified as length '40'
char[] ForCardHex = new char[40];
// Command is an already defined protocol format, like "200080581028000001"
// , which can be taken as string.
ffr_data_transceive_ex(Command, ForCardHex);
This works greatly and it returns the expected value on ForCardHex
. You should use char[]
. I couldn't get byte[]
passed into the function.
Thank all of the commentors and the answerer.
Upvotes: 0
Reputation: 941218
[DllImport("FFR_32.dll", CallingConvention = CallingConvention.Cdecl)]
It is not Cdecl. Visual Basic assumes the default, StdCall. You got away with it on the first function because it does not take any arguments. Not on the second since it imbalanced the stack, both the caller and the callee popped the arguments off the stack, forcing the MDA to step in and tell you about the drastic mishap.
Simply remove the property completely so you get the correct default in your C# program as well, CallingConvention.StdCall.
void ffr_data_transceive_ex([Out] string sp_sdata, [Out] string sp_rdata)
You cannot use string
, strings are immutable in .NET. Use StringBuilder
instead. Do make sure that its Capacity is large enough to be able to store the received data that the function writes. Guessing too low causes heap corruption, a very nasty bug to troubleshoot.
Also odds that it should be byte[]
, your question doesn't document the type of the returned data well enough. VB5 did not have a Byte type yet so a fixed string (like String * 40) was the next best choice. Doesn't work in .NET either, not all possible byte values have a corresponding Unicode codepoint. Use StringBuilder only if you know for a fact that the function only returns ASCII codes.
Upvotes: 7