summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/vm/method.cpp117
-rw-r--r--src/vm/method.hpp2
-rw-r--r--tests/src/Interop/CMakeLists.txt1
-rw-r--r--tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt14
-rw-r--r--tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp179
-rw-r--r--tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs110
-rw-r--r--tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj29
7 files changed, 401 insertions, 51 deletions
diff --git a/src/vm/method.cpp b/src/vm/method.cpp
index a2440ff92a..b16e27d33e 100644
--- a/src/vm/method.cpp
+++ b/src/vm/method.cpp
@@ -4948,6 +4948,51 @@ void NDirectMethodDesc::InterlockedSetNDirectFlags(WORD wFlags)
}
#ifndef CROSSGEN_COMPILE
+FARPROC NDirectMethodDesc::FindEntryPointWithMangling(HINSTANCE hMod, PTR_CUTF8 entryPointName) const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ FARPROC pFunc = GetProcAddress(hMod, entryPointName);
+
+#if defined(_TARGET_X86_)
+
+ if (pFunc)
+ {
+ return pFunc;
+ }
+
+ if (IsStdCall())
+ {
+ DWORD probedEntrypointNameLength = (DWORD)(strlen(entryPointName) + 1); // 1 for null terminator
+ int dstbufsize = (int)(sizeof(char) * (probedEntrypointNameLength + 10)); // 10 for stdcall mangling
+ LPSTR szProbedEntrypointName = ((LPSTR)_alloca(dstbufsize + 1));
+ szProbedEntrypointName[0] = '_';
+ strcpy_s(szProbedEntrypointName + 1, dstbufsize, entryPointName);
+ szProbedEntrypointName[probedEntrypointNameLength] = '\0'; // Add an extra '\0'.
+
+ UINT16 numParamBytesMangle = GetStackArgumentSize();
+
+ if (IsStdCallWithRetBuf())
+ {
+ _ASSERTE(numParamBytesMangle >= sizeof(LPVOID));
+ numParamBytesMangle -= (UINT16)sizeof(LPVOID);
+ }
+
+ sprintf_s(szProbedEntrypointName + probedEntrypointNameLength, dstbufsize - probedEntrypointNameLength + 1, "@%lu", (ULONG)numParamBytesMangle);
+ pFunc = GetProcAddress(hMod, szProbedEntrypointName);
+ }
+
+#endif
+
+ return pFunc;
+}
+
//*******************************************************************************
LPVOID NDirectMethodDesc::FindEntryPoint(HINSTANCE hMod) const
{
@@ -4959,84 +5004,54 @@ LPVOID NDirectMethodDesc::FindEntryPoint(HINSTANCE hMod) const
}
CONTRACTL_END;
- char const * funcName = NULL;
+ char const * funcName = GetEntrypointName();
- FARPROC pFunc = NULL, pFuncW = NULL;
+ FARPROC pFunc = NULL;
#ifndef FEATURE_PAL
// Handle ordinals.
- if (GetEntrypointName()[0] == '#')
+ if (funcName[0] == '#')
{
- long ordinal = atol(GetEntrypointName()+1);
+ long ordinal = atol(funcName + 1);
return reinterpret_cast<LPVOID>(GetProcAddress(hMod, (LPCSTR)(size_t)((UINT16)ordinal)));
}
#endif
- // Just look for the unmangled name. If it is unicode fcn, we are going
+ // Just look for the user-provided name without charset suffixes. If it is unicode fcn, we are going
// to need to check for the 'W' API because it takes precedence over the
// unmangled one (on NT some APIs have unmangled ANSI exports).
- pFunc = GetProcAddress(hMod, funcName = GetEntrypointName());
+ pFunc = FindEntryPointWithMangling(hMod, funcName);
if ((pFunc != NULL && IsNativeAnsi()) || IsNativeNoMangled())
{
- return (LPVOID)pFunc;
+ return reinterpret_cast<LPVOID>(pFunc);
}
+ DWORD probedEntrypointNameLength = (DWORD)(strlen(funcName) + 1); // +1 for charset decorations
+
// Allocate space for a copy of the entry point name.
- int dstbufsize = (int)(sizeof(char) * (strlen(GetEntrypointName()) + 1 + 20)); // +1 for the null terminator
- // +20 for various decorations
+ int dstbufsize = (int)(sizeof(char) * (probedEntrypointNameLength + 1)); // +1 for the null terminator
- // Allocate a single character before the start of this string to enable quickly
- // prepending a '_' character if we look for a stdcall entrypoint later on.
- LPSTR szAnsiEntrypointName = ((LPSTR)_alloca(dstbufsize + 1)) + 1;
+ LPSTR szProbedEntrypointName = ((LPSTR)_alloca(dstbufsize));
// Copy the name so we can mangle it.
- strcpy_s(szAnsiEntrypointName,dstbufsize,GetEntrypointName());
- DWORD nbytes = (DWORD)(strlen(GetEntrypointName()) + 1);
- szAnsiEntrypointName[nbytes] = '\0'; // Add an extra '\0'.
+ strcpy_s(szProbedEntrypointName, dstbufsize, funcName);
+ szProbedEntrypointName[probedEntrypointNameLength] = '\0'; // Add an extra '\0'.
-
- // If the program wants the ANSI api or if Unicode APIs are unavailable.
- if (IsNativeAnsi())
+ if(!IsNativeNoMangled())
{
- szAnsiEntrypointName[nbytes-1] = 'A';
- pFunc = GetProcAddress(hMod, funcName = szAnsiEntrypointName);
- }
- else
- {
- szAnsiEntrypointName[nbytes-1] = 'W';
- pFuncW = GetProcAddress(hMod, szAnsiEntrypointName);
+ szProbedEntrypointName[probedEntrypointNameLength - 1] = IsNativeAnsi() ? 'A' : 'W';
- // This overrides the unmangled API. See the comment above.
- if (pFuncW != NULL)
- {
- pFunc = pFuncW;
- funcName = szAnsiEntrypointName;
- }
- }
+ FARPROC pProbedFunc = FindEntryPointWithMangling(hMod, szProbedEntrypointName);
- if (!pFunc)
- {
-
-#if defined(_TARGET_X86_)
- /* try mangled names only for __stdcalls */
- if (!pFunc && IsStdCall())
+ if(pProbedFunc != NULL)
{
- UINT16 numParamBytesMangle = GetStackArgumentSize();
-
- if (IsStdCallWithRetBuf())
- {
- _ASSERTE(numParamBytesMangle >= sizeof(LPVOID));
- numParamBytesMangle -= (UINT16)sizeof(LPVOID);
- }
-
- szAnsiEntrypointName[-1] = '_';
- sprintf_s(szAnsiEntrypointName + nbytes - 1, dstbufsize - (nbytes - 1), "@%ld", (ULONG)numParamBytesMangle);
- pFunc = GetProcAddress(hMod, funcName = szAnsiEntrypointName - 1);
+ pFunc = pProbedFunc;
}
-#endif // _TARGET_X86_
+
+ probedEntrypointNameLength++;
}
- return (LPVOID)pFunc;
+ return reinterpret_cast<LPVOID>(pFunc);
}
#endif // CROSSGEN_COMPILE
diff --git a/src/vm/method.hpp b/src/vm/method.hpp
index 002985f4b8..414d2a33f0 100644
--- a/src/vm/method.hpp
+++ b/src/vm/method.hpp
@@ -2837,6 +2837,8 @@ public:
LPVOID FindEntryPoint(HINSTANCE hMod) const;
private:
+ FARPROC FindEntryPointWithMangling(HINSTANCE mod, PTR_CUTF8 entryPointName) const;
+
#ifdef MDA_SUPPORTED
Stub* GenerateStubForMDA(LPVOID pNativeTarget, Stub *pInnerStub);
#endif // MDA_SUPPORTED
diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt
index 934ab7a483..c81008bd2b 100644
--- a/tests/src/Interop/CMakeLists.txt
+++ b/tests/src/Interop/CMakeLists.txt
@@ -13,6 +13,7 @@ SET(CLR_INTEROP_TEST_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(common)
add_subdirectory(PInvoke/Primitives/Int)
+add_subdirectory(PInvoke/ExactSpelling)
add_subdirectory(NativeCallable)
add_subdirectory(PrimitiveMarshalling/Bool)
add_subdirectory(PrimitiveMarshalling/UIntPtr)
diff --git a/tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt b/tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt
new file mode 100644
index 0000000000..27850f4d3b
--- /dev/null
+++ b/tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required (VERSION 2.6)
+project (ExactSpellingNative)
+include_directories(${INC_PLATFORM_DIR})
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+set(SOURCES ExactSpellingNative.cpp)
+
+# add the executable
+add_library (ExactSpellingNative SHARED ${SOURCES})
+target_link_libraries(ExactSpellingNative ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS ExactSpellingNative DESTINATION bin)
+
+
diff --git a/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp
new file mode 100644
index 0000000000..a4dad9df67
--- /dev/null
+++ b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp
@@ -0,0 +1,179 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <stdio.h>
+#include <xplatform.h>
+
+int intManaged = 1000;
+int intNative = 2000;
+
+int intReturn = 3000;
+int intReturnA = 4000;
+int intReturnW = 5000;
+
+int intErrReturn = 6000;
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOut(/*[In,Out]*/int intValue)
+{
+ //Check the input
+ if(intValue != intManaged)
+ {
+ printf("Error in Function Marshal_Int_InOut(Native Client)\n");
+
+ //Expected
+ printf("Expected:%u\n", intManaged);
+
+ //Actual
+ printf("Actual:%u\n",intValue);
+
+ //Return the error value instead if verification failed
+ return intErrReturn;
+ }
+
+ //In-Place Change
+ intValue = intNative;
+
+ //Return
+ return intReturn;
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOutA(/*[In,Out]*/int intValue)
+{
+ //Check the input
+ if(intValue != intManaged)
+ {
+ printf("Error in Function Marshal_Int_InOutA(Native Client)\n");
+
+ //Expected
+ printf("Expected:%u\n", intManaged);
+
+ //Actual
+ printf("Actual:%u\n",intValue);
+
+ //Return the error value instead if verification failed
+ return intErrReturn;
+ }
+
+ //In-Place Change
+ intValue = intNative;
+
+ //Return
+ return intReturnA;
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOutW(/*[In,Out]*/int intValue)
+{
+ //Check the input
+ if(intValue != intManaged)
+ {
+ printf("Error in Function Marshal_Int_InOutW(Native Client)\n");
+
+ //Expected
+ printf("Expected:%u\n", intManaged);
+
+ //Actual
+ printf("Actual:%u\n",intValue);
+
+ //Return the error value instead if verification failed
+ return intErrReturn;
+ }
+
+ //In-Place Change
+ intValue = intNative;
+
+ //Return
+ return intReturnW;
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOut(/*[in,out]*/int *pintValue)
+{
+ //Check the input
+ if(*pintValue != intManaged)
+ {
+ printf("Error in Function MarshalPointer_Int_InOut(Native Client)\n");
+
+ //Expected
+ printf("Expected:%u\n", intManaged);
+
+ //Actual
+ printf("Actual:%u\n",*pintValue);
+
+ //Return the error value instead if verification failed
+ return intErrReturn;
+ }
+
+ //In-Place Change
+ *pintValue = intNative;
+
+ //Return
+ return intReturn;
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOutA(/*[in,out]*/int *pintValue)
+{
+ //Check the input
+ if(*pintValue != intManaged)
+ {
+ printf("Error in Function MarshalPointer_Int_InOutA(Native Client)\n");
+
+ //Expected
+ printf("Expected:%u\n", intManaged);
+
+ //Actual
+ printf("Actual:%u\n",*pintValue);
+
+ //Return the error value instead if verification failed
+ return intErrReturn;
+ }
+
+ //In-Place Change
+ *pintValue = intNative;
+
+ //Return
+ return intReturnA;
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOutW(/*[in,out]*/int *pintValue)
+{
+ //Check the input
+ if(*pintValue != intManaged)
+ {
+ printf("Error in Function MarshalPointer_Int_InOutW(Native Client)\n");
+
+ //Expected
+ printf("Expected:%u\n", intManaged);
+
+ //Actual
+ printf("Actual:%u\n",*pintValue);
+
+ //Return the error value instead if verification failed
+ return intErrReturn;
+ }
+
+ //In-Place Change
+ *pintValue = intNative;
+
+ //Return
+ return intReturnW;
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOut2A(/*[In,Out]*/int intValue)
+{
+ return Marshal_Int_InOutA(intValue);
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOut2W(/*[In,Out]*/int intValue)
+{
+ return Marshal_Int_InOutW(intValue);
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOut2A(/*[in,out]*/int *pintValue)
+{
+ return MarshalPointer_Int_InOutA(pintValue);
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOut2W(/*[in,out]*/int *pintValue)
+{
+ return MarshalPointer_Int_InOutW(pintValue);
+}
diff --git a/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs
new file mode 100644
index 0000000000..7616e6d6d8
--- /dev/null
+++ b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs
@@ -0,0 +1,110 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+class ExactSpellingTest
+{
+ class Ansi
+ {
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Ansi, ExactSpelling = true)]
+ public static extern int Marshal_Int_InOut([In, Out] int intValue);
+
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Ansi, ExactSpelling = true)]
+ public static extern int MarshalPointer_Int_InOut([In, Out] ref int intValue);
+
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Ansi, ExactSpelling = false)]
+ public static extern int Marshal_Int_InOut2([In, Out] int intValue);
+
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Ansi, ExactSpelling = false)]
+ public static extern int MarshalPointer_Int_InOut2([In, Out] ref int intValue);
+ }
+
+ class Unicode
+ {
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Unicode, ExactSpelling = true)]
+ public static extern int Marshal_Int_InOut([In, Out] int intValue);
+
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Unicode, ExactSpelling = true)]
+ public static extern int MarshalPointer_Int_InOut([In, Out] ref int intValue);
+
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Unicode, ExactSpelling = false)]
+ public static extern int Marshal_Int_InOut2([In, Out] int intValue);
+
+ [DllImport("ExactSpellingNative", CharSet = CharSet.Unicode, ExactSpelling = false)]
+ public static extern int MarshalPointer_Int_InOut2([In, Out] ref int intValue);
+ }
+
+ public static int Main(string[] args)
+ {
+ int failures = 0;
+ int intManaged = 1000;
+ int intNative = 2000;
+ int intReturn = 3000;
+
+ Console.WriteLine("Method Unicode.Marshal_Int_InOut: ExactSpelling = true");
+ int int1 = intManaged;
+ int intRet1 = Unicode.Marshal_Int_InOut(int1);
+ failures += Verify(intReturn, intManaged, intRet1, int1);
+
+ Console.WriteLine("Method Unicode.MarshalPointer_Int_InOut: ExactSpelling = true");
+ int int2 = intManaged;
+ int intRet2 = Unicode.MarshalPointer_Int_InOut(ref int2);
+
+ failures += Verify(intReturn, intNative, intRet2, int2);
+
+ Console.WriteLine("Method Ansi.Marshal_Int_InOut: ExactSpelling = true");
+ int int3 = intManaged;
+ int intRet3 = Ansi.Marshal_Int_InOut(int3);
+ failures += Verify(intReturn, intManaged, intRet3, int3);
+
+ Console.WriteLine("Method Ansi.MarshalPointer_Int_InOut: ExactSpelling = true");
+ int int4 = intManaged;
+ int intRet4 = Ansi.MarshalPointer_Int_InOut(ref int4);
+ failures += Verify(intReturn, intNative, intRet4, int4);
+
+ int intReturnAnsi = 4000;
+ int intReturnUnicode = 5000;
+
+ Console.WriteLine("Method Unicode.Marshal_Int_InOut2: ExactSpelling = false");
+ int int5 = intManaged;
+ int intRet5 = Unicode.Marshal_Int_InOut2(int5);
+ failures += Verify(intReturnUnicode, intManaged, intRet5, int5);
+
+ Console.WriteLine("Method Unicode.MarshalPointer_Int_InOut2: ExactSpelling = false");
+ int int6 = intManaged;
+ int intRet6 = Unicode.MarshalPointer_Int_InOut2(ref int6);
+ failures += Verify(intReturnUnicode, intNative, intRet6, int6);
+
+ Console.WriteLine("Method Ansi.Marshal_Int_InOut2: ExactSpelling = false");
+ int int7 = intManaged;
+ int intRet7 = Ansi.Marshal_Int_InOut2(int7);
+ failures += Verify(intReturnAnsi, intManaged, intRet7, int7);
+
+ Console.WriteLine("Method Ansi.MarshalPointer_Int_InOut2: ExactSpelling = false");
+ int int8 = intManaged;
+ int intRet8 = Ansi.MarshalPointer_Int_InOut2(ref int8);
+ failures += Verify(intReturnAnsi, intNative, intRet8, int8);
+
+ return 100 + failures;
+ }
+
+ private static int Verify(int expectedReturnValue, int expectedParameterValue, int actualReturnValue, int actualParameterValue)
+ {
+ int failures = 0;
+ if (expectedReturnValue != actualReturnValue)
+ {
+ failures++;
+ Console.WriteLine($"The return value is wrong. Expected {expectedReturnValue}, got {actualReturnValue}");
+ }
+ if (expectedParameterValue != actualParameterValue)
+ {
+ failures++;
+ Console.WriteLine($"The parameter value is changed. Expected {expectedParameterValue}, got {actualParameterValue}");
+ }
+
+ return failures;
+ }
+}
diff --git a/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj
new file mode 100644
index 0000000000..77a6dcc087
--- /dev/null
+++ b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>ExactSpellingTest</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"></PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'"></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ExactSpellingTest.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="CMakeLists.txt" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>