Skip to content
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a166d86
Update best-practices.md
AaronRobinsonMSFT Mar 23, 2021
11cb3d1
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 23, 2021
2b26da4
Update best-practices.md
AaronRobinsonMSFT Mar 23, 2021
d007a23
Update best-practices.md
AaronRobinsonMSFT Mar 23, 2021
3b8b004
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 24, 2021
e52394a
Update best-practices.md
AaronRobinsonMSFT Mar 24, 2021
d7d639d
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 24, 2021
e126fbc
Update best-practices.md
AaronRobinsonMSFT Mar 25, 2021
9bdfb57
Update best-practices.md
AaronRobinsonMSFT Mar 25, 2021
f3ab433
Update best-practices.md
AaronRobinsonMSFT Mar 25, 2021
7848881
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 25, 2021
77ce9ff
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 25, 2021
e353b12
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 25, 2021
bff1261
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 25, 2021
e44d4d9
Update best-practices.md
AaronRobinsonMSFT Mar 25, 2021
98771f0
Update best-practices.md
AaronRobinsonMSFT Mar 25, 2021
a979181
Update best-practices.md
AaronRobinsonMSFT Mar 25, 2021
89e658a
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 25, 2021
133f36e
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 25, 2021
f55a0b3
Update docs/standard/native-interop/best-practices.md
AaronRobinsonMSFT Mar 25, 2021
d7da6e8
Apply suggestions from code review
AaronRobinsonMSFT Mar 25, 2021
2333611
Update best-practices.md
AaronRobinsonMSFT Mar 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 78 additions & 23 deletions docs/standard/native-interop/best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,29 +169,29 @@ Here is a list of data types commonly used in Windows APIs and which C# types to

The following types are the same size on 32-bit and 64-bit Windows, despite their names.

| Width | Windows | C (Windows) | C# | Alternative |
|:------|:-----------------|:---------------------|:---------|:-------------------------------------|
| 32 | `BOOL` | `int` | `int` | `bool` |
| 8 | `BOOLEAN` | `unsigned char` | `byte` | `[MarshalAs(UnmanagedType.U1)] bool` |
| 8 | `BYTE` | `unsigned char` | `byte` | |
| 8 | `CHAR` | `char` | `sbyte` | |
| 8 | `UCHAR` | `unsigned char` | `byte` | |
| 16 | `SHORT` | `short` | `short` | |
| 16 | `CSHORT` | `short` | `short` | |
| 16 | `USHORT` | `unsigned short` | `ushort` | |
| 16 | `WORD` | `unsigned short` | `ushort` | |
| 16 | `ATOM` | `unsigned short` | `ushort` | |
| 32 | `INT` | `int` | `int` | |
| 32 | `LONG` | `long` | `int` | |
| 32 | `ULONG` | `unsigned long` | `uint` | |
| 32 | `DWORD` | `unsigned long` | `uint` | |
| 64 | `QWORD` | `long long` | `long` | |
| 64 | `LARGE_INTEGER` | `long long` | `long` | |
| 64 | `LONGLONG` | `long long` | `long` | |
| 64 | `ULONGLONG` | `unsigned long long` | `ulong` | |
| 64 | `ULARGE_INTEGER` | `unsigned long long` | `ulong` | |
| 32 | `HRESULT` | `long` | `int` | |
| 32 | `NTSTATUS` | `long` | `int` | |
| Width | Windows | C# | Alternative |
|:------|:-----------------|:---------|:-------------------------------------|
| 32 | `BOOL` | `int` | `bool` |
| 8 | `BOOLEAN` | `byte` | `[MarshalAs(UnmanagedType.U1)] bool` |
| 8 | `BYTE` | `byte` | |
| 8 | `CHAR` | `sbyte` | |
| 8 | `UCHAR` | `byte` | |
| 16 | `SHORT` | `short` | |
| 16 | `CSHORT` | `short` | |
| 16 | `USHORT` | `ushort` | |
| 16 | `WORD` | `ushort` | |
| 16 | `ATOM` | `ushort` | |
| 32 | `INT` | `int` | |
| 32 | `LONG` | `int` | See [`CLong` and `CULong`](#cc-long). |
| 32 | `ULONG` | `uint` | See [`CLong` and `CULong`](#cc-long). |
| 32 | `DWORD` | `uint` | |
| 64 | `QWORD` | `long` | |
| 64 | `LARGE_INTEGER` | `long` | |
| 64 | `LONGLONG` | `long` | |
| 64 | `ULONGLONG` | `ulong` | |
| 64 | `ULARGE_INTEGER` | `ulong` | |
| 32 | `HRESULT` | `int` | |
| 32 | `NTSTATUS` | `int` | |

The following types, being pointers, do follow the width of the platform. Use `IntPtr`/`UIntPtr` for these.

Expand Down Expand Up @@ -251,6 +251,61 @@ finally
}
```

## Cross-platform data type considerations

There are types in the C/C++ language that have latitude in how they are defined. When writing cross-platform interop, cases can arise where platforms differ and can cause issues if not considered.

### C/C++ `long`

C/C++ `long` and C# `long` are not the same types. Using C# `long` to interop with C/C++ `long` is almost never correct.

The `long` type in C/C++ is defined to have ["at least 32"](https://en.cppreference.com/w/c/language/arithmetic_types) bits. This means there is a minimum number of required bits, but platforms can choose to use more bits if desired. The following table illustrates the differences in provided bits for the C/C++ `long` data type between platforms.

| Platform | 32-bit | 64-bit |
|:------------|:-------|:-------|
| Windows | 32 | 32 |
| macOS/\*nix | 32 | 64 |

These differences can make authoring cross-platform P/Invokes difficult when the native function is defined to use `long` on all platforms.

In .NET 6 and later versions, use the [`CLong` and `CULong`](https://github.com/dotnet/runtime/issues/13788) types for interop with C/C++ `long` and `unsigned long` data types. The following example is for `CLong`, but you can use `CULong` to abstract `unsigned long` in a similar way.

```csharp
// Cross platform C function
// long Function(long a);
[DllImport("NativeLib")]
extern static CLong Function(CLong a);

// Usage
nint result = Function(new CLong(10)).Value;
```

When targeting .NET 5 and earlier versions, you should declare separate Windows and non-Windows signatures to handle the problem.

```csharp
static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

// Cross platform C function
// long Function(long a);

[DllImport("NativeLib", EntryPoint = "Function")]
extern static int FunctionWindows(int a);

[DllImport("NativeLib", EntryPoint = "Function")]
extern static nint FunctionUnix(nint a);

// Usage
nint result;
if (IsWindows)
{
result = FunctionWindows(10);
}
else
{
result = FunctionUnix(10);
}
```

## Structs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc jumps back and forth between general guidelines and very specific Windows stuff. I think overall flow of this document can be improved. It can be done in a different PR.

General guidelines - strings, structs, fixed buffers, etc.
Cross-platform data type considerations
Platform-specific - Common Windows data types, Formerly built-in supported types

It may be even fine to split this into multiple docs. I see that we have Writing cross platform P/Invoke code. Should the "Cross-platform data type considerations" topic be there?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This is part of the exercise being done with the interop-doc - what is a logical flow for learning about interop and best to walk users through writing interop solutions.


Managed structs are created on the stack and aren't removed until the method returns. By definition then, they are "pinned" (it won't get moved by the GC). You can also simply take the address in unsafe code blocks if native code won't use the pointer past the end of the current method.
Expand Down