0

I am calling a DLL(C write) in C# language in VS2012. mylib.dll is the native dll I am going to call in C# , and mylib.dll will also call another mylib_another.dll also.

C function declaration as:

extern DECLSPEC_DLL BOOLEAN_TYPE SetConnection(char *dev, char *addr); 

In My C#  file , I declare it as:

[DllImport("C:\\mylib.dll", EntryPoint = "SetConnection", CharSet = CharSet.Auto)]
public static unsafe extern int SetConnection(StringBuilder  dev,  StringBuilder addr);

When I calling it in code ,I found that the string only passed one character, when I pass dev as"USB" , the native DLL only get a "U" actually.

If I change declaration to :

[DllImport("C:\\mylib.dll", EntryPoint = "SetConnection", CharSet = CharSet.Ansi)]
public static unsafe extern int SetConnection(StringBuilder  dev,  StringBuilder addr);

Then it will raise an System.AccessViolationException Exception :

System.Reflection.TargetInvocationException was unhandled
HResult=-2146232828
Message=Exception has been thrown by the target of an invocation.
Source=mscorlib
StackTrace:
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
   at QC.QTMDotNetKernel.DotNetKernel.RunDotNetTest(Object stateObject)
   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
InnerException: System.AccessViolationException
   HResult=-2147467261
   Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Any ideas about that ?

Have solved as below(the answer is for the first question, for the second one , that is because the mylib.dll called another native dll and the system did not find it).

A similar issue here:

Community
  • 1
  • 1
Steve Wang
  • 335
  • 1
  • 3
  • 11

2 Answers2

3

When you only receive one character like that, it normally means that you are passing a Unicode (UTF16) string to a C/C++ function that is expecting an 8 bit format such as ASCII, UTF8 or ANSI.

The 16-bit Unicode format ends up with one byte of its two bytes being set to zero for characters in the ASCII/ANSI range, and because a C/C++ program treats a zero byte as an end-of-string character, it truncates the string.

It's easy to try using ANSI to see if it helps; change

CharSet = CharSet.Auto

to

CharSet = CharSet.Ansi
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • Second one is: `CharSet = CharSet.Ansi` when change to this , it raise System.AccessViolationException. – Steve Wang Jun 10 '13 at 17:13
  • Yeah, this one is correct , the second problem about `System.AccessViolationException` is because in the native dll, – Steve Wang Jun 10 '13 at 18:43
  • Yeah, this one is correct , the second problem about `System.AccessViolationException` is because in the native dll, I called another native dll called mylib_another.dll and it because I loaded the mylib.dll specificly from a full path, the mylib_another.dll in this path can not be loaded by main programm automatically. So all these dll (except that one in load in your code) should be place with your main programme 's home directory to make sure it can loaded automatically . – Steve Wang Jun 10 '13 at 18:51
0

There are a few things wrong with your p/invoke:

  1. The calling convention looks to be cdecl. It's not specified in the native code and the most likely default is cdecl.
  2. You are passing StringBuilder which is used for out parameters. Note that I am assuming that the two parameters are both input parameters.
  3. The character set is wrong. The native code receives 8 bit encoded text.
  4. You do not need to be using unsafe.

A corrected declaration is:

[DllImport(@"C:\mylib.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int SetConnection(string dev, string addr);

You can omit the specification of character set since ANSI is the default. Of course, it doesn't hurt to specify CharSet=CharSet.Ansi if you prefer to be explicit. There's no need to specify EntryPoint if it is the same name as the function that you declare.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I do need this line: `CharSet=CharSet.Ansi` to solve the first problem. – Steve Wang Jun 10 '13 at 19:09
  • No you do not. It is the default and it can be omitted. Clearly you cannot set it to `CharSet.Auto` as you did in the question, but the code in my answer is accurate. – David Heffernan Jun 10 '13 at 19:14