Files
SSCLI-sscli_20021101/docs/techinfo/native_managed_interop.html
yallie 9fa3874800 Added the contents of the archive.
Moved the original file to the archive subfolder.
2017-11-20 16:11:42 +03:00

299 lines
11 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Native and Managed Code Interop on Rotor</title>
<link rel="stylesheet" type="text/css" href="../rotor.css">
<body>
<h1 align="left">Native and Managed Code Interop in the Shared Source CLI</h1>
<p>The Microsoft&reg; Shared Source CLI (SSCLI) implementation supports reduced interoperation between native and managed code as compared to
the Microsoft .NET Framework implementation on Microsoft&reg; Windows&reg;.
Because of platform independence requirements, the SSCLI does not support COM
interop in any form.&nbsp; This includes calling from managed code out to an
unmanaged COM implementation as well as native code calling into managed code
through COM wrappers over managed classes.</p>
<p>The following tables indicate the interoperability options available on
either implementation.</p>
<table border="1" width="100%" >
<tr VALIGN="top">
<th width="21%">Managed Code Calling Native Code</th>
<th width="39%">Microsoft .NET Framework on Windows</th>
<th width="40%">Microsoft Shared Source CLI</th>
</tr>
<tr VALIGN="top">
<td width="21%">COM interop</td>
<td width="39%">Supported.&nbsp;
<p>Managed wrapper classes allow managed code to create instances of unmanaged COM
classes and invoke methods.</td>
<td width="40%">Not supported.</td>
</tr>
<tr VALIGN="top">
<td width="21%">Platform invoke</td>
<td width="39%">Supported.&nbsp;
<p>Managed code can call directly to stdcall or cdecl C-style functions
exported by dynamic libraries implemented in native code.</td>
<td width="40%">Supported.&nbsp;
<p>No changes from the .NET Frameworks on Windows.</td>
</tr>
</table>
<p>&nbsp;</p>
<table border="1" width="100%" >
<tr VALIGN="top">
<th width="21%">Native Code Calling Managed Code</th>
<th width="40%">Microsoft .NET Framework on Windows</th>
<th width="39%">Microsoft Shared Source CLI</th>
</tr>
<tr VALIGN="top">
<td width="21%">COM interop</td>
<td width="40%">Supported.&nbsp;
<p>Unmanaged code can use COM wrapper classes to create instances of managed classes
and invoke methods.</td>
<td width="39%">Not supported.</td>
</tr>
<tr VALIGN="top">
<td width="21%">Managed Extensions for C++ </td>
<td width="40%">Supported.&nbsp;
<p>Native C++ classes can be compiled in the same assembly as managed
classes and can interoperate directly.&nbsp; Managed methods can be exported
directly as dynamic library entry points.</td>
<td width="39%">Not supported.</td>
</tr>
<tr VALIGN="top">
<td width="21%">Delegates</td>
<td width="40%">Supported.&nbsp;
<p>Managed code implements a delegate that is exported as a function
pointer to native code.&nbsp; </td>
<td width="39%">Supported.
<p>No changes from the .NET Framework on Windows.</td>
</tr>
<tr VALIGN="top">
<td width="21%">Foreign Function Interface (FFI)</td>
<td width="40%">Not supported.</td>
<td width="39%">Supported.&nbsp;
<p>This is a light-weight substitute for COM interop.</td>
</tr>
</table>
<h2>Foreign Function Interface (FFI)</h2>
<p>Because the SSCLI implementation does not support either the Managed
Extensions for C++ or COM interop, the Foreign Function Interface (FFI) was
implemented.&nbsp; The FFI allows code implemented in unmanaged native C to
create instances of managed classes and invoke methods.&nbsp; The following
FFI class and interface definitions are located in <tt>
corffi.h </tt>and <tt>
mscoree.h. </tt>These files are generated during the build process.</p>
<hr>
<pre>/* modeled after System.Reflection.BindingFlags */
typedef enum CorFFIBindingFlags
{
CorFFIInvokeMethod = 0x0100,
// CreateInstance = 0x0200,
CorFFIGetField = 0x0400,
CorFFISetField = 0x0800,
CorFFIGetProperty = 0x1000,
CorFFISetProperty = 0x2000,
} CorFFIBindingFlags;
interface IManagedInstanceWrapper : IUnknown {
public:
HRESULT InvokeByName([in] LPCWSTR MemberName,
[in] INT32 BindingFlags, /* one or more of CorFFIBindingFlags */
[in] INT32 ArgCount,
[optional,in,out] VARIANT *ArgList,
[optional,out] VARIANT *pRetVal);
};
STDAPI
ClrCreateManagedInstance([in] LPCWSTR typeName,
[in] REFIID riid, /* only IID_IManagedInstanceWrapper and IID_IUnknown are valid for FFI */
[out] IManagedInstanceWrapper **ppInstanceWrapper);</pre>
<hr>
<p>The SSCLI platform adaptation layer (PAL) runtime supports a limited version of some COM-style types:&nbsp; rotor_palrt.h defines HRESULT, IUnknown* and VARIANT. Since
IManagedInstanceWrapper derives from IUnknown, FFI calls can return managed object references
by having the marshaler
create a new IManagedInstanceWrapper and store it in the VARIANT as an IUnknown*.&nbsp;&nbsp; This allows integer
types, floating point types, strings, and objects to be passed
through FFI in both directions ([in] parameters, [out] parameters,
and return values).</p>
<p>FFI is modeled on COM semantics, so some familiarity with COM programming is required to use it.&nbsp;
Standard COM reference-counting semantics must be followed.&nbsp; For more
details, consult
standard COM reference documentation.&nbsp; There are many
sources for information on COM programming, for example,
<a href="http://msdn.microsoft.com/library/en-us/dncomg/html/msdn_com_co.asp">
The COM Programmer's Cookbook</a> and many other articles on
<a href="http://msdn.microsoft.com">msdn.microsoft.com</a> as well as the
Microsoft Visual C++&reg; .NET documentation.</p>
<h4>FFI and Object References</h4>
<p>Object references are treated as vectors of function pointers.&nbsp;
</p>
<h4>FFI and Strings</h4>
<p>VT_BST should be used for by-value strings and VT_BSTR | VT_BYREF used for by-reference strings.</p>
<h4>FFI and VARIANT</h4>
<p>The following VARIANT types can be marshaled:</p>
<ul>
<li>VT_EMPTY</li>
<li>VT_NULL</li>
<li>VT_I2</li>
<li>VT_I4</li>
<li>VT_R4</li>
<li>VT_R8</li>
<li>VT_DATE</li>
<li>VT_BSTR</li>
<li>VT_BOOL</li>
<li>VT_UNKNOWN </li>
<li>VT_DECIMAL</li>
<li>VT_I1 </li>
<li>VT_UI1 </li>
<li>VT_UI2 </li>
<li>VT_UI4 </li>
<li>VT_I8</li>
<li>VT_UI8 </li>
<li>VT_INT</li>
<li>VT_UINT </li>
<li>VT_VOID</li>
</ul>
<p>Note: VT_BYREF can be marshaledbut VT_ARRAY is not supported.<h3>
FFI Sample Code</h3>
<pre>
IManagedInstanceWrapper *pWrap;
VARIANT RetVal;
HRESULT hr;
hr = ClrCreateManagedInstance(
L"System.Random,mscorlib,PublicKeyToken=b03f5f7f11d50a3a",
IID_IManagedInstanceWrapper,
(void**)&amp;pWrap);
if (FAILED(hr)) {
fprintf(stderr, "ClrCreateManagedInstance failed with hr=0x%08x\n", hr);
return;
}
VariantClear(&amp;RetVal);
hr = pWrap->InvokeByName(
L"Next", CorFFIInvokeMethod, 0, NULL, &amp;RetVal);
if (FAILED(hr)) {
fprintf(stderr, "InvokeMethodByName failed with hr=0x%08x\n", hr);
return;
}
printf("System.Random.Next() returned %d\n", V_I4(&amp;RetVal));
pWrap->Release();
</pre>
<h3>SSCLI FFI Implementation Location</h3>
<p>The implementation of the SSCLI FFI interop functionality can be found in the
following source files:</p>
<ul>
<li>%ROTOR_DIR%\clr\src\dlls\shim\shim.cpp</li>
<li>%ROTOR_DIR%\clr\src\inc\mscoree.h</li>
<li>%ROTOR_DIR%\clr\src\vm\ceemain.cpp</li>
<li>%ROTOR_DIR%\clr\src\inc\corffi.h</li>
<li>%ROTOR_DIR%\clr\src\vm\comcallwrapper.cpp</li>
<li>%ROTOR_DIR%\clr\src\vm\comcallwrapper.h</li>
</ul>
<p>The following tests show examples of using the FFI functionality:</p>
<ul>
<li>%ROTOR_DIR%\tests\dev\interoptest.cs</li>
<li>%ROTOR_DIR%\tests\dev\ffitest\ffitest.cpp</li>
</ul>
<h2>Wrapping cdecl Native Function Callbacks With Managed Delegates</h2>
<p>Both the .NET Framework on Windows and the SSCLI implementation support
delegates that can be exposed to native code as either stdcall or cdecl function
pointers.&nbsp; However, there is no existing syntax in C# to generate the
common intermediate language (CIL)
to support exporting delegates as cdecl function pointers.&nbsp; Note that this
only impacts cdecl APIs that are callback functions where the native code needs to call back
on a function pointer into the managed code for notification purposes. An
example of this would be a native API that requires a function pointer to call
back a function for
progress indication or resource enumeration.</p>
<p>There are several ways to work around this limitation.&nbsp; </p>
<ul>
<li>Create native wrappers over the cdecl APIs where the function pointers are
translated from cdecl to stdcall and back.&nbsp; You would only need to wrap
the API functions that require function pointers for callbacks.&nbsp; You can
then access the stdcall wrapper library from C# using delegates with platform
invoke for the callbacks.</li>
<li>Write the wrapper code in C# and modify the CIL.&nbsp;
<ul>
<li>Compile the wrapper code written in C# to an assembly.</li>
<li>Use the CIL Disassembler to store the resulting assembly as raw CIL.&nbsp;
</li>
<li>Modify the CIL to change the calling convention from stdcall to cdecl by
specifying the modopt for cdecl on the Invoke method of the delegate::
<blockquote>
<pre>&nbsp;modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)</pre>
</blockquote>
</li>
<li>Use the CIL Assembler to compile the modified CIL back into an assembly.</li>
</ul>
</li>
</ul>
<h2>Shared Source CLI Platform Invoke</h2>
<p>In general, the SSCLI implementation of a platform invoke method is the same as that
provided by the Microsoft .NET Framework on Windows.&nbsp; </p>
<p>For example,&nbsp; the SSCLI implementation follows the same rules as those
for the .NET Framework for string marshaling.
Strings are marshaled by read-only value.&nbsp; The value that is returned is the pinned
string object itself, so you can not modify it because the CLI string data type is immutable.</p>
<p>For some examples of using platform invoke in SSCLI please see
%ROTOR_DIR%\tests\dev\interoptest.cs.</p>
<p>Managed pointers an be marshaled using [MarshalAs(UnmanagedType.IUnknown)].&nbsp;
Because almost any type can be turned into the type Object through boxing, it
should be possible to marshal almost all types as IUnknown.&nbsp;&nbsp; If the
default marshaling does not work for your requirements, you can create a custom
marshaler in the same way as that supported in the .NET Framework.</p>
<p>Unlike the .NET Framework implementation of a platform invoke method, the SSCLI
implementation does not support VARIANT marshalling.<br>
&nbsp;</p>
<hr>
<p><i>Copyright (c) 2002 Microsoft Corporation. All rights reserved.</i></p>
</body>
</html>