Add test for ICLRRuntimeHost::ExecuteInDefaultAppDomain
authorSteve MacLean <Steve.MacLean@microsoft.com>
Fri, 12 Oct 2018 19:10:06 +0000 (15:10 -0400)
committerSteve MacLean <stmaclea@microsoft.com>
Thu, 25 Oct 2018 17:21:48 +0000 (13:21 -0400)
tests/src/Interop/CMakeLists.txt
tests/src/Interop/ExecInDefAppDom/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/ExecInDefAppDom/ExecInDefAppDom.cs [new file with mode: 0644]
tests/src/Interop/ExecInDefAppDom/ExecInDefAppDom.csproj [new file with mode: 0644]
tests/src/Interop/ExecInDefAppDom/ExecInDefAppDomDll.cpp [new file with mode: 0644]

index 282cda7..affc629 100644 (file)
@@ -38,6 +38,7 @@ add_subdirectory(SizeConst)
 add_subdirectory(DllImportAttribute/ExeFile)
 add_subdirectory(DllImportAttribute/FileNameContainDot)
 add_subdirectory(DllImportAttribute/Simple)
+add_subdirectory(ExecInDefAppDom)
 
 if(WIN32)
     # This test doesn't necessarily need to be Windows-only, but the implementation is very tied to Windows APIs
