You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
We have the following goals related to interop code being used in CoreFX:
6
6
7
7
- Minimize code duplication for interop.
8
-
- We should only define a given interop signature in a single place. This stuff is tricky, and we shouldn’t be copy-and-pasting it.
9
-
- Minimize unnecessary IL in assemblies.
10
-
- Interop signatures should only be compiled into the assemblies that actually consume them. Having extra signatures bloats assemblies and makes it more difficult to do static analysis over assemblies to understand what they actually use. It also leads to problems when such static verification is used as a gate, e.g. if a store verifies that only certain APIs are used by apps in the store.
8
+
- We should only define a given interop signature in a single place.
9
+
This stuff is tricky, and we shouldn't be copy-and-pasting it.
10
+
- Minimize unnecessary IL in assemblies.
11
+
- Interop signatures should only be compiled into the assemblies that
12
+
actually consume them. Having extra signatures bloats assemblies and
13
+
makes it more difficult to do static analysis over assemblies to
14
+
understand what they actually use. It also leads to problems when such
15
+
static verification is used as a gate, e.g. if a store verifies that
16
+
only certain APIs are used by apps in the store.
11
17
- Keep interop code isolated and consolidated.
12
-
- This is both for good hygiene and to help keep platform-specific code separated from platform-neutral code, which is important for maximizing reusable code above PAL layers.
18
+
- This is both for good hygiene and to help keep platform-specific code
19
+
separated from platform-neutral code, which is important for maximizing
20
+
reusable code above PAL layers.
13
21
14
22
## Approach
15
23
16
24
### Interop type
17
-
- All code related to interop signatures (DllImports, interop structs used in DllImports, constants that map to native values, etc.) should live in a partial, static, and internal “Interop” class in the root namespace, e.g.
25
+
- All code related to interop signatures (DllImports, interop structs
26
+
used in DllImports, constants that map to native values, etc.) should
27
+
live in a partial, static, and internal “Interop” class in the root
28
+
namespace, e.g.
29
+
18
30
```C#
19
-
internalstaticpartialclassInterop { … }
31
+
internalstaticpartialclassInterop { ... }
20
32
```
21
33
22
-
- Declarations shouldn’t be in Interop directly, but rather within a partial, static, internal nested type named for a given library or set of libraries, e.g.
34
+
- Declarations shouldn't be in Interop directly, but rather within a
35
+
partial, static, internal nested type named for a given library or set
36
+
of libraries, e.g.
37
+
23
38
```C#
24
39
internalstaticpartialclassInterop
25
40
{
26
-
internalstaticpartialclasslibc { … }
41
+
internalstaticpartialclasslibc { ... }
27
42
}
28
43
...
29
44
internalstaticpartialclassInterop
30
45
{
31
-
internalstaticpartialclassmincore { … }
46
+
internalstaticpartialclassmincore { ... }
32
47
}
33
48
```
34
-
- With few exceptions, the only methods that should be defined in these interop types are DllImports.
35
-
- Exceptions are limited to times when most or every consumer of a particular DllImport will need to wrap its invocation in a helper, e.g. to provide additional marshaling support, to hide thread-safety issues in the underlying OS implementation, to do any required manipulation of safe handles, etc. In such cases, the DllImport should be private whenever possible rather than internal, with the helper code exposed to consumers rather than having the DllImport exposed directly.
49
+
- With few exceptions, the only methods that should be defined in these
50
+
interop types are DllImports.
51
+
- Exceptions are limited to times when most or every consumer of a
52
+
particular DllImport will need to wrap its invocation in a helper, e.g.
53
+
to provide additional marshaling support, to hide thread-safety issues
54
+
in the underlying OS implementation, to do any required manipulation of
55
+
safe handles, etc. In such cases, the DllImport should be private
56
+
whenever possible rather than internal, with the helper code exposed to
57
+
consumers rather than having the DllImport exposed directly.
36
58
37
59
### File organization
38
-
- The Interop partial class definitions should live in Interop.*.cs files. These Interop.*.cs files should all live under Common rather than within a given assembly’s folder.
39
-
- The only exception to this should be when an assembly P/Invokes to its own native library that isn’t available to or consumed by anyone else, e.g. System.IO.Compression P/Invoking to clrcompression.dll. In such cases, System.IO.Compression should have its own Interop folder which follows a similar scheme as outlined in this proposal, but just for these private P/Invokes.
40
-
- Under Common\src\Interop, we’ll have a folder for each target platform, and within each platform, for each library from which functionality is being consumed. The Interop.*.cs files will live within those library folders, e.g.
60
+
61
+
- The Interop partial class definitions should live in Interop.*.cs
62
+
files. These Interop.*.cs files should all live under Common rather than
63
+
within a given assembly's folder.
64
+
- The only exception to this should be when an assembly P/Invokes to its
65
+
own native library that isn't available to or consumed by anyone else,
66
+
e.g. System.IO.Compression P/Invoking to clrcompression.dll. In such
67
+
cases, System.IO.Compression should have its own Interop folder which
68
+
follows a similar scheme as outlined in this proposal, but just for
69
+
these private P/Invokes.
70
+
- Under Common\src\Interop, we'll have a folder for each target
71
+
platform, and within each platform, for each library from which
72
+
functionality is being consumed. The Interop.*.cs files will live within
73
+
those library folders, e.g.
74
+
41
75
```
42
76
\Common\src\Interop
43
77
\Windows
@@ -50,11 +84,19 @@ internal static partial class Interop
50
84
\libc
51
85
... interop files
52
86
```
53
-
As shown above, platforms may be additive, in that an assembly may use functionality from multiple folders, e.g. System.IO.FileSystem’s Linux build will use functionality both from Unix (common across all Unix systems) and from Linux (specific to Linux and not available across non-Linux Unix systems).
87
+
88
+
As shown above, platforms may be additive, in that an assembly may use functionality from multiple folders, e.g. System.IO.FileSystem's Linux build will use functionality both from Unix (common across all Unix systems) and from Linux (specific to Linux and not available across non-Linux Unix systems).
54
89
55
-
- Interop.*.cs files are created in a way such that every assembly consuming the file will need every DllImport it contains.
56
-
- If multiple related DllImports will all be needed by every consumer, they may be declared in the same file, named for the functionality grouping, e.g. Interop.IOErrors.cs.
57
-
- Otherwise, in the limit (and the expected case for most situations) each Interop.*.cs file will contain a single DllImport and associated interop types (e.g. the structs used with that signature) and helper wrappers, e.g. Interop.strerror.cs.
90
+
- Interop.*.cs files are created in a way such that every assembly
91
+
consuming the file will need every DllImport it contains.
92
+
- If multiple related DllImports will all be needed by every consumer,
93
+
they may be declared in the same file, named for the functionality
94
+
grouping, e.g. Interop.IOErrors.cs.
95
+
- Otherwise, in the limit (and the expected case for most situations)
96
+
each Interop.*.cs file will contain a single DllImport and associated
97
+
interop types (e.g. the structs used with that signature) and helper
98
+
wrappers, e.g. Interop.strerror.cs.
99
+
58
100
```
59
101
\Common\src\Interop
60
102
\Unix
@@ -65,8 +107,14 @@ As shown above, platforms may be additive, in that an assembly may use functiona
65
107
\Interop.OutputDebugString.cs
66
108
```
67
109
68
-
- If structs/constants will be used on their own without an associated DllImport, or if they may be used with multiple DllImports not in the same file, they should be declared in a separate file.
69
-
- In the case of multiple overloads of the same DllImport (e.g. some overloads taking a SafeHandle and others taking an IntPtr, or overloads taking different kinds of SafeHandles), if they can’t all be declared in the same file (because they won’t all be consumed by all consumers), the file should be qualified with the key differentiator, e.g.
110
+
- If structs/constants will be used on their own without an associated
111
+
DllImport, or if they may be used with multiple DllImports not in the
112
+
same file, they should be declared in a separate file.
113
+
- In the case of multiple overloads of the same DllImport (e.g. some
114
+
overloads taking a SafeHandle and others taking an IntPtr, or overloads
115
+
taking different kinds of SafeHandles), if they can't all be declared in
116
+
the same file (because they won't all be consumed by all consumers), the
117
+
file should be qualified with the key differentiator, e.g.
70
118
71
119
```
72
120
\Common\src\Interop
@@ -76,7 +124,12 @@ As shown above, platforms may be additive, in that an assembly may use functiona
76
124
\Interop.DuplicateHandle_IntPtr.cs
77
125
```
78
126
79
-
- The library names used per-platform are stored in internal constants in the Interop class in a private Libraries class in a per-platform file named Interop.Libraries.cs. These constants are then used for all DllImports to that library, rather than having the string duplicated each time, e.g.
127
+
- The library names used per-platform are stored in internal constants
128
+
in the Interop class in a private Libraries class in a per-platform file
129
+
named Interop.Libraries.cs. These constants are then used for all
130
+
DllImports to that library, rather than having the string duplicated
131
+
each time, e.g.
132
+
80
133
```C#
81
134
internalstaticpartialclassInterop// contents of Common\src\Interop\Windows\Interop.Libraries.cs
82
135
{
@@ -92,8 +145,11 @@ internal static partial class Interop // contents of Common\src\Interop\Windows\
92
145
...
93
146
}
94
147
}
148
+
95
149
```
96
-
(Note that this will likely result in some extra constants defined in each assembly that uses interop, which minimally violates one of the goals, but it’s very minimal.)
150
+
(Note that this will likely result in some extra constants defined in
151
+
each assembly that uses interop, which minimally violates one of the
152
+
goals, but it's very minimal.)
97
153
98
154
- .csproj project files then include the interop code they need, e.g.
99
155
```XML
@@ -106,30 +162,45 @@ internal static partial class Interop // contents of Common\src\Interop\Windows\
When building CoreFx, we use the "OSGroup" property to control what target platform we are building for. The valid values for this property are Windows_NT (which is the default value from MSBuild when running on Windows), Linux and OSX.
169
+
When building CoreFx, we use the "OSGroup" property to control what
170
+
target platform we are building for. The valid values for this property
171
+
are Windows_NT (which is the default value from MSBuild when running on
172
+
Windows), Linux and OSX.
114
173
115
-
The build system sets a few MSBuild properties, depending on the OSGroup setting:
174
+
The build system sets a few MSBuild properties, depending on the OSGroup
175
+
setting:
116
176
117
177
* TargetsWindows
118
178
* TargetsLinux
119
179
* TargetsOSX
120
180
* TargetsUnix
121
181
122
-
TargetsUnix is true for both OSX and Linux builds and can be used to include code that can be used on both Linux and OSX (e.g. it is written against a POSIX API that is present on both platforms).
182
+
TargetsUnix is true for both OSX and Linux builds and can be used to
183
+
include code that can be used on both Linux and OSX (e.g. it is written
184
+
against a POSIX API that is present on both platforms).
123
185
124
-
You should not test the value of the OSGroup property directly, instead use one of the values above.
186
+
You should not test the value of the OSGroup property directly, instead
187
+
use one of the values above.
125
188
126
189
#### Project Files
127
-
Whenever possible, a single .csproj should be used per assembly, spanning all target platforms, e.g. System.Console.csproj includes conditional entries for when targeting Windows vs when targeting Linux. A property can be passed to msbuild to control which flavor is built, e.g. msbuild /p:OSGroup=OSX System.Console.csproj.
190
+
Whenever possible, a single .csproj should be used per assembly,
191
+
spanning all target platforms, e.g. System.Console.csproj includes
192
+
conditional entries for when targeting Windows vs when targeting Linux.
193
+
A property can be passed to msbuild to control which flavor is built,
194
+
e.g. msbuild /p:OSGroup=OSX System.Console.csproj.
128
195
129
196
### Constants
130
-
- Wherever possible, constants should be defined as "const". Only if the data type doesn’t support this (e.g. IntPtr) should they instead be static readonly fields.
197
+
- Wherever possible, constants should be defined as "const". Only if the
198
+
data type doesn't support this (e.g. IntPtr) should they instead be
199
+
static readonly fields.
200
+
201
+
- Related constants should be grouped under a partial, static, internal
202
+
type, e.g. for error codes they'd be grouped under an Errors type:
131
203
132
-
- Related constants should be grouped under a partial, static, internal type, e.g. for error codes they'd be grouped under an Errors type:
133
204
```C#
134
205
internalstaticpartialclassInterop
135
206
{
@@ -153,8 +224,23 @@ internal static partial class Interop
153
224
}
154
225
}
155
226
```
156
-
Using enums instead of partial, static classes can lead to needing lots of casts at call sites and can cause problems if such a type needs to be split across multiple files (enums can’t currently be partial). However, enums can be valuable in making it clear in a DllImport signature what values are permissible. Enums may be used in limited circumstances where these aren’t concerns: the full set of values can be represented in the enum, and the interop signature can be defined to use the enum type rather than the underlying integral type.
227
+
228
+
Using enums instead of partial, static classes can lead to needing lots
229
+
of casts at call sites and can cause problems if such a type needs to be
230
+
split across multiple files (enums can't currently be partial). However,
231
+
enums can be valuable in making it clear in a DllImport signature what
232
+
values are permissible. Enums may be used in limited circumstances where
233
+
these aren't concerns: the full set of values can be represented in the
234
+
enum, and the interop signature can be defined to use the enum type
235
+
rather than the underlying integral type.
157
236
158
237
## Naming
159
-
- Interop signatures / structs / constants should be defined using the same name / capitalization / etc. that’s used in the corresponding native code.
160
-
- We should not rename any of these based on managed coding guidelines. The only exception to this is for the constant grouping type, which should be named with the most discoverable name possible; if that name is a concept (e.g. Errors), it can be named using managed naming guidelines.
238
+
239
+
- Interop signatures / structs / constants should be defined using the
240
+
same name / capitalization / etc. that's used in the corresponding
241
+
native code.
242
+
- We should not rename any of these based on managed coding guidelines.
243
+
The only exception to this is for the constant grouping type, which
244
+
should be named with the most discoverable name possible; if that name
245
+
is a concept (e.g. Errors), it can be named using managed naming
0 commit comments