Platform Invoke

Punch line:  

PInvoke should be used sparingly; there should be a managed API for almost everything you want to do.  However, sometimes you absolutely have to talk to the unmanaged world from your comfy managed world.  Not using it often is not an excuse for not knowing about it.  First, who knows what problems you may run into that could only be solved by invoking some platform API.  Moreover, the more you know, the more you know.broadening your understanding of programming concepts is obviously advantageous to a developer.blah, blah, blah learning's good, mmmkay.  Recently i was prototyping some functionality using the GetStringScripts API and ran into some issues with out parameters.

MSDN defines the syntax for this unmanaged function as the following.

    1:  int GetStringScripts(
    2:    __in   DWORD dwFlags,
    3:    __in   LPCWSTR lpString,
    4:    __in   int cchString,
    5:    __out  LPWSTR lpScripts,
    6:    __in   int cchScripts
    7:  );

When i saw the __out LPWSTR lpScripts parameter, i erroneously assumed that i should use the C# out keyword.  i was getting Access Violation errors until i was wised up by some site i found.  i hacked fixed up the code according to the example i saw and everything worked fine.  Left ill at ease, i decided to investigate more deeply.  i found myself asking, "How do you know how to properly write PInvoke signatures?"  PInvoke.net is "primarily a wiki, allowing developers to find, edit and add PInvoke signatures, user-defined types, and any other information related to calling Win32 and other unmanaged APIs from managed code" but that's me doing copy-and-paste hacking-that's not me understanding what i'm doing.

    1:          [DllImport("kernel32.dll",
    2:                      EntryPoint="GetStringScripts",
    3:                      SetLastError= true,
    4:                      CharSet = CharSet.Unicode,
    5:                      ExactSpelling=true,
    6:                      BestFitMapping=false,
    7:                      ThrowOnUnmappableChar=true,
    8:                      CallingConvention = CallingConvention.StdCall)]
    9:          public static extern int GetStringScriptsImport(
   10:              uint dwFlags,
   11:              [In, MarshalAs(UnmanagedType.LPWStr)] String lpString,
   12:              int cchString,
   13:              [Out, MarshalAs(UnmanagedType.LPWStr)] String lpScripts,
   14:              int cchScripts);

 

C# syntax only allows for DllImport, while Visual Basic allows for the declare statement and for the DllImport.  While blittable types were a new one on me, my investigation is going to stop shorter than i had originally guessed.  The real information that i care about is captured in msdn articles on Platform Invoke Data Types, Marshaling Arguments, and the DllImportAttribute Class which defines how to deal with complex method definitions that include BestFitMapping, CallingConvention, ExactSpelling, PreserveSig, SetLastError, or ThrowOnUnmappableChar fields.  And if i ever need to do some more Platform Invocations, i'll look to the Platform Invoke Tutorial.

Background Reading:

Blittable/Non-Blittable Types

Broad definition

A memory copy operation is sometimes referred to as a 'block transfer'. This term is sometimes abbreviated as BLT (there's actually a BLT instruction on the PDP-10) and pronounced 'blit'. The term 'blittable' expresses whether it is legal to copy an object using a block transfer.

Pasted from <https://en.wikipedia.org/wiki/Blittable_types>

.NET framework context

Blittable and Non-Blittable Types

Most data types have a common representation in both managed and unmanaged memory and do not require special handling by the interop marshaler. These types are called blittable types because they do not require conversion when they are passed between managed and unmanaged code.

Return values from platform invoke calls must be blittable types. Platform invoke does not support non-blittable return types.

The following types from the System namespace are blittable types:

The following complex types are also blittable types:

  • One-dimensional arrays of blittable types, such as an array of integers. However, a type that contains a variable array of blittable types is not itself blittable.

  • Formatted value types that contain only blittable types (and classes if they are marshaled as formatted types). For more information about formatted value types, see Default Marshaling for Value Types.

Object references are not blittable. This includes an array of references to objects that are blittable by themselves. For example, you can define a structure that is blittable, but you cannot define a blittable type that contains an array of references to those structures.

As an optimization, arrays of blittable types and classes that contain only blittable members are pinned instead of copied during marshaling. These types can appear to be marshaled as In/Out parameters when the caller and callee are in the same apartment. However, these types are actually marshaled as In parameters, and you must apply the InAttribute and OutAttribute attributes if you want to marshal the argument as an In/Out parameter.

Some managed data types require a different representation in an unmanaged environment. These non-blittable data types must be converted into a form that can be marshaled. For example, managed strings are non-blittable types because they must be converted into string objects before they can be marshaled.

The following table lists non-blittable types from the System namespace. Delegates, which are data structures that refer to a static method or to a class instance, are also non-blittable.

Non-blittable type

Description

System.Array

Converts to a C-style array or a SAFEARRAY.

System.Boolean

Converts to a 1, 2, or 4-byte value with true as 1 or -1.

System.Char

Converts to a Unicode or ANSI character.

System.Class

Converts to a class interface.

System.Object

Converts to a variant or an interface.

System.Mdarray

Converts to a C-style array or a SAFEARRAY.

System.String

Converts to a string terminating in a null reference or to a BSTR.

System.Valuetype

Converts to a structure with a fixed memory layout.

System.Szarray

Converts to a C-style array or a SAFEARRAY.

Class and object types are supported only by COM interop. For corresponding types in Visual Basic 2005, C#, and C++, see the .NET Framework Class Library Overview.

See Also


Other Resources

Default Marshaling Behavior

 

Pasted from https://msdn.microsoft.com/en-us/library/75dwhxf7.aspx

OutAttribute

The OutAttribute is optional. The attribute is supported for COM interop and platform invoke only. In the absence of explicit settings, the interop marshaler assumes rules based on the parameter type, whether the parameter is passed by reference or by value, and whether the type is blittable or non-blittable. For example, the StringBuilder class is always assumed to be In/Out and an array of strings passed by value is assumed to be In.

Out-only behavior is never a default marshaling behavior for parameters. You can apply the OutAttribute to value and reference types passed by reference to change In/Out behavior to Out-only behavior, which is equivalent to using the out keyword in C#. For example, arrays passed by value, marshaled as In-only parameters by default, can be changed to Out-only. However, the behavior does not always provide expected semantics when the types include all-blittable elements or fields because the interop marshaler uses pinning. If you do not care about passing data into the callee, Out-only marshaling can provide better performance for non-blittable types.

Combining the InAttribute and OutAttribute is particularly useful when applied to arrays and formatted, non-blittable types. Callers see the changes a callee makes to these types only when you apply both attributes. Since these types require copying during marshaling, you can use InAttribute and OutAttribute to reduce unnecessary copies.

Pasted from <https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.outattribute.aspx>