diff --git a/tests/src/Interop/ExecInDefAppDom/CMakeLists.txt b/tests/src/Interop/ExecInDefAppDom/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e1c903c
--- /dev/null
@@ -0,0 +1,10 @@
+cmake_minimum_required (VERSION 2.6)
+project (ExecInDefAppDomDll)
+set(SOURCES ExecInDefAppDomDll.cpp )
+
+# add the executable
+add_library (ExecInDefAppDomDll SHARED ${SOURCES})
+target_link_libraries(ExecInDefAppDomDll ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS ExecInDefAppDomDll DESTINATION bin)
diff --git a/tests/src/Interop/ExecInDefAppDom/ExecInDefAppDom.cs b/tests/src/Interop/ExecInDefAppDom/ExecInDefAppDom.cs
new file mode 100644 (file)
index 0000000..fe094fb
--- /dev/null
@@ -0,0 +1,90 @@
+// 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;
+
+using Console = Internal.Console;
+
+public class FakeInjectedCode
+{
+    int NonStatic(String argument) { return 0;}
+    static bool WrongReturnType(String argument) { return false;}
+    static int Return0(String argument) { return 0;}
+    static int Return1(String argument) { return 1;}
+    static int ThrowAnything(String argument) { throw new Exception("Throwing something");}
+    static int ParseArgument(String argument) { return int.Parse(argument);}
+}
+
+public class Program
+{
+    public static class NativeMethods
+    {
+        [DllImport("ExecInDefAppDomDll")]
+        public static extern int
+        CallExecuteInDefaultAppDomain(
+            [In, MarshalAs( UnmanagedType.LPWStr )] String assemblyPath,
+            [In, MarshalAs( UnmanagedType.LPWStr )] String typeName,
+            [In, MarshalAs( UnmanagedType.LPWStr )] String methodName,
+            [In, MarshalAs( UnmanagedType.LPWStr )] String argument,
+            [In, Out, MarshalAs( UnmanagedType.I4 )] ref int result
+        );
+    }
+
+    static int TestExecuteInAppDomain(string assemblyPath, string typeName, string methodName, string argument, int expectedHResult, int expectedResult)
+    {
+        bool passed = true;
+        try
+        {
+            int result = 0;
+            int hresult = NativeMethods.CallExecuteInDefaultAppDomain(assemblyPath, typeName, methodName, argument, ref result);
+
+            if (hresult != expectedHResult)
+            {
+                Console.WriteLine($"Bad HRESULT: expected {expectedHResult:X} actual {hresult:X}");
+                passed = false;
+            }
+            else if (result != expectedResult)
+            {
+                Console.WriteLine($"Bad result: expected {expectedResult} actual {result}");
+                passed = false;
+            }
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Unexpected exception: {e}");
+            passed = false;
+        }
+        return passed ? 0 : 1;
+    }
+
+    public static int Main(string[] args)
+    {
+        int result = 100;
+        String myPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
+        String bogusPath = myPath + "random";
+
+        const int S_OK = unchecked((int)0);
+        const int COR_E_FILENOTFOUND = unchecked((int)0x80070002);
+        const int COR_E_TYPELOAD = unchecked((int)0x80131522);
+        const int COR_E_MISSINGMETHOD = unchecked((int)0x80131513);
+        const int COR_E_EXCEPTION = unchecked((int)0x80131500);
+        const int COR_E_FORMAT = unchecked((int)0x80131537);
+
+        result += TestExecuteInAppDomain(bogusPath, "BogusType", "Return0", "None", COR_E_FILENOTFOUND, 0);
+
+        result += TestExecuteInAppDomain(myPath, "BogusType", "Return0", "None", COR_E_TYPELOAD, 0);
+
+        result += TestExecuteInAppDomain(myPath, "FakeInjectedCode", "NonStatic", "None", COR_E_MISSINGMETHOD, 0);
+        result += TestExecuteInAppDomain(myPath, "FakeInjectedCode", "WrongReturnType", "None", COR_E_MISSINGMETHOD, 0);
+        result += TestExecuteInAppDomain(myPath, "FakeInjectedCode", "Return0", "None", S_OK, 0);
+        result += TestExecuteInAppDomain(myPath, "FakeInjectedCode", "Return1", "None", S_OK, 1);
+        result += TestExecuteInAppDomain(myPath, "FakeInjectedCode", "ThrowAnything", "None", COR_E_EXCEPTION, 0);
+        result += TestExecuteInAppDomain(myPath, "FakeInjectedCode", "ParseArgument", "0", S_OK, 0);
+        result += TestExecuteInAppDomain(myPath, "FakeInjectedCode", "ParseArgument", "200", S_OK, 200);
+        result += TestExecuteInAppDomain(myPath, "FakeInjectedCode", "ParseArgument", "None", COR_E_FORMAT, 0);
+
+        return result;
+    }
+
+}
diff --git a/tests/src/Interop/ExecInDefAppDom/ExecInDefAppDom.csproj b/tests/src/Interop/ExecInDefAppDom/ExecInDefAppDom.csproj
new file mode 100644 (file)
index 0000000..edbf53d
--- /dev/null
@@ -0,0 +1,36 @@
+<?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" />
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Interop.settings.targets))\Interop.settings.targets" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>ExecInDefAppDom</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{0A370152-9D24-475E-A171-AB58897132C3}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+    <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ExecInDefAppDom.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <!-- This is needed to make sure native binary gets installed in the right location -->
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
\ No newline at end of file
diff --git a/tests/src/Interop/ExecInDefAppDom/ExecInDefAppDomDll.cpp b/tests/src/Interop/ExecInDefAppDom/ExecInDefAppDomDll.cpp
new file mode 100644 (file)
index 0000000..cd83137
--- /dev/null
@@ -0,0 +1,62 @@
+// 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 <xplatform.h>
+#include "mscoree.h"
+
+typedef HRESULT  (STDAPICALLTYPE *FnGetCLRRuntimeHost)(REFIID riid, IUnknown **pUnk);
+
+// Returns the ICLRRuntimeHost instance or nullptr on failure.
+ICLRRuntimeHost* GetCLRRuntimeHost()
+{
+    HMODULE coreCLRModule = ::GetModuleHandle(L"coreclr.dll");
+    if (!coreCLRModule)
+    {
+        coreCLRModule = ::GetModuleHandle(L"coreclr.so");
+    }
+    if (!coreCLRModule)
+    {
+        coreCLRModule = ::GetModuleHandle(L"coreclr.dynlib");
+    }
+    if (!coreCLRModule)
+    {
+        return nullptr;
+    }
+
+    FnGetCLRRuntimeHost pfnGetCLRRuntimeHost = (FnGetCLRRuntimeHost)::GetProcAddress(coreCLRModule, "GetCLRRuntimeHost");
+    if (!pfnGetCLRRuntimeHost)
+    {
+        return nullptr;
+    }
+
+    ICLRRuntimeHost* clrRuntimeHost = nullptr;
+    HRESULT hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost, (IUnknown**)&clrRuntimeHost);
+    if (FAILED(hr)) {
+        return nullptr;
+    }
+
+    return clrRuntimeHost;
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE
+CallExecuteInDefaultAppDomain(LPCWSTR pwzAssemblyPath,
+                        LPCWSTR pwzTypeName,
+                        LPCWSTR pwzMethodName,
+                        LPCWSTR pwzArgument,
+                        DWORD   *pReturnValue)
+{
+    ICLRRuntimeHost* host = GetCLRRuntimeHost();
+
+    if (!host)
+        return E_FAIL;
+
+    if(host->Start())
+        return E_FAIL;
+
+    auto result = host->ExecuteInDefaultAppDomain(pwzAssemblyPath, pwzTypeName, pwzMethodName, pwzArgument, pReturnValue);
+
+    host->Stop();
+
+    return result;
+}