}
}
+ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signature, out CorInfoCallConv callConv)
+ {
+ callConv = CorInfoCallConv.CORINFO_CALLCONV_UNMANAGED;
+ if (!signature.HasEmbeddedSignatureData || signature.GetEmbeddedSignatureData() == null)
+ return false;
+
+ foreach (EmbeddedSignatureData data in signature.GetEmbeddedSignatureData())
+ {
+ if (data.kind != EmbeddedSignatureDataKind.OptionalCustomModifier)
+ continue;
+
+ // We only care about the modifiers for the return type. These will be at the start of
+ // the signature, so will be first in the array of embedded signature data.
+ if (data.index != MethodSignature.IndexOfCustomModifiersOnReturnType)
+ break;
+
+ if (!(data.type is DefType defType))
+ continue;
+
+ if (defType.Namespace != "System.Runtime.CompilerServices")
+ continue;
+
+ // Take the first recognized calling convention in metadata.
+ switch (defType.Name)
+ {
+ case "CallConvCdecl":
+ callConv = CorInfoCallConv.CORINFO_CALLCONV_C;
+ return true;
+ case "CallConvStdcall":
+ callConv = CorInfoCallConv.CORINFO_CALLCONV_STDCALL;
+ return true;
+ case "CallConvFastcall":
+ callConv = CorInfoCallConv.CORINFO_CALLCONV_FASTCALL;
+ return true;
+ case "CallConvThiscall":
+ callConv = CorInfoCallConv.CORINFO_CALLCONV_THISCALL;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig)
{
sig->callConv = (CorInfoCallConv)(signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask);
if (!signature.IsStatic) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_HASTHIS;
+ // Unmanaged calling convention indicates modopt should be read
+ if (sig->callConv == CorInfoCallConv.CORINFO_CALLCONV_UNMANAGED)
+ {
+ if (TryGetUnmanagedCallingConventionFromModOpt(signature, out CorInfoCallConv callConvMaybe))
+ {
+ sig->callConv = callConvMaybe;
+ }
+ else
+ {
+ // Use platform default
+ sig->callConv = _compilation.TypeSystemContext.Target.IsWindows
+ ? CorInfoCallConv.CORINFO_CALLCONV_STDCALL
+ : CorInfoCallConv.CORINFO_CALLCONV_C;
+ }
+ }
+
TypeDesc returnType = signature.ReturnType;
CorInfoType corInfoRetType = asCorInfoType(signature.ReturnType, &sig->retTypeClass);
internal TypeDesc[] _parameters;
internal EmbeddedSignatureData[] _embeddedSignatureData;
+ // Value of <see cref="EmbeddedSignatureData.index" /> for any custom modifiers on the return type
+ public const string IndexOfCustomModifiersOnReturnType = "0.1.1.1";
+
public MethodSignature(MethodSignatureFlags flags, int genericParameterCount, TypeDesc returnType, TypeDesc[] parameters, EmbeddedSignatureData[] embeddedSignatureData = null)
{
_flags = flags;
#endif // !TARGET_UNIX
}
+namespace
+{
+ bool TryConvertCallConvValueToPInvokeCallConv(_In_ BYTE callConv, _Out_ CorPinvokeMap *pPinvokeMapOut)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ switch (callConv)
+ {
+ case IMAGE_CEE_CS_CALLCONV_C:
+ *pPinvokeMapOut = pmCallConvCdecl;
+ return true;
+ case IMAGE_CEE_CS_CALLCONV_STDCALL:
+ *pPinvokeMapOut = pmCallConvStdcall;
+ return true;
+ case IMAGE_CEE_CS_CALLCONV_THISCALL:
+ *pPinvokeMapOut = pmCallConvThiscall;
+ return true;
+ case IMAGE_CEE_CS_CALLCONV_FASTCALL:
+ *pPinvokeMapOut = pmCallConvFastcall;
+ return true;
+ }
+
+ return false;
+ }
+
+ HRESULT GetUnmanagedPInvokeCallingConvention(
+ _In_ Module *pModule,
+ _In_ PCCOR_SIGNATURE pSig,
+ _In_ ULONG cSig,
+ _Out_ CorPinvokeMap *pPinvokeMapOut)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ CorUnmanagedCallingConvention callConvMaybe;
+ HRESULT hr = MetaSig::TryGetUnmanagedCallingConventionFromModOpt(pModule, pSig, cSig, &callConvMaybe);
+ if (hr != S_OK)
+ return hr;
+
+ if (!TryConvertCallConvValueToPInvokeCallConv(callConvMaybe, pPinvokeMapOut))
+ return S_FALSE;
+
+ return hr;
+ }
+}
+
void PInvokeStaticSigInfo::InitCallConv(CorPinvokeMap callConv, BOOL bIsVarArg)
{
CONTRACTL
callConv = GetDefaultCallConv(bIsVarArg);
CorPinvokeMap sigCallConv = (CorPinvokeMap)0;
- BOOL fSuccess = MetaSig::GetUnmanagedCallingConvention(m_pModule, m_sig.GetRawSig(), m_sig.GetRawSigLen(), &sigCallConv);
-
- if (!fSuccess)
+ HRESULT hr = GetUnmanagedPInvokeCallingConvention(m_pModule, m_sig.GetRawSig(), m_sig.GetRawSigLen(), &sigCallConv);
+ if (FAILED(hr))
{
SetError(IDS_EE_NDIRECT_BADNATL); //Bad metadata format
}
dwStubFlags |= NDIRECTSTUB_FL_UNMANAGED_CALLI;
// need to convert the CALLI signature to stub signature with managed calling convention
- switch (MetaSig::GetCallingConvention(pVASigCookie->pModule, pVASigCookie->signature))
+ BYTE callConv = MetaSig::GetCallingConvention(pVASigCookie->pModule, signature);
+
+ // Unmanaged calling convention indicates modopt should be read
+ if (callConv == IMAGE_CEE_CS_CALLCONV_UNMANAGED)
{
- case IMAGE_CEE_CS_CALLCONV_C:
- unmgdCallConv = pmCallConvCdecl;
- break;
- case IMAGE_CEE_CS_CALLCONV_STDCALL:
- unmgdCallConv = pmCallConvStdcall;
- break;
- case IMAGE_CEE_CS_CALLCONV_THISCALL:
- unmgdCallConv = pmCallConvThiscall;
- break;
- case IMAGE_CEE_CS_CALLCONV_FASTCALL:
- unmgdCallConv = pmCallConvFastcall;
- break;
- case IMAGE_CEE_CS_CALLCONV_UNMANAGED:
- COMPlusThrow(kNotImplementedException);
- default:
- COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
+ CorUnmanagedCallingConvention callConvMaybe;
+ if (S_OK == MetaSig::TryGetUnmanagedCallingConventionFromModOpt(pVASigCookie->pModule, signature.GetRawSig(), signature.GetRawSigLen(), &callConvMaybe))
+ {
+ callConv = callConvMaybe;
+ }
+ else
+ {
+ callConv = MetaSig::GetDefaultUnmanagedCallingConvention();
+ }
}
+ if (!TryConvertCallConvValueToPInvokeCallConv(callConv, &unmgdCallConv))
+ COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
+
LoaderHeap *pHeap = pVASigCookie->pModule->GetLoaderAllocator()->GetHighFrequencyHeap();
PCOR_SIGNATURE new_sig = (PCOR_SIGNATURE)(void *)pHeap->AllocMem(S_SIZE_T(signature.GetRawSigLen()));
CopyMemory(new_sig, signature.GetRawSig(), signature.GetRawSigLen());
SigTypeContext::InitTypeContext(contextType, &typeContext);
}
- _ASSERTE(CORINFO_CALLCONV_DEFAULT == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_DEFAULT);
- _ASSERTE(CORINFO_CALLCONV_VARARG == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_VARARG);
- _ASSERTE(CORINFO_CALLCONV_MASK == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_MASK);
- _ASSERTE(CORINFO_CALLCONV_HASTHIS == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_HASTHIS);
+ static_assert_no_msg(CORINFO_CALLCONV_DEFAULT == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_DEFAULT);
+ static_assert_no_msg(CORINFO_CALLCONV_VARARG == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_VARARG);
+ static_assert_no_msg(CORINFO_CALLCONV_MASK == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_MASK);
+ static_assert_no_msg(CORINFO_CALLCONV_HASTHIS == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_HASTHIS);
TypeHandle typeHnd = TypeHandle();
}
#endif // defined(TARGET_UNIX) || defined(TARGET_ARM)
+ // Unmanaged calling convention indicates modopt should be read
+ if (sigRet->callConv == CORINFO_CALLCONV_UNMANAGED)
+ {
+ static_assert_no_msg(CORINFO_CALLCONV_C == (CorInfoCallConv)IMAGE_CEE_UNMANAGED_CALLCONV_C);
+ static_assert_no_msg(CORINFO_CALLCONV_STDCALL == (CorInfoCallConv)IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL);
+ static_assert_no_msg(CORINFO_CALLCONV_THISCALL == (CorInfoCallConv)IMAGE_CEE_UNMANAGED_CALLCONV_THISCALL);
+ static_assert_no_msg(CORINFO_CALLCONV_FASTCALL == (CorInfoCallConv)IMAGE_CEE_UNMANAGED_CALLCONV_FASTCALL);
+
+ CorUnmanagedCallingConvention callConvMaybe;
+ if (S_OK == MetaSig::TryGetUnmanagedCallingConventionFromModOpt(module, pSig, cbSig, &callConvMaybe))
+ {
+ sigRet->callConv = (CorInfoCallConv)callConvMaybe;
+ }
+ else
+ {
+ sigRet->callConv = (CorInfoCallConv)MetaSig::GetDefaultUnmanagedCallingConvention();
+ }
+ }
+
// Skip number of type arguments
if (sigRet->callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
IfFailThrow(sig.GetData(NULL));
sigRet->args = (CORINFO_ARG_LIST_HANDLE)sig.GetPtr();
}
- if (sigRet->callConv == CORINFO_CALLCONV_UNMANAGED)
- {
- COMPlusThrowHR(E_NOTIMPL);
- }
+ _ASSERTE(sigRet->callConv != CORINFO_CALLCONV_UNMANAGED);
// Set computed flags
sigRet->flags = sigRetFlags;
#ifndef DACCESS_COMPILE
+namespace
+{
+ HRESULT GetNameOfTypeRefOrDef(
+ _In_ const Module *pModule,
+ _In_ mdToken token,
+ _Out_ LPCSTR *namespaceOut,
+ _Out_ LPCSTR *nameOut)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ IMDInternalImport *pInternalImport = pModule->GetMDImport();
+ if (TypeFromToken(token) == mdtTypeDef)
+ {
+ HRESULT hr = pInternalImport->GetNameOfTypeDef(token, nameOut, namespaceOut);
+ if (FAILED(hr))
+ return hr;
+ }
+ else if (TypeFromToken(token) == mdtTypeRef)
+ {
+ HRESULT hr = pInternalImport->GetNameOfTypeRef(token, namespaceOut, nameOut);
+ if (FAILED(hr))
+ return hr;
+ }
+ else
+ {
+ return E_INVALIDARG;
+ }
+
+ return S_OK;
+ }
+}
+
//----------------------------------------------------------
// Returns the unmanaged calling convention.
//----------------------------------------------------------
/*static*/
-BOOL
-MetaSig::GetUnmanagedCallingConvention(
- Module * pModule,
- PCCOR_SIGNATURE pSig,
- ULONG cSig,
- CorPinvokeMap * pPinvokeMapOut)
+HRESULT
+MetaSig::TryGetUnmanagedCallingConventionFromModOpt(
+ _In_ Module *pModule,
+ _In_ PCCOR_SIGNATURE pSig,
+ _In_ ULONG cSig,
+ _Out_ CorUnmanagedCallingConvention *callConvOut)
{
CONTRACTL
{
GC_NOTRIGGER;
FORBID_FAULT;
MODE_ANY;
+ PRECONDITION(callConvOut != NULL);
}
CONTRACTL_END
-
// Instantiations aren't relevant here
MetaSig msig(pSig, cSig, pModule, NULL);
PCCOR_SIGNATURE pWalk = msig.m_pRetType.GetPtr();
_ASSERTE(pWalk <= pSig + cSig);
+
+ *callConvOut = (CorUnmanagedCallingConvention)0;
while ((pWalk < (pSig + cSig)) && ((*pWalk == ELEMENT_TYPE_CMOD_OPT) || (*pWalk == ELEMENT_TYPE_CMOD_REQD)))
{
BOOL fIsOptional = (*pWalk == ELEMENT_TYPE_CMOD_OPT);
pWalk++;
if (pWalk + CorSigUncompressedDataSize(pWalk) > pSig + cSig)
- {
- return FALSE; // Bad formatting
- }
+ return E_FAIL; // Bad formatting
+
mdToken tk;
pWalk += CorSigUncompressToken(pWalk, &tk);
- if (fIsOptional)
+ if (!fIsOptional)
+ continue;
+
+ LPCSTR typeNamespace;
+ LPCSTR typeName;
+
+ // Check for CallConv types specified in modopt
+ if (FAILED(GetNameOfTypeRefOrDef(pModule, tk, &typeNamespace, &typeName)))
+ continue;
+
+ if (::strcmp(typeNamespace, CMOD_CALLCONV_NAMESPACE) != 0)
+ continue;
+
+ const struct {
+ LPCSTR name;
+ CorUnmanagedCallingConvention value;
+ } knownCallConvs[] = {
+ { CMOD_CALLCONV_NAME_CDECL, IMAGE_CEE_UNMANAGED_CALLCONV_C },
+ { CMOD_CALLCONV_NAME_STDCALL, IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL },
+ { CMOD_CALLCONV_NAME_THISCALL, IMAGE_CEE_UNMANAGED_CALLCONV_THISCALL },
+ { CMOD_CALLCONV_NAME_FASTCALL, IMAGE_CEE_UNMANAGED_CALLCONV_FASTCALL } };
+
+ for (const auto &callConv : knownCallConvs)
{
- if (IsTypeRefOrDef("System.Runtime.CompilerServices.CallConvCdecl", pModule, tk))
+ // Take the first recognized calling convention in metadata.
+ if (::strcmp(typeName, callConv.name) == 0)
{
- *pPinvokeMapOut = pmCallConvCdecl;
- return TRUE;
- }
- else if (IsTypeRefOrDef("System.Runtime.CompilerServices.CallConvStdcall", pModule, tk))
- {
- *pPinvokeMapOut = pmCallConvStdcall;
- return TRUE;
- }
- else if (IsTypeRefOrDef("System.Runtime.CompilerServices.CallConvThiscall", pModule, tk))
- {
- *pPinvokeMapOut = pmCallConvThiscall;
- return TRUE;
- }
- else if (IsTypeRefOrDef("System.Runtime.CompilerServices.CallConvFastcall", pModule, tk))
- {
- *pPinvokeMapOut = pmCallConvFastcall;
- return TRUE;
+ *callConvOut = callConv.value;
+ return S_OK;
}
}
}
- *pPinvokeMapOut = (CorPinvokeMap)0;
- return TRUE;
+ return S_FALSE;
}
//---------------------------------------------------------------------------------------
}
//----------------------------------------------------------
- // Returns the unmanaged calling convention.
+ // Gets the unmanaged calling convention by reading any modopts.
+ // If there are multiple modopts specifying recognized calling
+ // conventions, the first one that is found in the metadata wins.
+ // Note: the order in the metadata is the reverse of that in IL.
+ //
+ // Returns:
+ // E_FAIL - Signature had an invalid format
+ // S_OK - Calling convention was read from modopt
+ // S_FALSE - Calling convention was not read from modopt
//----------------------------------------------------------
- static BOOL GetUnmanagedCallingConvention(Module *pModule, PCCOR_SIGNATURE pSig, ULONG cSig, CorPinvokeMap *pPinvokeMapOut);
+ static HRESULT TryGetUnmanagedCallingConventionFromModOpt(
+ _In_ Module *pModule,
+ _In_ PCCOR_SIGNATURE pSig,
+ _In_ ULONG cSig,
+ _Out_ CorUnmanagedCallingConvention *callConvOut);
+
+ static CorUnmanagedCallingConvention GetDefaultUnmanagedCallingConvention()
+ {
+#ifdef TARGET_UNIX
+ return IMAGE_CEE_UNMANAGED_CALLCONV_C;
+#else // TARGET_UNIX
+ return IMAGE_CEE_UNMANAGED_CALLCONV_STDCALL;
+#endif // !TARGET_UNIX
+ }
//------------------------------------------------------------------
// Like NextArg, but return only normalized type (enums flattned to
ret
}
+ .method public hidebysig static int32 CallUnmanagedIntInt_ModOptCdecl(void* p, int32 b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged int32 modopt([System.Runtime]System.Runtime.CompilerServices.CallConvCdecl) (int32)
+ ret
+ }
+
+ .method public hidebysig static int32 CallUnmanagedIntInt_ModOptStdcall(void* p, int32 b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged int32 modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) (int32)
+ ret
+ }
+
+ .method public hidebysig static int32 CallUnmanagedIntInt_ModOptUnknown(void* p, int32 b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged int32 modopt([System.Runtime]System.Runtime.CompilerServices.RuntimeFeature) (int32)
+ ret
+ }
+
+ .method public hidebysig static int32 CallUnmanagedIntInt_ModOptStdcall_ModOptCdecl(void* p, int32 b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged int32 modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) modopt([System.Runtime]System.Runtime.CompilerServices.CallConvCdecl) (int32)
+ ret
+ }
+
+ .method public hidebysig static int32 CallUnmanagedIntInt_ModOptStdcall_ModOptUnknown(void* p, int32 b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged int32 modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) modopt([System.Runtime]System.Runtime.CompilerServices.RuntimeFeature) (int32)
+ ret
+ }
+
+ .method public hidebysig static int32 CallUnmanagedCdeclIntInt_ModOptStdcall(void* p, int32 b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged cdecl int32 modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) (int32)
+ ret
+ }
+
+ .method public hidebysig static int32 CallUnmanagedStdcallIntInt_ModOptCdecl(void* p, int32 b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged stdcall int32 modopt([System.Runtime]System.Runtime.CompilerServices.CallConvCdecl) (int32)
+ ret
+ }
+
.method public hidebysig static char CallUnmanagedCharChar(void* p, char b) cil managed
{
.maxstack 2
calli unmanaged stdcall char(char)
ret
}
+
+ .method public hidebysig static char CallUnmanagedCharChar_ModOptCdecl(void* p, char b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged char modopt([System.Runtime]System.Runtime.CompilerServices.CallConvCdecl) (char)
+ ret
+ }
+
+ .method public hidebysig static char CallUnmanagedCharChar_ModOptStdcall(void* p, char b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged char modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) (char)
+ ret
+ }
+
+ .method public hidebysig static char CallUnmanagedCharChar_ModOptUnknown(void* p, char b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged char modopt([System.Runtime]System.Runtime.CompilerServices.RuntimeFeature) (char)
+ ret
+ }
+
+ .method public hidebysig static char CallUnmanagedCharChar_ModOptStdcall_ModOptCdecl(void* p, char b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged char modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) modopt([System.Runtime]System.Runtime.CompilerServices.CallConvCdecl) (char)
+ ret
+ }
+
+ .method public hidebysig static char CallUnmanagedCharChar_ModOptStdcall_ModOptUnknown(void* p, char b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged char modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) modopt([System.Runtime]System.Runtime.CompilerServices.RuntimeFeature) (char)
+ ret
+ }
+
+ .method public hidebysig static char CallUnmanagedCdeclCharChar_ModOptStdcall(void* p, char b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged cdecl char modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) (char)
+ ret
+ }
+
+ .method public hidebysig static char CallUnmanagedStdcallCharChar_ModOptCdecl(void* p, char b) cil managed
+ {
+ .maxstack 2
+ ldarg.1
+ ldarg.0
+ calli unmanaged stdcall char modopt([System.Runtime]System.Runtime.CompilerServices.CallConvCdecl) (char)
+ ret
+ }
}
Console.WriteLine($"Running {nameof(BlittableFunctionPointers)}...");
IntPtr mod = NativeLibrary.Load(NativeFunctions.GetFullPath());
+ var cbDefault = NativeLibrary.GetExport(mod, "DoubleInt").ToPointer();
+ var cbCdecl = NativeLibrary.GetExport(mod, "DoubleIntCdecl").ToPointer();
+ var cbStdcall = NativeLibrary.GetExport(mod, "DoubleIntStdcall").ToPointer();
const int a = 7;
const int expected = a * 2;
+
+ {
+ // No modopt
+ Console.WriteLine($" -- unmanaged");
+ int b = CallFunctionPointers.CallUnmanagedIntInt(cbDefault, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ Console.WriteLine($" -- unmanaged cdecl");
+ int b = CallFunctionPointers.CallUnmanagedCdeclIntInt(cbCdecl, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ Console.WriteLine($" -- unmanaged stdcall");
+ int b = CallFunctionPointers.CallUnmanagedStdcallIntInt(cbStdcall, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ Console.WriteLine($" -- unmanaged modopt(cdecl)");
+ int b = CallFunctionPointers.CallUnmanagedIntInt_ModOptCdecl(cbCdecl, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ Console.WriteLine($" -- unmanaged modopt(stdcall)");
+ int b = CallFunctionPointers.CallUnmanagedIntInt_ModOptStdcall(cbStdcall, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ // Value in modopt is not a recognized calling convention
+ Console.WriteLine($" -- unmanaged modopt unrecognized");
+ int b = CallFunctionPointers.CallUnmanagedIntInt_ModOptUnknown(cbDefault, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ // Multiple modopts with calling conventions
+ Console.WriteLine($" -- unmanaged modopt(stdcall) modopt(cdecl)");
+ int b = CallFunctionPointers.CallUnmanagedIntInt_ModOptStdcall_ModOptCdecl(cbCdecl, a);
+ Assert.AreEqual(expected, b);
+ }
+
{
- var cb = NativeLibrary.GetExport(mod, "DoubleInt").ToPointer();
- Assert.Throws<NotImplementedException>(() => CallFunctionPointers.CallUnmanagedIntInt(cb, a));
+ Console.WriteLine($" -- unmanaged modopt(stdcall) modopt(unrecognized)");
+ int b = CallFunctionPointers.CallUnmanagedIntInt_ModOptStdcall_ModOptUnknown(cbStdcall, a);
+ Assert.AreEqual(expected, b);
}
{
- var cb = NativeLibrary.GetExport(mod, "DoubleIntCdecl").ToPointer();
- int b = CallFunctionPointers.CallUnmanagedCdeclIntInt(cb, a);
- Assert.AreEqual(b, expected);
+ Console.WriteLine($" -- unmanaged cdecl modopt(stdcall)");
+ int b = CallFunctionPointers.CallUnmanagedCdeclIntInt_ModOptStdcall(cbCdecl, a);
+ Assert.AreEqual(expected, b);
}
{
- var cb = NativeLibrary.GetExport(mod, "DoubleIntStdcall").ToPointer();
- int b = CallFunctionPointers.CallUnmanagedStdcallIntInt(cb, a);
- Assert.AreEqual(b, expected);
+ Console.WriteLine($" -- unmanaged stdcall modopt(cdecl)");
+ int b = CallFunctionPointers.CallUnmanagedStdcallIntInt_ModOptCdecl(cbStdcall, a);
+ Assert.AreEqual(expected, b);
}
}
Console.WriteLine($"Running {nameof(NonblittableFunctionPointers)}...");
IntPtr mod = NativeLibrary.Load(NativeFunctions.GetFullPath());
+ var cbDefault = NativeLibrary.GetExport(mod, "ToUpper").ToPointer();
+ var cbCdecl = NativeLibrary.GetExport(mod, "ToUpperCdecl").ToPointer();
+ var cbStdcall = NativeLibrary.GetExport(mod, "ToUpperStdcall").ToPointer();
const char a = 'i';
const char expected = 'I';
+
+ {
+ // No modopt
+ Console.WriteLine($" -- unmanaged");
+ var b = CallFunctionPointers.CallUnmanagedCharChar(cbDefault, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ Console.WriteLine($" -- unmanaged cdecl");
+ var b = CallFunctionPointers.CallUnmanagedCdeclCharChar(cbCdecl, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ Console.WriteLine($" -- unmanaged stdcall");
+ var b = CallFunctionPointers.CallUnmanagedStdcallCharChar(cbStdcall, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ Console.WriteLine($" -- unmanaged modopt(cdecl)");
+ var b = CallFunctionPointers.CallUnmanagedCharChar_ModOptCdecl(cbCdecl, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ Console.WriteLine($" -- unmanaged modopt(stdcall)");
+ var b = CallFunctionPointers.CallUnmanagedCharChar_ModOptStdcall(cbStdcall, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ // Value in modopt is not a recognized calling convention
+ Console.WriteLine($" -- unmanaged modopt(unrecognized)");
+ var b = CallFunctionPointers.CallUnmanagedCharChar_ModOptUnknown(cbDefault, a);
+ Assert.AreEqual(expected, b);
+ }
+
+ {
+ // Multiple modopts with calling conventions
+ Console.WriteLine($" -- unmanaged modopt(stdcall) modopt(cdecl)");
+ var b = CallFunctionPointers.CallUnmanagedCharChar_ModOptStdcall_ModOptCdecl(cbCdecl, a);
+ Assert.AreEqual(expected, b);
+ }
+
{
- var cb = NativeLibrary.GetExport(mod, "ToUpper").ToPointer();
- Assert.Throws<NotImplementedException>(() => CallFunctionPointers.CallUnmanagedCharChar(cb, a));
+ Console.WriteLine($" -- unmanaged modopt(stdcall) modopt(unrecognized)");
+ var b = CallFunctionPointers.CallUnmanagedCharChar_ModOptStdcall_ModOptUnknown(cbStdcall, a);
+ Assert.AreEqual(expected, b);
}
{
- var cb = NativeLibrary.GetExport(mod, "ToUpperCdecl").ToPointer();
- var b = CallFunctionPointers.CallUnmanagedCdeclCharChar(cb, a);
- Assert.AreEqual(b, expected);
+ Console.WriteLine($" -- unmanaged cdecl modopt(stdcall)");
+ var b = CallFunctionPointers.CallUnmanagedCdeclCharChar_ModOptStdcall(cbCdecl, a);
+ Assert.AreEqual(expected, b);
}
{
- var cb = NativeLibrary.GetExport(mod, "ToUpperStdcall").ToPointer();
- var b = CallFunctionPointers.CallUnmanagedStdcallCharChar(cb, a);
- Assert.AreEqual(b, expected);
+ Console.WriteLine($" -- unmanaged stdcall modopt(cdecl)");
+ var b = CallFunctionPointers.CallUnmanagedStdcallCharChar_ModOptCdecl(cbStdcall, a);
+ Assert.AreEqual(expected, b);
}
}