Alex
Alex

Reputation: 307

P/Invoke method broken just by updating from .Net 4.5 to .Net 4.5.1

I have a P/Invoke call in my application which communicates with an external piece of hardware via an API (a .dll file) written in C++

This method worked perfectly untill I installed the latest .Net 4.5.1, and without making any code changes it now throws an exception: "Cannot implicitly convert type 'void' to 'object'"

Is there anything I should check to make my app compatible with .Net 4.5.1? Rolling back to a previous framework is the only way I have found to make it work!

[DllImport("TestAPI", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
internal static extern bool OMBOpen(int VID, int PID, int CID,
   StringBuilder description,
   int bufferSize);

The native declaration of the method in C++

TESTAPI_API 
bool OMBOpen(int VID,int PID,int IID,char* buffer,int bufferSize);

EDIT:

In the end the problem turned out not to be with the above method call itself at all, but with marshalling data from a pointer to a data structure (See accepted answer), which was elsewhere in the code as David Heffernan wiseley suspected.

Upvotes: 1

Views: 1835

Answers (2)

Xy Ziemba
Xy Ziemba

Reputation: 388

The exception "Cannot implicitly convert type 'void' to 'object'" is thrown when attempting to assign the result of a void method to a variable. While this is normally a compiler error, if it's seen during execution then it usually occurred as a result of dynamic invocation of a method (e.g. using the dynamic keyword or using a dynamic language like IronPython).

.NET 4.5.1 introduced a overloads for Marshal.SizeOf and Marshal.PtrToStructure and dynamic method invocation may pick the new overload under certain circumstances. See Microsoft KB2909958. The above exception wouldn't be coming from the P/Invoke itself, but from the subsequent marshaling operations.

The easiest solution to these kinds of issues is to just introduce a cast within the call to ensure that the correct method is resolved.

#Code from 4.5
dynamic dynamicVar = ...
object boxedStruct = Marshal.PtrToStructure(myPtr, dynamicVar.GetType());
// Above resolves to 'object Marshal.PtrToStructure(IntPtr, Type)' in 4.5
// and resolves to 'void Marshal.PtrToStructure<T>(IntPtr, T)' in 4.5.1
int size = Marshal.SizeOf(dynamicVar.GetType());
// Above resolves to 'object Marshal.SizeOf(Type)' in 4.5
// and resolves to 'void Marshal.SizeOf<T>(T)' in 4.5.1

#Fixed for 4.5.1
dynamic dynamicVar = ...
object boxedStruct = Marshal.PtrToStructure(myPtr, (Type)dynamicVar.GetType());
// Adding the cast forces the code to resolve to
// 'object Marshal.PtrToStructure(IntPtr, Type)'
int size = Marshal.SizeOf((Type)dynamicVar.GetType());
// Adding the cast forces the code to resolve to
// 'int Marshal.SizeOf(Type)'

Upvotes: 2

David Heffernan
David Heffernan

Reputation: 612963

The only thing that I can find wrong with the code in the question is the return value. By default the marshaller will assume the native code is returning a 4 byte BOOL. You should write the pinvoke like this:

[DllImport(...)]
[return: MarshalAs(UnmanagedType.I1)]
internal static extern bool OMBOpen(...);

However, I don't see how that could lead to the error that you report. I rather suspect that the actual problem lies in code that is not in the question.

References:

  1. http://blogs.msdn.com/b/jaredpar/archive/2008/10/14/pinvoke-and-bool-or-should-i-say-bool.aspx
  2. http://www.greengingerwine.com/index.php/2011/08/always-marshal-bool-return-types-when-using-pinvoke/
  3. http://msdn.microsoft.com/en-us/library/ms182206.aspx

Upvotes: 2

Related Questions