Basic implementation for testing of COM activation of a .NET class (#19760)
authorAaron Robinson <arobins@microsoft.com>
Tue, 11 Sep 2018 00:24:49 +0000 (17:24 -0700)
committerGitHub <noreply@github.com>
Tue, 11 Sep 2018 00:24:49 +0000 (17:24 -0700)
* Rough outline of managed implementation for COM activation in SPCL

* Add property for finding interop common
Add property to exclude default assertion file
Display exe ExeLaunchProgram class is going to launch

* Add a native client for the NETServer
Consume the ExeLauncherProgram.cs file as a wrapper for the native test

* Update COM Server contracts to use 'int' instead of 'long'

* Complete symmetric testing coverage for .NET server and native client.

* Block EXE launch from running on non-Windows machines

* Disable COM testing in helix since it has issues on Windows Nano and there
is no way to determine that is the platform.

* Update tests based on CLSID mapping manifest approach.

40 files changed:
build-test.cmd
src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComActivator.cs [new file with mode: 0644]
src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
src/coreclr/hosts/CMakeLists.txt
src/coreclr/hosts/coreshim/CMakeLists.txt [new file with mode: 0644]
src/coreclr/hosts/coreshim/ComActivation.cpp [new file with mode: 0644]
src/coreclr/hosts/coreshim/CoreShim.cpp [new file with mode: 0644]
src/coreclr/hosts/coreshim/CoreShim.h [new file with mode: 0644]
src/coreclr/hosts/coreshim/Exports.def [new file with mode: 0644]
tests/issues.targets
tests/src/CLRTest.Execute.Batch.targets
tests/src/Interop/CMakeLists.txt
tests/src/Interop/COM/Activator/Activator.csproj [new file with mode: 0644]
tests/src/Interop/COM/Activator/Program.cs [new file with mode: 0644]
tests/src/Interop/COM/NETServer/ArrayTesting.cs [new file with mode: 0644]
tests/src/Interop/COM/NETServer/ErrorMarshalTesting.cs [new file with mode: 0644]
tests/src/Interop/COM/NETServer/NETServer.csproj
tests/src/Interop/COM/NETServer/NumericTesting.cs [new file with mode: 0644]
tests/src/Interop/COM/NETServer/StringTesting.cs [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives.csproj [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/App.manifest [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/ArrayTests.cpp [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/Client.cpp [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/ClientTests.h [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/CoreShim.X.manifest [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/ErrorTests.cpp [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/NumericTests.cpp [new file with mode: 0644]
tests/src/Interop/COM/NativeClients/Primitives/StringTests.cpp [new file with mode: 0644]
tests/src/Interop/COM/NativeServer/ArrayTesting.h
tests/src/Interop/COM/NativeServer/ErrorMarshalTesting.h
tests/src/Interop/COM/NativeServer/NumericTesting.h
tests/src/Interop/COM/NativeServer/Servers.h
tests/src/Interop/COM/ServerContracts/Primitives.cs
tests/src/Interop/COM/ServerContracts/Server.Contracts.tlh
tests/src/Interop/COM/ServerContracts/Server.Contracts.tli
tests/src/Interop/Interop.settings.targets
tests/src/Interop/common/ExeLauncherProgram.cs [new file with mode: 0644]
tests/src/Interop/common/xplatform.h

index 361a3f8..c0f45b1 100644 (file)
@@ -324,7 +324,8 @@ for /l %%G in (1, 1, %__BuildLoopCount%) do (
     set __MsbuildErr=/flp2:ErrorsOnly;LogFile="%__BuildErr%";Append=!__AppendToLog!
 
     set TestBuildSlice=%%G
-    call %__DotnetHost% msbuild %__ProjectDir%\tests\build.proj !__MsbuildLog! !__MsbuildWrn! !__MsbuildErr! %__msbuildArgs% %__BuildAgainstPackagesMsbuildArg% !__PriorityMsbuildArg! %__UnprocessedBuildArgs%
+    echo Running: msbuild %__ProjectDir%\tests\build.proj !__MsbuildLog! !__MsbuildWrn! !__MsbuildErr! %__msbuildArgs% %__BuildAgainstPackagesMsbuildArg% !__PriorityMsbuildArg! %__UnprocessedBuildArgs%
+    call msbuild %__ProjectDir%\tests\build.proj !__MsbuildLog! !__MsbuildWrn! !__MsbuildErr! %__msbuildArgs% %__BuildAgainstPackagesMsbuildArg% !__PriorityMsbuildArg! %__UnprocessedBuildArgs%
 
     if errorlevel 1 (
         echo %__MsgPrefix%Error: build failed. Refer to the build log files for details:
@@ -342,7 +343,8 @@ for /l %%G in (1, 1, %__BuildLoopCount%) do (
 REM Check that we've built about as many tests as we expect. This is primarily intended to prevent accidental changes that cause us to build
 REM drastically fewer Pri-1 tests than expected.
 echo %__MsgPrefix%Check the managed tests build
-call %__DotnetHost% msbuild %__ProjectDir%\tests\runtest.proj /t:CheckTestBuild /p:CLRTestPriorityToBuild=%__Priority% %__msbuildArgs% %__unprocessedBuildArgs%
+echo Running: msbuild %__ProjectDir%\tests\runtest.proj /t:CheckTestBuild /p:CLRTestPriorityToBuild=%__Priority% %__msbuildArgs% %__unprocessedBuildArgs%
+call msbuild %__ProjectDir%\tests\runtest.proj /t:CheckTestBuild /p:CLRTestPriorityToBuild=%__Priority% %__msbuildArgs% %__unprocessedBuildArgs%
 if errorlevel 1 (
     echo %__MsgPrefix%Error: build failed.
     exit /b 1
index 30b5b2b..74f04d8 100644 (file)
     <Compile Condition="'$(FeatureCominterop)' != 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\NonPortable.cs" />
     <Compile Condition="'$(FeatureCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\DispatchWrapper.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ICustomFactory.cs" />
+    <Compile Condition="'$(FeatureCominteropUnmanagedActivation)' == 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComActivator.cs" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Expando\IExpando.cs" />
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComActivator.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComActivator.cs
new file mode 100644 (file)
index 0000000..785e9ac
--- /dev/null
@@ -0,0 +1,261 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Reflection;
+
+namespace System.Runtime.InteropServices
+{
+    [ComImport]
+    [ComVisible(false)]
+    [Guid("00000001-0000-0000-C000-000000000046")]
+    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+    internal interface IClassFactory
+    {
+        void CreateInstance(
+            [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+            ref Guid riid,
+            [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
+
+        void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct LICINFO
+    {
+        public int cbLicInfo;
+
+        [MarshalAs(UnmanagedType.Bool)]
+        public bool fRuntimeKeyAvail;
+
+        [MarshalAs(UnmanagedType.Bool)]
+        public bool fLicVerified;
+    }
+
+    [ComImport]
+    [ComVisible(false)]
+    [Guid("B196B28F-BAB4-101A-B69C-00AA00341D07")]
+    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+    internal interface IClassFactory2 : IClassFactory
+    {
+        new void CreateInstance(
+            [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+            ref Guid riid,
+            [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
+
+        new void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
+
+        void GetLicInfo(ref LICINFO pLicInfo);
+
+        void RequestLicKey(
+            int dwReserved,
+            [MarshalAs(UnmanagedType.BStr)] out string pBstrKey);
+
+        void CreateInstanceLic(
+            [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+            [MarshalAs(UnmanagedType.Interface)] object pUnkReserved,
+            ref Guid riid,
+            [MarshalAs(UnmanagedType.BStr)] string bstrKey,
+            [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct ComActivationContext
+    {
+        public Guid ClassId;
+        public Guid InterfaceId;
+        public string AssemblyName;
+        public string TypeName;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct ComActivationContextInternal
+    {
+        public Guid ClassId;
+        public Guid InterfaceId;
+        public IntPtr AssemblyNameBuffer;
+        public IntPtr TypeNameBuffer;
+        public IntPtr ClassFactoryDest;
+    }
+
+    public static class ComActivator
+    {
+        /// <summary>
+        /// Entry point for unmanaged COM activation API from managed code
+        /// </summary>
+        /// <param name="cxt">Reference to a <see cref="ComActivationContext"/> instance</param>
+        public static object GetClassFactoryForType(ComActivationContext cxt)
+        {
+            if (cxt.InterfaceId != typeof(IClassFactory).GUID
+                && cxt.InterfaceId != typeof(IClassFactory2).GUID)
+            {
+                throw new NotSupportedException();
+            }
+
+            Type classType = FindClassType(cxt.ClassId, cxt.AssemblyName, cxt.TypeName);
+            return new BasicClassFactory(cxt.ClassId, classType);
+        }
+
+        /// <summary>
+        /// Internal entry point for unmanaged COM activation API from native code
+        /// </summary>
+        /// <param name="cxtInt">Reference to a <see cref="ComActivationContextInternal"/> instance</param>
+        public static int GetClassFactoryForTypeInternal(ref ComActivationContextInternal cxtInt)
+        {
+            if (IsLoggingEnabled())
+            {
+                Log(
+$@"{nameof(GetClassFactoryForTypeInternal)} arguments:
+    {cxtInt.ClassId}
+    {cxtInt.InterfaceId}
+    0x{cxtInt.AssemblyNameBuffer.ToInt64():x}
+    0x{cxtInt.TypeNameBuffer.ToInt64():x}
+    0x{cxtInt.ClassFactoryDest.ToInt64():x}");
+            }
+
+            try
+            {
+                var cxt = new ComActivationContext()
+                {
+                    ClassId = cxtInt.ClassId,
+                    InterfaceId = cxtInt.InterfaceId,
+                    AssemblyName = Marshal.PtrToStringUTF8(cxtInt.AssemblyNameBuffer),
+                    TypeName = Marshal.PtrToStringUTF8(cxtInt.TypeNameBuffer)
+                };
+
+                object cf = GetClassFactoryForType(cxt);
+                IntPtr nativeIUnknown = Marshal.GetIUnknownForObject(cf);
+                Marshal.WriteIntPtr(cxtInt.ClassFactoryDest, nativeIUnknown);
+            }
+            catch (Exception e)
+            {
+                return e.HResult;
+            }
+
+            return 0;
+        }
+
+        private static bool IsLoggingEnabled()
+        {
+#if COM_ACTIVATOR_DEBUG
+            return true;
+#else
+            return false;
+#endif
+        }
+
+        private static void Log(string fmt, params object[] args)
+        {
+            // [TODO] Use FrameworkEventSource in release builds
+
+            Debug.WriteLine(fmt, args);
+         }
+
+        private static Type FindClassType(Guid clsid, string assemblyName, string typeName)
+        {
+            try
+            {
+                Assembly assem = Assembly.LoadFrom(assemblyName);
+                Type t = assem.GetType(typeName);
+                if (t != null)
+                {
+                    return t;
+                }
+            }
+            catch (Exception e)
+            {
+                if (IsLoggingEnabled())
+                {
+                    Log($"COM Activation of {clsid} failed. {e}");
+                }
+            }
+
+            const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)0x80040111);
+            throw new COMException(string.Empty, CLASS_E_CLASSNOTAVAILABLE);
+        }
+
+        [ComVisible(true)]
+        internal class BasicClassFactory : IClassFactory2
+        {
+            private readonly Guid classId;
+            private readonly Type classType;
+
+            public BasicClassFactory(Guid clsid, Type classType)
+            {
+                this.classId = clsid;
+                this.classType = classType;
+            }
+
+            public void CreateInstance(
+                [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+                ref Guid riid,
+                [MarshalAs(UnmanagedType.Interface)] out object ppvObject)
+            {
+                if (riid != Marshal.IID_IUnknown)
+                {
+                    bool found = false;
+
+                    // Verify the class implements the desired interface
+                    foreach (Type i in this.classType.GetInterfaces())
+                    {
+                        if (i.GUID == riid)
+                        {
+                            found = true;
+                            break;
+                        }
+                    }
+
+                    if (!found)
+                    {
+                        // E_NOINTERFACE
+                        throw new InvalidCastException();
+                    }
+                }
+
+                ppvObject = Activator.CreateInstance(this.classType);
+                if (pUnkOuter != null)
+                {
+                    try
+                    {
+                        IntPtr outerPtr = Marshal.GetIUnknownForObject(pUnkOuter);
+                        IntPtr innerPtr = Marshal.CreateAggregatedObject(outerPtr, ppvObject);
+                        ppvObject = Marshal.GetObjectForIUnknown(innerPtr);
+                    }
+                    finally
+                    {
+                        // Decrement the above 'Marshal.GetIUnknownForObject()'
+                        Marshal.ReleaseComObject(pUnkOuter);
+                    }
+                }
+            }
+
+            public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock)
+            {
+                // nop
+            }
+
+            public void GetLicInfo(ref LICINFO pLicInfo)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void RequestLicKey(int dwReserved, [MarshalAs(UnmanagedType.BStr)] out string pBstrKey)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void CreateInstanceLic(
+                [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+                [MarshalAs(UnmanagedType.Interface)] object pUnkReserved,
+                ref Guid riid,
+                [MarshalAs(UnmanagedType.BStr)] string bstrKey,
+                [MarshalAs(UnmanagedType.Interface)] out object ppvObject)
+            {
+                throw new NotImplementedException();
+            }
+        }
+    }
+}
index e214f1e..c4c65da 100644 (file)
@@ -28,14 +28,15 @@ namespace System.Runtime.InteropServices
     /// </summary>
     public static partial class Marshal
     {
+#if FEATURE_COMINTEROP
+        internal static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
+#endif //FEATURE_COMINTEROP
+
         private const int LMEM_FIXED = 0;
         private const int LMEM_MOVEABLE = 2;
 #if !FEATURE_PAL
         private const long HiWordMask = unchecked((long)0xffffffffffff0000L);
 #endif //!FEATURE_PAL
-#if FEATURE_COMINTEROP
-        private static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
-#endif //FEATURE_COMINTEROP
 
         // Win32 has the concept of Atoms, where a pointer can either be a pointer
         // or an int.  If it's less than 64K, this is guaranteed to NOT be a 
index bb425b9..c27ba16 100644 (file)
@@ -3,6 +3,7 @@ include_directories(inc)
 if(WIN32)
   add_subdirectory(corerun)
   add_subdirectory(coreconsole)
+  add_subdirectory(coreshim)
 else(WIN32)
   add_subdirectory(unixcoreruncommon)
   add_subdirectory(unixcorerun)
diff --git a/src/coreclr/hosts/coreshim/CMakeLists.txt b/src/coreclr/hosts/coreshim/CMakeLists.txt
new file mode 100644 (file)
index 0000000..828b91c
--- /dev/null
@@ -0,0 +1,25 @@
+project (CoreShim)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CoreShim_SOURCES
+    CoreShim.cpp
+    ComActivation.cpp
+    Exports.def)
+
+add_library_clr(CoreShim
+    SHARED
+    ${CoreShim_SOURCES}
+)
+
+target_link_libraries(CoreShim
+    utilcodestaticnohost
+    advapi32.lib
+    oleaut32.lib
+    uuid.lib
+    user32.lib
+    ${STATIC_MT_CRT_LIB}
+    ${STATIC_MT_VCRT_LIB}
+)
+
+install_clr(CoreShim)
\ No newline at end of file
diff --git a/src/coreclr/hosts/coreshim/ComActivation.cpp b/src/coreclr/hosts/coreshim/ComActivation.cpp
new file mode 100644 (file)
index 0000000..5df1d00
--- /dev/null
@@ -0,0 +1,91 @@
+// 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 "CoreShim.h"
+
+#include <vector>
+
+namespace
+{
+    HRESULT InitializeCoreClr(_In_ coreclr* inst)
+    {
+        assert(inst != nullptr);
+
+        HRESULT hr;
+
+        std::string tpaList;
+        RETURN_IF_FAILED(coreclr::CreateTpaList(tpaList));
+
+        const char *keys[] =
+        {
+            "APP_PATHS",
+            "TRUSTED_PLATFORM_ASSEMBLIES",
+        };
+
+        // [TODO] Support UNICODE app path
+        char wd[MAX_PATH];
+        (void)::GetCurrentDirectoryA(ARRAYSIZE(wd), wd);
+
+        const char *values[] =
+        {
+            wd,
+            tpaList.c_str(),
+        };
+
+        static_assert(ARRAYSIZE(keys) == ARRAYSIZE(values), "key/values pairs should match in length");
+
+        return inst->Initialize(ARRAYSIZE(keys), keys, values, "COMAct");
+    }
+}
+
+STDAPI DllGetClassObject(
+    _In_ REFCLSID rclsid,
+    _In_ REFIID riid,
+    _Outptr_ LPVOID FAR* ppv)
+{
+    HRESULT hr;
+
+    coreclr *inst;
+    RETURN_IF_FAILED(coreclr::GetCoreClrInstance(&inst));
+
+    if (hr == S_OK)
+        RETURN_IF_FAILED(InitializeCoreClr(inst));
+
+    using GetClassFactoryForTypeInternal_ptr = HRESULT(*)(void *);
+    GetClassFactoryForTypeInternal_ptr GetClassFactoryForTypeInternal;
+    RETURN_IF_FAILED(inst->CreateDelegate(
+        "System.Private.CoreLib",
+        "System.Runtime.InteropServices.ComActivator",
+        "GetClassFactoryForTypeInternal", (void**)&GetClassFactoryForTypeInternal));
+
+    // Get assembly and type for activation
+    std::string assemblyName;
+    RETURN_IF_FAILED(Utility::TryGetEnvVar(COMACT_ASSEMBLYNAME_ENVVAR, assemblyName));
+
+    std::string typeName;
+    RETURN_IF_FAILED(Utility::TryGetEnvVar(COMACT_TYPENAME_ENVVAR, typeName));
+
+    IUnknown *ccw = nullptr;
+
+    struct ComActivationContext
+    {
+        GUID ClassId;
+        GUID InterfaceId;
+        const void *AssemblyName;
+        const void *TypeName;
+        void **ClassFactoryDest;
+    } comCxt{ rclsid, riid, assemblyName.data(), typeName.data(), (void**)&ccw };
+
+    RETURN_IF_FAILED(GetClassFactoryForTypeInternal(&comCxt));
+    assert(ccw != nullptr);
+
+    hr = ccw->QueryInterface(riid, ppv);
+    ccw->Release();
+    return hr;
+}
+
+STDAPI DllCanUnloadNow(void)
+{
+    return S_FALSE;
+}
diff --git a/src/coreclr/hosts/coreshim/CoreShim.cpp b/src/coreclr/hosts/coreshim/CoreShim.cpp
new file mode 100644 (file)
index 0000000..497c10e
--- /dev/null
@@ -0,0 +1,293 @@
+// 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 "CoreShim.h"
+
+#include <set>
+#include <sstream>
+#include <vector>
+#include <mutex>
+
+namespace
+{
+    struct PathBuffer
+    {
+        PathBuffer()
+            : DefBuffer{}
+            , Buf{ DefBuffer }
+            , Len{ ARRAYSIZE(DefBuffer) }
+        { }
+
+        void SetLength(_In_ DWORD len)
+        {
+            if (len > Len)
+            {
+                Buf = BigBuffer.data();
+                Len = static_cast<DWORD>(BigBuffer.size());
+            }
+        }
+
+        void ExpandBuffer(_In_ DWORD factor = 2)
+        {
+            SetLength(Len * factor);
+        }
+
+        operator DWORD()
+        {
+            return Len;
+        }
+
+        operator WCHAR *()
+        {
+            return Buf;
+        }
+
+        WCHAR DefBuffer[MAX_PATH];
+        std::vector<WCHAR> BigBuffer;
+
+        WCHAR *Buf;
+        DWORD Len;
+    };
+
+    std::string GetExePath()
+    {
+        PathBuffer buffer;
+        DWORD len = ::GetModuleFileNameW(nullptr, buffer, buffer);
+        while (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+        {
+            buffer.ExpandBuffer();
+            len = ::GetModuleFileNameW(nullptr, buffer, buffer);
+        }
+
+        return std::string{ buffer.Buf, buffer.Buf + len };
+    }
+
+    std::wstring GetEnvVar(_In_z_ const WCHAR *env)
+    {
+        DWORD len = ::GetEnvironmentVariableW(env, nullptr, 0);
+        if (len == 0)
+            throw __HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND);
+
+        PathBuffer buffer;
+        buffer.SetLength(len);
+        (void)::GetEnvironmentVariableW(env, buffer, buffer);
+
+        return static_cast<WCHAR *>(buffer.Buf);
+    }
+
+    coreclr *s_CoreClrInstance;
+}
+
+namespace Utility
+{
+    HRESULT TryGetEnvVar(_In_z_ const WCHAR *env, _Inout_ std::string &envVar)
+    {
+        try
+        {
+            std::wstring envVarLocal = GetEnvVar(env);
+            envVar = { std::begin(envVarLocal), std::end(envVarLocal) };
+        }
+        catch (HRESULT hr)
+        {
+            return hr;
+        }
+
+        return S_OK;
+    }
+}
+
+HRESULT coreclr::GetCoreClrInstance(_Outptr_ coreclr **instance, _In_opt_z_ const WCHAR *path)
+{
+    if (s_CoreClrInstance != nullptr)
+    {
+        *instance = s_CoreClrInstance;
+        return S_FALSE;
+    }
+
+    try
+    {
+        std::wstring pathLocal;
+        if (path == nullptr)
+        {
+            pathLocal = GetEnvVar(W("CORE_ROOT"));
+        }
+        else
+        {
+            pathLocal = { path };
+        }
+
+        pathLocal.append(W("\\coreclr.dll"));
+
+        AutoModule hmod = ::LoadLibraryExW(pathLocal.c_str() , nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+        if (hmod == nullptr)
+            return HRESULT_FROM_WIN32(::GetLastError());
+
+        s_CoreClrInstance = new coreclr{ std::move(hmod) };
+    }
+    catch (HRESULT hr)
+    {
+        return hr;
+    }
+    catch (const std::bad_alloc&)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    *instance = s_CoreClrInstance;
+    return S_OK;
+}
+
+HRESULT coreclr::CreateTpaList(_Inout_ std::string &tpaList, _In_opt_z_ const WCHAR *dir)
+{
+    assert(tpaList.empty());
+
+    // Represents priority order
+    static const WCHAR * const tpaExtensions[] =
+    {
+        W(".ni.dll"),
+        W(".dll"),
+        W(".ni.exe"),
+        W(".exe"),
+    };
+
+    try
+    {
+        std::wstring w_dirLocal;
+        if (dir == nullptr)
+        {
+            w_dirLocal = GetEnvVar(W("CORE_ROOT"));
+        }
+        else
+        {
+            w_dirLocal = { dir };
+        }
+
+        std::string dirLocal{ std::begin(w_dirLocal), std::end(w_dirLocal) };
+        w_dirLocal.append(W("\\*"));
+
+        std::set<std::wstring> addedAssemblies;
+        std::stringstream tpaStream;
+
+        // Walk the directory for each extension separately so assembly types
+        // are discovered in priority order - see above.
+        for (int extIndex = 0; extIndex < ARRAYSIZE(tpaExtensions); extIndex++)
+        {
+            const WCHAR* ext = tpaExtensions[extIndex];
+            size_t extLength = ::wcslen(ext);
+
+            WIN32_FIND_DATAW ffd;
+            AutoFindFile sh = ::FindFirstFileW(w_dirLocal.c_str(), &ffd);
+            if (sh == nullptr)
+                break;
+
+            // For all entries in the directory
+            do
+            {
+                // Only examine non-directory entries
+                if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+                {
+                    std::wstring filename{ ffd.cFileName };
+
+                    // Check if the extension matches
+                    int extPos = static_cast<int>(filename.length() - extLength);
+                    if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
+                    {
+                        continue;
+                    }
+
+                    std::wstring filenameWithoutExt{ filename.substr(0, extPos) };
+
+                    // Only one type of a particular assembly instance should be inserted
+                    // See extension list above.
+                    if (addedAssemblies.find(filenameWithoutExt) == std::end(addedAssemblies))
+                    {
+                        addedAssemblies.insert(std::move(filenameWithoutExt));
+
+                        // [TODO] Properly convert to UTF-8
+                        std::string filename_utf8{ std::begin(filename), std::end(filename) };
+                        tpaStream << dirLocal << "\\" << filename_utf8 << ";";
+                    }
+                }
+            } while (::FindNextFileW(sh, &ffd) != FALSE);
+        }
+
+        tpaList = tpaStream.str();
+    }
+    catch (HRESULT hr)
+    {
+        return hr;
+    }
+    catch (const std::bad_alloc&)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    return S_OK;
+}
+
+coreclr::coreclr(_Inout_ AutoModule hmod)
+    : _hmod{ std::move(hmod) }
+    , _clrInst{ nullptr }
+    , _appDomainId{ std::numeric_limits<uint32_t>::max() }
+{
+    _initialize = (decltype(_initialize))::GetProcAddress(_hmod, "coreclr_initialize");
+    assert(_initialize != nullptr);
+
+    _create_delegate = (decltype(_create_delegate))::GetProcAddress(_hmod, "coreclr_create_delegate");
+    assert(_create_delegate != nullptr);
+
+    _shutdown = (decltype(_shutdown))::GetProcAddress(_hmod, "coreclr_shutdown");
+    assert(_shutdown != nullptr);
+}
+
+coreclr::~coreclr()
+{
+    if (_clrInst != nullptr)
+    {
+        HRESULT hr = _shutdown(_clrInst, _appDomainId);
+        assert(SUCCEEDED(hr));
+        (void)hr;
+    }
+}
+
+HRESULT coreclr::Initialize(
+    _In_ int propertyCount,
+    _In_reads_(propertCount) const char **keys,
+    _In_reads_(propertCount) const char **values,
+    _In_opt_z_ const char *appDomainName)
+{
+    if (_clrInst != nullptr)
+        return __HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+
+    if (appDomainName == nullptr)
+        appDomainName = "CoreShim";
+
+    HRESULT hr;
+    try
+    {
+        const std::string exePath = GetExePath();
+        RETURN_IF_FAILED(_initialize(exePath.c_str(), appDomainName, propertyCount, keys, values, &_clrInst, &_appDomainId));
+    }
+    catch (const std::bad_alloc&)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    return S_OK;
+}
+
+HRESULT coreclr::CreateDelegate(
+    _In_z_ const char *assembly,
+    _In_z_ const char *type,
+    _In_z_ const char *method,
+    _Out_ void **del)
+{
+    if (_clrInst == nullptr)
+        return E_NOT_VALID_STATE;
+
+    HRESULT hr;
+    RETURN_IF_FAILED(_create_delegate(_clrInst, _appDomainId, assembly, type, method, del));
+
+    return S_OK;
+}
diff --git a/src/coreclr/hosts/coreshim/CoreShim.h b/src/coreclr/hosts/coreshim/CoreShim.h
new file mode 100644 (file)
index 0000000..dd5e9d1
--- /dev/null
@@ -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.
+
+#ifndef _CORESHIM_H_
+#define _CORESHIM_H_
+
+// Platform
+#define NOMINMAX
+#include <Windows.h>
+#include <combaseapi.h>
+
+// Standard library
+#include <utility>
+#include <string>
+#include <cstdint>
+#include <cassert>
+
+// CoreCLR
+#include <palclr.h>
+#include <coreclrhost.h>
+
+#define WCHAR wchar_t
+
+#define RETURN_IF_FAILED(exp) { hr = (exp); if (FAILED(hr)) { assert(false && #exp); return hr; } }
+
+template
+<
+    typename T,
+    T DEFAULT,
+    void(*RELEASE)(T)
+>
+struct AutoClass
+{
+    T c;
+
+    AutoClass() : c{ DEFAULT }
+    { }
+
+    AutoClass(_Inout_ T t) : c{ t }
+    { }
+
+    AutoClass(_In_ const AutoClass&) = delete;
+    AutoClass& operator=(_In_ const AutoClass&) = delete;
+
+    AutoClass(_Inout_ AutoClass &&other)
+        : c{ other.Detach() }
+    { }
+
+    AutoClass& operator=(_Inout_ AutoClass &&other)
+    {
+        Attach(other.Detach());
+    }
+
+    ~AutoClass()
+    {
+        Attach(DEFAULT);
+    }
+
+    operator T()
+    {
+        return c;
+    }
+
+    T* operator &()
+    {
+        return &c;
+    }
+
+    void Attach(_In_opt_ T cm)
+    {
+        RELEASE(c);
+        c = cm;
+    }
+
+    T Detach()
+    {
+        T tmp = c;
+        c = DEFAULT;
+        return tmp;
+    }
+};
+
+inline void ReleaseHandle(_In_ HANDLE h)
+{
+    if (h != nullptr && h != INVALID_HANDLE_VALUE)
+        ::CloseHandle(h);
+}
+
+using AutoHandle = AutoClass<HANDLE, nullptr, &ReleaseHandle>;
+
+inline void ReleaseFindFile(_In_ HANDLE h)
+{
+    if (h != nullptr)
+        ::FindClose(h);
+}
+
+using AutoFindFile = AutoClass<HANDLE, nullptr, &ReleaseFindFile>;
+
+inline void ReleaseModule(_In_ HMODULE m)
+{
+    if (m != nullptr)
+        ::FreeLibrary(m);
+}
+
+using AutoModule = AutoClass<HMODULE, nullptr, &ReleaseModule>;
+
+namespace Utility
+{
+    /// <summary>
+    /// Get the supplied environment variable.
+    /// </summary>
+    HRESULT TryGetEnvVar(_In_z_ const WCHAR *env, _Inout_ std::string &envVar);
+}
+
+// CoreShim environment variables used to indicate what assembly/type tuple
+// to load during COM activation.
+#define COMACT_ASSEMBLYNAME_ENVVAR W("CORESHIM_COMACT_ASSEMBLYNAME")
+#define COMACT_TYPENAME_ENVVAR W("CORESHIM_COMACT_TYPENAME")
+
+// CoreCLR class to handle lifetime and provide a simpler API surface
+class coreclr
+{
+public: // static
+    /// <summary>
+    /// Get a CoreCLR instance
+    /// </summary>
+    /// <returns>S_OK if newly created and needs initialization, S_FALSE if already exists and no initialization needed, otherwise an error code</returns>
+    /// <remarks>
+    /// If a CoreCLR instance has already been created, the existing instance is returned.
+    /// If the <paramref name="path"/> is not supplied, the 'CORE_ROOT' environment variable is used.
+    /// </remarks>
+    static HRESULT GetCoreClrInstance(_Outptr_ coreclr **instance, _In_opt_z_ const WCHAR *path = nullptr);
+
+    /// <summary>
+    /// Populate the supplied string with a delimited string of TPA assemblies in from the supplied directory path.
+    /// </summary>
+    /// <remarks>
+    /// If <paramref name="dir"/> is not supplied, the 'CORE_ROOT' environment variable is used.
+    /// </remarks>
+    static HRESULT CreateTpaList(_Inout_ std::string &tpaList, _In_opt_z_ const WCHAR *dir = nullptr);
+
+public:
+    coreclr(_Inout_ AutoModule hmod);
+
+    coreclr(_In_ const coreclr &) = delete;
+    coreclr& operator=(_In_ const coreclr &) = delete;
+
+    coreclr(_Inout_ coreclr &&) = delete;
+    coreclr& operator=(_Inout_ coreclr &&) = delete;
+
+    ~coreclr();
+
+    // See exported function 'coreclr_initialize' from coreclr library
+    HRESULT Initialize(
+        _In_ int propertyCount,
+        _In_reads_(propertyCount) const char **keys,
+        _In_reads_(propertyCount) const char **values,
+        _In_opt_z_ const char *appDomainName = nullptr);
+
+    // See exported function 'coreclr_create_delegate' from coreclr library
+    HRESULT CreateDelegate(
+        _In_z_ const char *assembly,
+        _In_z_ const char *type,
+        _In_z_ const char *method,
+        _Out_ void **del);
+
+private:
+    AutoModule _hmod;
+
+    void *_clrInst;
+    uint32_t _appDomainId;
+
+    coreclr_initialize_ptr _initialize;
+    coreclr_create_delegate_ptr _create_delegate;
+    coreclr_shutdown_ptr _shutdown;
+};
+
+#endif /* _CORESHIM_H_ */
diff --git a/src/coreclr/hosts/coreshim/Exports.def b/src/coreclr/hosts/coreshim/Exports.def
new file mode 100644 (file)
index 0000000..fbdded0
--- /dev/null
@@ -0,0 +1,3 @@
+EXPORTS
+    DllGetClassObject   PRIVATE
+    DllCanUnloadNow     PRIVATE
\ No newline at end of file
index 2a34642..68f7472 100644 (file)
         <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/GitHub_11408/GitHub_11408/*">
             <Issue>11408</Issue>
         </ExcludeList>
-        <ExcludeList Include="$(XunitTestBinBase)\Interop\COM\NETClients\Primitives\NETClientPrimitives\NETClientPrimitives.cmd">
+        <!-- Disable COM tests since they don't properly run on Windows.Nano and at present there is no way to special case that OS flavor. -->
+        <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NETClients/Primitives/NETClientPrimitives/NETClientPrimitives.cmd">
+            <Issue>19164</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NativeClients/Primitives/Primitives.cmd">
             <Issue>19164</Issue>
         </ExcludeList>
     </ItemGroup>
         <ExcludeList Include="$(XunitTestBinBase)/baseservices/varargs/varargsupport_r/*">
             <Issue>Varargs supported on this platform</Issue>
         </ExcludeList>
-
-        <!-- Disable COM tests since they don't properly run on Windows.Nano and at present there is no way to special case that OS flavor. -->
-        <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NETClients/Primitives/NETClientPrimitives/*">
-            <Issue>Fails on Windows.Nano</Issue>
-        </ExcludeList>
     </ItemGroup>
 
     <!-- The following are tests that fail on non-Windows, which we must not run when building against packages -->
         <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NETClients/Primitives/NETClientPrimitives/NETClientPrimitives.*">
             <Issue>by design Windows only</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NativeClients/Primitives/Primitives.*">
+            <Issue>by design Windows only</Issue>
+        </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/JIT/Directed/IL/PInvokeTail/PInvokeTail/*">
             <Issue>needs triage</Issue>
         </ExcludeList>
index 4407010..1b1d36e 100644 (file)
@@ -253,6 +253,11 @@ if defined DoLink (
       </PropertyGroup>
       <PropertyGroup>      
       <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"%CORE_ROOT%\corerun.exe"</_CLRTestRunFile>
+      <BatchCopyCoreShimLocalCmds Condition="'$(CLRTestScriptLocalCoreShim)' == 'true'"><![CDATA[
+REM Local CoreShim requested - see MSBuild property 'CLRTestScriptLocalCoreShim'
+ECHO Copying '%CORE_ROOT%\CoreShim.dll'...
+COPY /y %CORE_ROOT%\CoreShim.dll .
+      ]]></BatchCopyCoreShimLocalCmds>
       <BatchCLRTestLaunchCmds><![CDATA[
 IF NOT "%CLRCustomTestLauncher%"=="" (
   set LAUNCHER=call %CLRCustomTestLauncher% %~dp0
@@ -260,6 +265,7 @@ IF NOT "%CLRCustomTestLauncher%"=="" (
   set LAUNCHER=%_DebuggerFullPath% $(_CLRTestRunFile)
 )
 $(BatchIlrtTestLaunchCmds)
+$(BatchCopyCoreShimLocalCmds)
       ]]></BatchCLRTestLaunchCmds>
       <BatchCLRTestLaunchCmds Condition="'$(CLRTestKind)' == 'BuildAndRun'">
     <![CDATA[
index 0107958..76384b4 100644 (file)
@@ -33,6 +33,7 @@ add_subdirectory(DllImportAttribute/Simple)
 
 if(WIN32)
     add_subdirectory(COM/NativeServer)
+    add_subdirectory(COM/NativeClients/Primitives)
     add_subdirectory(IJW/FakeMscoree)
 
     # IJW isn't supported on ARM64
diff --git a/tests/src/Interop/COM/Activator/Activator.csproj b/tests/src/Interop/COM/Activator/Activator.csproj
new file mode 100644 (file)
index 0000000..51b89eb
--- /dev/null
@@ -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" />
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Interop.settings.targets))\Interop.settings.targets" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>Activator</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <OutputType>Exe</OutputType>
+    <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
+
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\NetServer\NetServer.csproj" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/COM/Activator/Program.cs b/tests/src/Interop/COM/Activator/Program.cs
new file mode 100644 (file)
index 0000000..117d55f
--- /dev/null
@@ -0,0 +1,67 @@
+// 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.
+
+namespace Activator
+{
+    using System;
+    using System.Runtime.InteropServices;
+
+    using CoreFXTestLibrary;
+
+    using Console = Internal.Console;
+
+    class Program
+    {
+        static void InvalidInterfaceRequest()
+        {
+            Assert.Throws<NotSupportedException>(
+                () =>
+                {
+                    var notIClassFactory = new Guid("ED53F949-63E4-43B5-A13D-5655478AADD5");
+                    var cxt = new ComActivationContext()
+                    {
+                        InterfaceId = notIClassFactory
+                    };
+                    ComActivator.GetClassFactoryForType(cxt);
+                },
+                "Non-IClassFactory request should fail");
+        }
+
+        static void ClassNotRegistered()
+        {
+            COMException e = Assert.Throws<COMException>(
+                () =>
+                {
+                    var CLSID_NotRegistered = new Guid("328FF83E-3F6C-4BE9-A742-752562032925"); // Random GUID
+                    var IID_IClassFactory = new Guid("00000001-0000-0000-C000-000000000046");
+                    var cxt = new ComActivationContext()
+                    {
+                        ClassId = CLSID_NotRegistered,
+                        InterfaceId = IID_IClassFactory
+                    };
+                    ComActivator.GetClassFactoryForType(cxt);
+                },
+                "Class should not be found");
+
+            const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)0x80040111);
+            Assert.AreEqual(CLASS_E_CLASSNOTAVAILABLE, e.HResult, "Unexpected HRESULT");
+        }
+
+        static int Main(string[] doNotUse)
+        {
+            try
+            {
+                InvalidInterfaceRequest();
+                ClassNotRegistered();
+            }
+            catch (Exception e)
+            {
+                Console.WriteLine($"Test Failure: {e}");
+                return 101;
+            }
+
+            return 100;
+        }
+    }
+}
diff --git a/tests/src/Interop/COM/NETServer/ArrayTesting.cs b/tests/src/Interop/COM/NETServer/ArrayTesting.cs
new file mode 100644 (file)
index 0000000..3cdd5d1
--- /dev/null
@@ -0,0 +1,241 @@
+// 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;
+
+#pragma warning disable 618 // Must test deprecated features
+
+[ComVisible(true)]
+[Guid(Server.Contract.Guids.ArrayTesting)]
+public class ArrayTesting : Server.Contract.IArrayTesting
+{
+    private static double Mean(byte[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+    private static double Mean(short[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+    private static double Mean(ushort[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+    private static double Mean(int[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+    private static double Mean(uint[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+    private static double Mean(long[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+    private static double Mean(ulong[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+    private static double Mean(float[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+    private static double Mean(double[] d)
+    {
+        double t = 0.0;
+        foreach (var b in d)
+        {
+            t += b;
+        }
+        return (t / d.Length);
+    }
+
+    public double Mean_Byte_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] byte[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Short_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] short[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_UShort_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] ushort[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Int_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] int[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_UInt_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] uint[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Long_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] long[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_ULong_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] ulong[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Float_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] float[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Double_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] double[] d)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Byte_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] byte[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Short_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] short[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_UShort_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ushort[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Int_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] int[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_UInt_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] uint[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Long_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] long[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_ULong_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ulong[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Float_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] float[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Double_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] double[] d, int len)
+    {
+        return Mean(d);
+    }
+
+    public double Mean_Byte_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] byte[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+
+    public double Mean_Short_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] short[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+
+    public double Mean_UShort_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] ushort[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+
+    public double Mean_Int_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] int[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+
+    public double Mean_UInt_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] uint[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+
+    public double Mean_Long_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] long[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+
+    public double Mean_ULong_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] ulong[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+
+    public double Mean_Float_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] float[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+
+    public double Mean_Double_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] double[] d, out int len)
+    {
+        len = d.Length;
+        return Mean(d);
+    }
+}
+
+#pragma warning restore 618 // Must test deprecated features
\ No newline at end of file
diff --git a/tests/src/Interop/COM/NETServer/ErrorMarshalTesting.cs b/tests/src/Interop/COM/NETServer/ErrorMarshalTesting.cs
new file mode 100644 (file)
index 0000000..6bd104f
--- /dev/null
@@ -0,0 +1,28 @@
+// 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;
+
+[ComVisible(true)]
+[Guid(Server.Contract.Guids.ErrorMarshalTesting)]
+public class ErrorMarshalTesting : Server.Contract.IErrorMarshalTesting
+{
+    public void Throw_HResult(int hresultToReturn)
+    {
+        // This GetExceptionForHR call is needed to 'eat' the IErrorInfo put to TLS by
+        // any previous exception on this thread. If this isn't done, calls can return
+        // previous exception objects that have occurred.
+        Marshal.GetExceptionForHR(hresultToReturn);
+
+        Exception e = Marshal.GetExceptionForHR(hresultToReturn);
+        throw e;
+    }
+
+    [PreserveSig]
+    public int Return_As_HResult(int hresultToReturn)
+    {
+        return hresultToReturn;
+    }
+}
\ No newline at end of file
index 214bb25..0610fca 100644 (file)
@@ -1,6 +1,7 @@
 <?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>
@@ -9,8 +10,6 @@
     <ProjectGuid>{C04AB564-CC61-499D-9F4C-AA1A9FDE42C9}</ProjectGuid>
     <OutputType>library</OutputType>
     <ProjectTypeGuids>{4948E98A-ECFC-4988-851E-68E1ADD2DD5A};{B850CC46-E8FB-4569-A28D-423F81E8A861}</ProjectTypeGuids>
-    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
-    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
   </PropertyGroup>
   <!-- Default configurations to help VS understand the configurations -->
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
   </PropertyGroup>
   <ItemGroup>
-    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
-      <Visible>False</Visible>
-    </CodeAnalysisDependentAssemblyPaths>
-  </ItemGroup>
-  <ItemGroup>
     <Compile Include="ImportedTypes.cs" />
+    <Compile Include="NumericTesting.cs" />
+    <Compile Include="ArrayTesting.cs" />
+    <Compile Include="StringTesting.cs" />
+    <Compile Include="ErrorMarshalTesting.cs" />
+    <Compile Include="../ServerContracts/Primitives.cs" />
     <Compile Include="../ServerContracts/ServerGuids.cs" />
   </ItemGroup>
-  <ItemGroup>
-    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
-  </ItemGroup>
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
 </Project>
\ No newline at end of file
diff --git a/tests/src/Interop/COM/NETServer/NumericTesting.cs b/tests/src/Interop/COM/NETServer/NumericTesting.cs
new file mode 100644 (file)
index 0000000..c6b1163
--- /dev/null
@@ -0,0 +1,193 @@
+// 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;
+
+#pragma warning disable 618 // Must test deprecated features
+
+[ComVisible(true)]
+[Guid(Server.Contract.Guids.NumericTesting)]
+public class NumericTesting : Server.Contract.INumericTesting
+{
+    public byte Add_Byte(byte a, byte b)
+    {
+        return (byte)(a + b);
+    }
+
+    public short Add_Short(short a, short b)
+    {
+        return (short)(a + b);
+    }
+
+    public ushort Add_UShort(ushort a, ushort b)
+    {
+        return (ushort)(a + b);
+    }
+
+    public int Add_Int(int a, int b)
+    {
+        return a + b;
+    }
+
+    public uint Add_UInt(uint a, uint b)
+    {
+        return a + b;
+    }
+
+    public long Add_Long(long a, long b)
+    {
+        return a + b;
+    }
+
+    public ulong Add_ULong(ulong a, ulong b)
+    {
+        return a + b;
+    }
+
+    public float Add_Float(float a, float b)
+    {
+        return a + b;
+    }
+
+    public double Add_Double(double a, double b)
+    {
+        return a + b;
+    }
+
+    public void Add_Byte_Ref(byte a, byte b, ref byte c)
+    {
+        if (c != byte.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = (byte)(a + b);
+    }
+
+    public void Add_Short_Ref(short a, short b, ref short c)
+    {
+        if (c != short.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = (short)(a + b);
+    }
+
+    public void Add_UShort_Ref(ushort a, ushort b, ref ushort c)
+    {
+        if (c != ushort.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = (ushort)(a + b);
+    }
+
+    public void Add_Int_Ref(int a, int b, ref int c)
+    {
+        if (c != int.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = a + b;
+    }
+
+    public void Add_UInt_Ref(uint a, uint b, ref uint c)
+    {
+        if (c != uint.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = a + b;
+    }
+
+    public void Add_Long_Ref(long a, long b, ref long c)
+    {
+        if (c != long.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = a + b;
+    }
+
+    public void Add_ULong_Ref(ulong a, ulong b, ref ulong c)
+    {
+        if (c != ulong.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = a + b;
+    }
+
+    public void Add_Float_Ref(float a, float b, ref float c)
+    {
+        if (c != float.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = a + b;
+    }
+
+    public void Add_Double_Ref(double a, double b, ref double c)
+    {
+        if (c != double.MaxValue)
+        {
+            throw new Exception();
+        }
+
+        c = a + b;
+    }
+
+    public void Add_Byte_Out(byte a, byte b, out byte c)
+    {
+        c = (byte)(a + b);
+    }
+
+    public void Add_Short_Out(short a, short b, out short c)
+    {
+        c = (short)(a + b);
+    }
+
+    public void Add_UShort_Out(ushort a, ushort b, out ushort c)
+    {
+        c = (ushort)(a + b);
+    }
+
+    public void Add_Int_Out(int a, int b, out int c)
+    {
+        c = a + b;
+    }
+
+    public void Add_UInt_Out(uint a, uint b, out uint c)
+    {
+        c = a + b;
+    }
+
+    public void Add_Long_Out(long a, long b, out long c)
+    {
+        c = a + b;
+    }
+
+    public void Add_ULong_Out(ulong a, ulong b, out ulong c)
+    {
+        c = a + b;
+    }
+
+    public void Add_Float_Out(float a, float b, out float c)
+    {
+        c = a + b;
+    }
+
+    public void Add_Double_Out(double a, double b, out double c)
+    {
+        c = a + b;
+    }
+}
\ No newline at end of file
diff --git a/tests/src/Interop/COM/NETServer/StringTesting.cs b/tests/src/Interop/COM/NETServer/StringTesting.cs
new file mode 100644 (file)
index 0000000..3a510f5
--- /dev/null
@@ -0,0 +1,191 @@
+// 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.Text;
+using System.Runtime.InteropServices;
+
+#pragma warning disable 618 // Must test deprecated features
+
+[ComVisible(true)]
+[Guid(Server.Contract.Guids.StringTesting)]
+public class StringTesting : Server.Contract.IStringTesting
+{
+    private static string Reverse(string s)
+    {
+        var chars = s.ToCharArray();
+        Array.Reverse(chars);
+        return new string(chars);
+    }
+
+    [return: MarshalAs(UnmanagedType.LPStr)]
+    public string Add_LPStr(
+        [MarshalAs(UnmanagedType.LPStr)] string a,
+        [MarshalAs(UnmanagedType.LPStr)] string b)
+    {
+        return a + b;
+    }
+
+    [return: MarshalAs(UnmanagedType.LPWStr)]
+    public string Add_LPWStr(
+        [MarshalAs(UnmanagedType.LPWStr)] string a,
+        [MarshalAs(UnmanagedType.LPWStr)] string b)
+    {
+        return a + b;
+    }
+
+    [return: MarshalAs(UnmanagedType.BStr)]
+    public string Add_BStr(
+        [MarshalAs(UnmanagedType.BStr)] string a,
+        [MarshalAs(UnmanagedType.BStr)] string b)
+    {
+        return a + b;
+    }
+
+    // LPStr
+
+    [return: MarshalAs(UnmanagedType.LPStr)]
+    public string Reverse_LPStr([MarshalAs(UnmanagedType.LPStr)] string a)
+    {
+        return Reverse(a);
+    }
+
+    [return: MarshalAs(UnmanagedType.LPStr)]
+    public string Reverse_LPStr_Ref([MarshalAs(UnmanagedType.LPStr)] ref string a)
+    {
+        return Reverse(a);
+    }
+
+    [return: MarshalAs(UnmanagedType.LPStr)]
+    public string Reverse_LPStr_InRef([In][MarshalAs(UnmanagedType.LPStr)] ref string a)
+    {
+        return Reverse(a);
+    }
+
+    public void Reverse_LPStr_Out([MarshalAs(UnmanagedType.LPStr)] string a, [MarshalAs(UnmanagedType.LPStr)] out string b)
+    {
+        b = Reverse(a);
+    }
+
+    public void Reverse_LPStr_OutAttr([MarshalAs(UnmanagedType.LPStr)] string a, [Out][MarshalAs(UnmanagedType.LPStr)] string b)
+    {
+        b = Reverse(a);
+    }
+
+    [return: MarshalAs(UnmanagedType.LPStr)]
+    public StringBuilder Reverse_SB_LPStr([MarshalAs(UnmanagedType.LPStr)] StringBuilder a)
+    {
+        return new StringBuilder(Reverse(a.ToString()));
+    }
+
+    [return: MarshalAs(UnmanagedType.LPStr)]
+    public StringBuilder Reverse_SB_LPStr_Ref([MarshalAs(UnmanagedType.LPStr)] ref StringBuilder a)
+    {
+        return new StringBuilder(Reverse(a.ToString()));
+    }
+
+    [return: MarshalAs(UnmanagedType.LPStr)]
+    public StringBuilder Reverse_SB_LPStr_InRef([In][MarshalAs(UnmanagedType.LPStr)] ref StringBuilder a)
+    {
+        return new StringBuilder(Reverse(a.ToString()));
+    }
+
+    public void Reverse_SB_LPStr_Out([MarshalAs(UnmanagedType.LPStr)] StringBuilder a, [MarshalAs(UnmanagedType.LPStr)] out StringBuilder b)
+    {
+        b = new StringBuilder(Reverse(a.ToString()));
+    }
+
+    public void Reverse_SB_LPStr_OutAttr([MarshalAs(UnmanagedType.LPStr)] StringBuilder a, [Out][MarshalAs(UnmanagedType.LPStr)] StringBuilder b)
+    {
+        b.Append(Reverse(a.ToString()));
+    }
+
+    // LPWStr
+
+    [return: MarshalAs(UnmanagedType.LPWStr)]
+    public string Reverse_LPWStr([MarshalAs(UnmanagedType.LPWStr)] string a)
+    {
+        return Reverse(a);
+    }
+
+    [return: MarshalAs(UnmanagedType.LPWStr)]
+    public string Reverse_LPWStr_Ref([MarshalAs(UnmanagedType.LPWStr)] ref string a)
+    {
+        return Reverse(a);
+    }
+
+    [return: MarshalAs(UnmanagedType.LPWStr)]
+    public string Reverse_LPWStr_InRef([In][MarshalAs(UnmanagedType.LPWStr)] ref string a)
+    {
+        return Reverse(a);
+    }
+
+    public void Reverse_LPWStr_Out([MarshalAs(UnmanagedType.LPWStr)] string a, [MarshalAs(UnmanagedType.LPWStr)] out string b)
+    {
+        b = Reverse(a);
+    }
+
+    public void Reverse_LPWStr_OutAttr([MarshalAs(UnmanagedType.LPWStr)] string a, [Out][MarshalAs(UnmanagedType.LPWStr)] string b)
+    {
+        b = Reverse(a);
+    }
+
+    [return: MarshalAs(UnmanagedType.LPWStr)]
+    public StringBuilder Reverse_SB_LPWStr([MarshalAs(UnmanagedType.LPWStr)] StringBuilder a)
+    {
+        return new StringBuilder(Reverse(a.ToString()));
+    }
+
+    [return: MarshalAs(UnmanagedType.LPWStr)]
+    public StringBuilder Reverse_SB_LPWStr_Ref([MarshalAs(UnmanagedType.LPWStr)] ref StringBuilder a)
+    {
+        return new StringBuilder(Reverse(a.ToString()));
+    }
+
+    [return: MarshalAs(UnmanagedType.LPWStr)]
+    public StringBuilder Reverse_SB_LPWStr_InRef([In][MarshalAs(UnmanagedType.LPWStr)] ref StringBuilder a)
+    {
+        return new StringBuilder(Reverse(a.ToString()));
+    }
+
+    public void Reverse_SB_LPWStr_Out([MarshalAs(UnmanagedType.LPWStr)] StringBuilder a, [MarshalAs(UnmanagedType.LPWStr)] out StringBuilder b)
+    {
+        b = new StringBuilder(Reverse(a.ToString()));
+    }
+
+    public void Reverse_SB_LPWStr_OutAttr([MarshalAs(UnmanagedType.LPWStr)] StringBuilder a, [Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder b)
+    {
+        b.Append(Reverse(a.ToString()));
+    }
+
+    // BSTR
+
+    [return: MarshalAs(UnmanagedType.BStr)]
+    public string Reverse_BStr([MarshalAs(UnmanagedType.BStr)] string a)
+    {
+        return Reverse(a);
+    }
+
+    [return: MarshalAs(UnmanagedType.BStr)]
+    public string Reverse_BStr_Ref([MarshalAs(UnmanagedType.BStr)] ref string a)
+    {
+        return Reverse(a);
+    }
+
+    [return: MarshalAs(UnmanagedType.BStr)]
+    public string Reverse_BStr_InRef([In][MarshalAs(UnmanagedType.BStr)] ref string a)
+    {
+        return Reverse(a);
+    }
+
+    public void Reverse_BStr_Out([MarshalAs(UnmanagedType.BStr)] string a, [MarshalAs(UnmanagedType.BStr)] out string b)
+    {
+        b = Reverse(a);
+    }
+
+    public void Reverse_BStr_OutAttr([MarshalAs(UnmanagedType.BStr)] string a, [Out][MarshalAs(UnmanagedType.BStr)] string b)
+    {
+        b = Reverse(a);
+    }
+}
\ No newline at end of file
diff --git a/tests/src/Interop/COM/NativeClients/Primitives.csproj b/tests/src/Interop/COM/NativeClients/Primitives.csproj
new file mode 100644 (file)
index 0000000..b1bc0e6
--- /dev/null
@@ -0,0 +1,17 @@
+<?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>
+    <IgnoreInteropAssertionFile>true</IgnoreInteropAssertionFile>
+    <CLRTestScriptLocalCoreShim>true</CLRTestScriptLocalCoreShim>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(InteropCommonDir)ExeLauncherProgram.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="Primitives/CMakeLists.txt"/>
+    <ProjectReference Include="../NetServer/NetServer.csproj" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
\ No newline at end of file
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/App.manifest b/tests/src/Interop/COM/NativeClients/Primitives/App.manifest
new file mode 100644 (file)
index 0000000..ed66508
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity
+    type="win32"
+    name="COMClientPrimitives"
+    version="1.0.0.0"/>
+
+  <dependency>
+    <dependentAssembly>
+      <!-- RegFree COM - CoreCLR Shim -->
+      <assemblyIdentity
+        type="win32"
+        name="CoreShim.X"
+        version="1.0.0.0"/>
+    </dependentAssembly>
+  </dependency>
+</assembly>
\ No newline at end of file
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/ArrayTests.cpp b/tests/src/Interop/COM/NativeClients/Primitives/ArrayTests.cpp
new file mode 100644 (file)
index 0000000..5b54a14
--- /dev/null
@@ -0,0 +1,318 @@
+// 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 "ClientTests.h"
+#include <numeric>
+#include <vector>
+
+namespace
+{
+    bool EqualByBound(double expected, double actual)
+    {
+        double low = expected - 0.00001;
+        double high = expected + 0.00001;
+        double eps = std::fabs(expected - actual);
+        bool isEqual = eps < std::numeric_limits<double>::epsilon() || (low < actual && actual < high);
+        return isEqual;
+    }
+
+    template<typename T>
+    std::vector<T> Convert(_In_ const std::vector<int> &in)
+    {
+        std::vector<T> out;
+        for (auto i : in)
+            out.push_back((T)i);
+        return out;
+    }
+
+    template<typename T>
+    VARTYPE ToSafeArrayType();
+    template<>
+    VARTYPE ToSafeArrayType<byte>() { return VT_UI1; }
+    template<>
+    VARTYPE ToSafeArrayType<int16_t>() { return VT_I2; }
+    template<>
+    VARTYPE ToSafeArrayType<uint16_t>() { return VT_UI2; }
+    template<>
+    VARTYPE ToSafeArrayType<int32_t>() { return VT_I4; }
+    template<>
+    VARTYPE ToSafeArrayType<uint32_t>() { return VT_UI4; }
+    template<>
+    VARTYPE ToSafeArrayType<int64_t>() { return VT_I8; }
+    template<>
+    VARTYPE ToSafeArrayType<uint64_t>() { return VT_UI8; }
+    template<>
+    VARTYPE ToSafeArrayType<float>() { return VT_R4; }
+    template<>
+    VARTYPE ToSafeArrayType<double>() { return VT_R8; }
+
+    template<typename T>
+    class SafeArraySmartPtr
+    {
+    public:
+        SafeArraySmartPtr(_In_ const std::vector<T> &in)
+            : _safeArray{}
+            , _elementCount{ static_cast<int>(in.size()) }
+        {
+            SAFEARRAYBOUND saBound;
+            saBound.lLbound = 0;
+            saBound.cElements = static_cast<ULONG>(in.size());
+
+            _safeArray = ::SafeArrayCreate(ToSafeArrayType<T>(), 1, &saBound);
+            assert(_safeArray != nullptr);
+
+            std::memcpy(static_cast<T*>(_safeArray->pvData), in.data(), sizeof(T) * in.size());
+        }
+
+        ~SafeArraySmartPtr()
+        {
+            ::SafeArrayDestroy(_safeArray);
+        }
+
+        int Length() const
+        {
+            return _elementCount;
+        }
+
+        operator SAFEARRAY *()
+        {
+            return _safeArray;
+        }
+
+    private:
+        int _elementCount;
+        SAFEARRAY *_safeArray;
+    };
+
+    void ByteArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<byte>(baseData);
+
+        ::printf("Byte[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Byte_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Byte_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<byte> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Byte_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+
+    void ShortArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<int16_t>(baseData);
+
+        ::printf("Short[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Short_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Short_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<int16_t> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Short_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+
+    void UShortArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<uint16_t>(baseData);
+
+        ::printf("UShort[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_UShort_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_UShort_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<uint16_t> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_UShort_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+
+    void IntArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<int32_t>(baseData);
+
+        ::printf("Int[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Int_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Int_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<int32_t> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Int_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+
+    void UIntArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<uint32_t>(baseData);
+
+        ::printf("UInt[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_UInt_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_UInt_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<uint32_t> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_UInt_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+
+    void LongArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<int64_t>(baseData);
+
+        ::printf("Long[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Long_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Long_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<int64_t> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Long_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+
+    void ULongArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<uint64_t>(baseData);
+
+        ::printf("ULong[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_ULong_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_ULong_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<uint64_t> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_ULong_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+
+    void FloatArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<float>(baseData);
+
+        ::printf("Float[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Float_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Float_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<float> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Float_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+
+    void DoubleArray(_In_ IArrayTesting *arrayTesting, _In_ const std::vector<int> &baseData, _In_ double expectedMean)
+    {
+        HRESULT hr;
+        auto data = Convert<double>(baseData);
+
+        ::printf("Double[] marshalling\n");
+
+        double actual;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Double_LP_PreLen(static_cast<int>(baseData.size()), data.data(), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Double_LP_PostLen(data.data(), static_cast<int>(baseData.size()), &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+
+        actual = 0.0;
+        int len;
+        SafeArraySmartPtr<double> saData{ data };
+        THROW_IF_FAILED(arrayTesting->raw_Mean_Double_SafeArray_OutLen(saData, &len, &actual));
+        THROW_FAIL_IF_FALSE(EqualByBound(expectedMean, actual));
+        THROW_FAIL_IF_FALSE(len == saData.Length());
+    }
+}
+
+void Run_ArrayTests()
+{
+    HRESULT hr;
+
+    CoreShimComActivation csact{ W("NETServer.dll"), W("ArrayTesting") };
+
+    ComSmartPtr<IArrayTesting> arrayTesting;
+    THROW_IF_FAILED(::CoCreateInstance(CLSID_ArrayTesting, nullptr, CLSCTX_INPROC, IID_IArrayTesting, (void**)&arrayTesting));
+
+    std::vector<int> baseData(10);
+    std::iota(std::begin(baseData), std::end(baseData), 0);
+    double mean = std::accumulate(std::begin(baseData), std::end(baseData), 0.0) / baseData.size();
+
+    ByteArray(arrayTesting, baseData, mean);
+    ShortArray(arrayTesting, baseData, mean);
+    UShortArray(arrayTesting, baseData, mean);
+    IntArray(arrayTesting, baseData, mean);
+    UIntArray(arrayTesting, baseData, mean);
+    LongArray(arrayTesting, baseData, mean);
+    ULongArray(arrayTesting, baseData, mean);
+    FloatArray(arrayTesting, baseData, mean);
+    DoubleArray(arrayTesting, baseData, mean);
+}
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/CMakeLists.txt b/tests/src/Interop/COM/NativeClients/Primitives/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b2b2fb0
--- /dev/null
@@ -0,0 +1,23 @@
+cmake_minimum_required (VERSION 2.6)
+
+project (COMClientPrimitives)
+include_directories( ${INC_PLATFORM_DIR} )
+include_directories( "../../ServerContracts" )
+include_directories( "../../NativeServer" )
+set(SOURCES
+    Client.cpp
+    NumericTests.cpp
+    ArrayTests.cpp
+    StringTests.cpp
+    ErrorTests.cpp
+    App.manifest)
+
+# add the executable
+add_executable (COMClientPrimitives ${SOURCES})
+target_link_libraries(COMClientPrimitives ${LINK_LIBRARIES_ADDITIONAL})
+
+# Copy CoreShim manifest to project output
+file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/CoreShim.X.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/CoreShim.X.manifest)
+
+# add the install targets
+install (TARGETS COMClientPrimitives DESTINATION bin)
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/Client.cpp b/tests/src/Interop/COM/NativeClients/Primitives/Client.cpp
new file mode 100644 (file)
index 0000000..bfb32c5
--- /dev/null
@@ -0,0 +1,45 @@
+// 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 "ClientTests.h"
+
+template<COINIT TM>
+struct ComInit
+{
+    const HRESULT Result;
+
+    ComInit()
+        : Result{ ::CoInitializeEx(nullptr, TM) }
+    { }
+
+    ~ComInit()
+    {
+        if (SUCCEEDED(Result))
+            ::CoUninitialize();
+    }
+};
+
+using ComMTA = ComInit<COINIT_MULTITHREADED>;
+
+int __cdecl main()
+{
+    ComMTA init;
+    if (FAILED(init.Result))
+        return -1;
+
+    try
+    {
+        Run_NumericTests();
+        Run_ArrayTests();
+        Run_StringTests();
+        Run_ErrorTests();
+    }
+    catch (HRESULT hr)
+    {
+        ::printf("Test Failure: 0x%08x\n", hr);
+        return 101;
+    }
+
+    return 100;
+}
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/ClientTests.h b/tests/src/Interop/COM/NativeClients/Primitives/ClientTests.h
new file mode 100644 (file)
index 0000000..029ed02
--- /dev/null
@@ -0,0 +1,70 @@
+// 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 <cassert>
+#include <Server.Contracts.tlh>
+
+#define COM_CLIENT
+#include <Servers.h>
+
+#define THROW_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { ::printf("FAILURE: 0x%08x = %s\n", hr, #exp); throw hr; } }
+#define THROW_FAIL_IF_FALSE(exp) { if (!(exp)) { ::printf("FALSE: %s\n", #exp); throw E_FAIL; } }
+
+template<typename T>
+struct ComSmartPtr
+{
+    ComSmartPtr()
+        : p{}
+    { }
+
+    ComSmartPtr(_In_ const ComSmartPtr &) = delete;
+    ComSmartPtr(_Inout_ ComSmartPtr &&) = delete;
+
+    ComSmartPtr& operator=(_In_ const ComSmartPtr &) = delete;
+    ComSmartPtr& operator=(_Inout_ ComSmartPtr &&) = delete;
+
+    ~ComSmartPtr()
+    {
+        if (p != nullptr)
+            p->Release();
+    }
+
+    operator T*()
+    {
+        return p;
+    }
+
+    T** operator&()
+    {
+        return &p;
+    }
+
+    T* operator->()
+    {
+        return p;
+    }
+
+    void Attach(_In_opt_ T *t)
+    {
+        if (p != nullptr)
+            p->Release();
+
+        p = t;
+    }
+
+    T *Detach()
+    {
+        T *tmp = p;
+        p = nullptr;
+        return tmp;
+    }
+
+    T *p;
+};
+
+void Run_NumericTests();
+void Run_ArrayTests();
+void Run_StringTests();
+void Run_ErrorTests();
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/CoreShim.X.manifest b/tests/src/Interop/COM/NativeClients/Primitives/CoreShim.X.manifest
new file mode 100644 (file)
index 0000000..1dba8e0
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+
+<assemblyIdentity
+  type="win32"
+  name="CoreShim.X"
+  version="1.0.0.0" />
+
+<file name="CoreShim.dll">
+  <!-- NumericTesting -->
+  <comClass
+    clsid="{53169A33-E85D-4E3C-B668-24E438D0929B}"
+    threadingModel="Both" />
+  <!-- ArrayTesting -->
+  <comClass
+    clsid="{B99ABE6A-DFF6-440F-BFB6-55179B8FE18E}"
+    threadingModel="Both" />
+  <!-- StringTesting -->
+  <comClass
+    clsid="{C73C83E8-51A2-47F8-9B5C-4284458E47A6}"
+    threadingModel="Both" />
+  <!-- ErrorMarshalTesting -->
+  <comClass
+    clsid="{71CF5C45-106C-4B32-B418-43A463C6041F}"
+    threadingModel="Both" />
+</file>
+
+</assembly>
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/ErrorTests.cpp b/tests/src/Interop/COM/NativeClients/Primitives/ErrorTests.cpp
new file mode 100644 (file)
index 0000000..9f0af22
--- /dev/null
@@ -0,0 +1,68 @@
+// 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 "ClientTests.h"
+
+namespace
+{
+    void VerifyExpectedException(_In_ IErrorMarshalTesting *et)
+    {
+       ::printf("Verify expected exception from HRESULT\n");
+
+        HRESULT hrs[] =
+        {
+            E_NOTIMPL,
+            E_POINTER,
+            E_ACCESSDENIED,
+            E_OUTOFMEMORY,
+            E_INVALIDARG,
+            E_UNEXPECTED,
+            HRESULT{-1}
+        };
+
+        for (int i = 0; i < ARRAYSIZE(hrs); ++i)
+        {
+            HRESULT hr = hrs[i];
+            HRESULT hrMaybe = et->raw_Throw_HResult(hr);
+            THROW_FAIL_IF_FALSE(hr == hrMaybe);
+        }
+    }
+
+    void VerifyReturnHResult(_In_ IErrorMarshalTesting *et)
+    {
+        ::printf("Verify preserved function signature\n");
+
+        HRESULT hrs[] =
+        {
+            E_NOTIMPL,
+            E_POINTER,
+            E_ACCESSDENIED,
+            E_INVALIDARG,
+            E_UNEXPECTED,
+            HRESULT{-1},
+            S_FALSE,
+            HRESULT{2}
+        };
+
+        for (int i = 0; i < ARRAYSIZE(hrs); ++i)
+        {
+            HRESULT hr = hrs[i];
+            HRESULT hrMaybe = et->Return_As_HResult(hr);
+            THROW_FAIL_IF_FALSE(hr == hrMaybe);
+        }
+    }
+}
+
+void Run_ErrorTests()
+{
+    HRESULT hr;
+
+    CoreShimComActivation csact{ W("NETServer.dll"), W("ErrorMarshalTesting") };
+
+    ComSmartPtr<IErrorMarshalTesting> errorMarshal;
+    THROW_IF_FAILED(::CoCreateInstance(CLSID_ErrorMarshalTesting, nullptr, CLSCTX_INPROC, IID_IErrorMarshalTesting, (void**)&errorMarshal));
+
+    VerifyExpectedException(errorMarshal);
+    VerifyReturnHResult(errorMarshal);
+}
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/NumericTests.cpp b/tests/src/Interop/COM/NativeClients/Primitives/NumericTests.cpp
new file mode 100644 (file)
index 0000000..68ae4f4
--- /dev/null
@@ -0,0 +1,227 @@
+// 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 "ClientTests.h"
+#include <cstdint>
+#include <limits>
+
+namespace
+{
+    void MarshalByte(_In_ INumericTesting *numericTesting, _In_ byte a, _In_ byte b)
+    {
+        HRESULT hr;
+
+        byte expected = a + b;
+        ::printf("Byte test invariant: %d + %d = %d\n", a, b, expected);
+
+        byte c;
+        THROW_IF_FAILED(numericTesting->raw_Add_Byte(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_Byte_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_Byte_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+    }
+
+    void MarshalShort(_In_ INumericTesting *numericTesting, _In_ int16_t a, _In_ int16_t b)
+    {
+        HRESULT hr;
+
+        int16_t expected = a + b;
+        ::printf("Short test invariant: %d + %d = %d\n", a, b, expected);
+
+        int16_t c;
+        THROW_IF_FAILED(numericTesting->raw_Add_Short(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_Short_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_Short_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+    }
+
+    void MarshalUShort(_In_ INumericTesting *numericTesting, _In_ uint16_t a, _In_ uint16_t b)
+    {
+        HRESULT hr;
+
+        uint16_t expected = a + b;
+        ::printf("UShort test invariant: %u + %u = %u\n", a, b, expected);
+
+        uint16_t c;
+        THROW_IF_FAILED(numericTesting->raw_Add_UShort(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_UShort_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_UShort_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+    }
+
+    void MarshalInt(_In_ INumericTesting *numericTesting, _In_ int32_t a, _In_ int32_t b)
+    {
+        HRESULT hr;
+
+        int32_t expected = a + b;
+        ::printf("Int test invariant: %d + %d = %d\n", a, b, expected);
+
+        int32_t c;
+        THROW_IF_FAILED(numericTesting->raw_Add_Int(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_Int_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_Int_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+    }
+
+    void MarshalUInt(_In_ INumericTesting *numericTesting, _In_ uint32_t a, _In_ uint32_t b)
+    {
+        HRESULT hr;
+
+        uint32_t expected = a + b;
+        ::printf("UInt test invariant: %u + %u = %u\n", a, b, expected);
+
+        uint32_t c;
+        THROW_IF_FAILED(numericTesting->raw_Add_UInt(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_UInt_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_UInt_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+    }
+
+    void MarshalLong(_In_ INumericTesting *numericTesting, _In_ int64_t a, _In_ int64_t b)
+    {
+        HRESULT hr;
+
+        int64_t expected = a + b;
+        ::printf("Long test invariant: %lld + %lld = %lld\n", a, b, expected);
+
+        int64_t c;
+        THROW_IF_FAILED(numericTesting->raw_Add_Long(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_Long_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_Long_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+    }
+
+    void MarshalULong(_In_ INumericTesting *numericTesting, _In_ uint64_t a, _In_ uint64_t b)
+    {
+        HRESULT hr;
+
+        uint64_t expected = a + b;
+        ::printf("ULong test invariant: %llu + %llu = %llu\n", a, b, expected);
+
+        uint64_t c;
+        THROW_IF_FAILED(numericTesting->raw_Add_ULong(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_ULong_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_ULong_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(expected == c);
+    }
+
+    template<typename T>
+    bool EqualByBound(_In_ T expected, _In_ T actual)
+    {
+        T low = expected - (T)0.0001;
+        T high = expected + (T)0.0001;
+        T eps = std::abs(expected - actual);
+        return (eps < std::numeric_limits<T>::epsilon() || (low < actual && actual < high));
+    }
+
+    void MarshalFloat(_In_ INumericTesting *numericTesting, _In_ float a, _In_ float b)
+    {
+        HRESULT hr;
+
+        float expected = a + b;
+        ::printf("Float test invariant: %f + %f = %f\n", a, b, expected);
+
+        float c;
+        THROW_IF_FAILED(numericTesting->raw_Add_Float(a, b, &c));
+        THROW_FAIL_IF_FALSE(EqualByBound(expected, c));
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_Float_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(EqualByBound(expected, c));
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_Float_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(EqualByBound(expected, c));
+    }
+
+    void MarshalDouble(_In_ INumericTesting *numericTesting, _In_ double a, _In_ double b)
+    {
+        HRESULT hr;
+
+        double expected = a + b;
+        ::printf("Double test invariant: %f + %f = %f\n", a, b, expected);
+
+        double c;
+        THROW_IF_FAILED(numericTesting->raw_Add_Double(a, b, &c));
+        THROW_FAIL_IF_FALSE(EqualByBound(expected, c));
+
+        c = std::numeric_limits<decltype(c)>::max();
+        THROW_IF_FAILED(numericTesting->raw_Add_Double_Ref(a, b, &c));
+        THROW_FAIL_IF_FALSE(EqualByBound(expected, c));
+
+        c = 0;
+        THROW_IF_FAILED(numericTesting->raw_Add_Double_Out(a, b, &c));
+        THROW_FAIL_IF_FALSE(EqualByBound(expected, c));
+    }
+}
+
+void Run_NumericTests()
+{
+    HRESULT hr;
+
+    CoreShimComActivation csact{ W("NETServer.dll"), W("NumericTesting") };
+
+    ComSmartPtr<INumericTesting> numericTesting;
+    THROW_IF_FAILED(::CoCreateInstance(CLSID_NumericTesting, nullptr, CLSCTX_INPROC, IID_INumericTesting, (void**)&numericTesting));
+
+    int seed = 37;
+    ::srand(seed);
+
+    ::printf("Numeric RNG seed: %d\n", seed);
+
+    int a = ::rand();
+    int b = ::rand();
+
+    MarshalByte(numericTesting, (byte)a, (byte)b);
+    MarshalShort(numericTesting, (int16_t)a, (int16_t)b);
+    MarshalUShort(numericTesting, (uint16_t)a, (uint16_t)b);
+    MarshalInt(numericTesting, a, b);
+    MarshalUInt(numericTesting, (uint32_t)a, (uint32_t)b);
+    MarshalLong(numericTesting, (int64_t)a, (int64_t)b);
+    MarshalULong(numericTesting, (uint64_t)a, (uint64_t)b);
+    MarshalFloat(numericTesting, (float)a / 100.f, (float)b / 100.f);
+    MarshalDouble(numericTesting, (double)a / 100.0, (double)b / 100.0);
+}
diff --git a/tests/src/Interop/COM/NativeClients/Primitives/StringTests.cpp b/tests/src/Interop/COM/NativeClients/Primitives/StringTests.cpp
new file mode 100644 (file)
index 0000000..a9dad99
--- /dev/null
@@ -0,0 +1,422 @@
+// 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 "ClientTests.h"
+#include <vector>
+#include <sstream>
+
+namespace
+{
+    template
+    <
+        typename CT,
+        void*(*STR_ALLOC)(size_t),
+        void(*STR_FREE)(void*)
+    >
+    class AnyStr
+    {
+    public:
+        AnyStr() : _lenBytes{ 0 } , _str{ nullptr }
+        { }
+
+        explicit AnyStr(_In_z_ const char *str)
+            : _lenBytes{ (::strlen(str) + 1) * sizeof(CT) }
+        {
+            _str = (CT *)STR_ALLOC(_lenBytes);
+            CT *strLocal = _str;
+
+            while (*str)
+            {
+                // [TODO] handle UTF8
+                *strLocal = static_cast<CT>(*str++);
+                strLocal++;
+            }
+            *strLocal = CT{ '\0' };
+        }
+
+        // Concat strings
+        AnyStr(_In_ const AnyStr &l, _In_ const AnyStr &r)
+            : _lenBytes{ l._lenBytes + r._lenBytes - sizeof(CT) } // Remove duplicate null
+        {
+            _str = (CT *)STR_ALLOC(_lenBytes);
+            CT *strLocal = _str;
+            std::memcpy(strLocal, l._str, l._lenBytes - sizeof(CT)); // Ignore null
+
+            size_t l_len = l.Length();
+            std::memcpy(strLocal + l_len, r._str, r._lenBytes);
+        }
+
+        AnyStr(_In_ const AnyStr &other)
+            : _lenBytes{ other._lenBytes }
+            , _str{ (CT *)STR_ALLOC(other._lenBytes) }
+        {
+            std::memcpy(_str, other._str, _lenBytes);
+        }
+
+        AnyStr& operator=(_In_ const AnyStr &other)
+        {
+            AnyStr old{ std::move(*this) };
+            AnyStr otherCopy{ other };
+            (*this) = std::move(otherCopy);
+            return (*this);
+        }
+
+        AnyStr(_Inout_ AnyStr &&other)
+            : _lenBytes{ other._lenBytes }
+            , _str{ other._str }
+        {
+            other._str = nullptr;
+        }
+
+        AnyStr& operator=(_Inout_ AnyStr &&other)
+        {
+            AnyStr tmp{ std::move(*this) };
+            _lenBytes = other._lenBytes;
+            _str = other._str;
+            other._str = nullptr;
+            return (*this);
+        }
+
+        ~AnyStr()
+        {
+            if (_str != nullptr)
+                STR_FREE(_str);
+        }
+
+        operator CT*()
+        {
+            return _str;
+        }
+
+        operator const CT*() const
+        {
+            return _str;
+        }
+
+        CT** operator &()
+        {
+            return &_str;
+        }
+
+        bool operator==(_In_ const AnyStr &other) const
+        {
+            return EqualTo(other._str);
+        }
+
+        bool operator!=(_In_ const AnyStr &other) const
+        {
+            return !(*this == other);
+        }
+
+        void Attach(_In_z_ CT *data)
+        {
+            AnyStr tmp{ std::move(*this) };
+
+            CT *dataIter = data;
+            int len = 1; // Include 1 for null
+            while (*dataIter++)
+                ++len;
+
+            _str = data;
+            _lenBytes = len * sizeof(CT);
+        }
+
+        // String length _not_ including null
+        size_t Length() const
+        {
+            if (_lenBytes == 0)
+                return 0;
+
+            return (_lenBytes - sizeof(CT)) / sizeof(CT);
+        }
+
+        // String length including null in bytes
+        size_t LengthByte() const
+        {
+            return _lenBytes;
+        }
+
+        bool AllAscii() const
+        {
+            const CT *c = _str;
+            const CT MaxAscii = (CT)0x7f;
+            while (*c)
+            {
+                if ((*c++) > MaxAscii)
+                    return false;
+            }
+
+            return true;
+        }
+
+        bool EqualTo(_In_z_ const CT *str) const
+        {
+            const CT *tmp = str;
+            int len = 1; // Include 1 for null
+            while (*tmp++)
+                ++len;
+
+            if (_lenBytes != (sizeof(CT) * len))
+                return false;
+
+            return (0 == std::memcmp(_str, str, _lenBytes));
+        }
+
+        HRESULT Reverse(_Inout_ AnyStr &res) const
+        {
+            AnyStr tmp{};
+
+            if (_lenBytes > 0)
+            {
+                tmp._lenBytes = _lenBytes;
+                tmp._str = (CT *)STR_ALLOC(_lenBytes);
+                if (tmp._str == nullptr)
+                    return E_OUTOFMEMORY;
+
+                ::memcpy(tmp._str, _str, _lenBytes);
+                std::reverse(tmp._str, tmp._str + Length());
+            }
+
+            res = std::move(tmp);
+            return S_OK;
+        }
+
+    private:
+        size_t _lenBytes;
+        CT *_str;
+    };
+
+    // BSTR string
+    using BStr = AnyStr<OLECHAR, &CoreClrBstrAlloc, &CoreClrBstrFree>;
+
+    // Wide string
+    using WStr = AnyStr<WCHAR, &CoreClrAlloc, &CoreClrFree>;
+
+    // Narrow string
+    using NStr = AnyStr<CHAR, &CoreClrAlloc, &CoreClrFree>;
+
+    template <typename STR>
+    std::vector<std::pair<STR, STR>> GetAddPairs()
+    {
+        std::vector<std::pair<STR, STR>> pairs;
+
+        pairs.push_back({ STR{ "" }, STR{ "" } });
+        pairs.push_back({ STR{ "" }, STR{ "def" } });
+        pairs.push_back({ STR{ "abc" }, STR{ "" } });
+        pairs.push_back({ STR{ "abc" }, STR{ "def" } });
+
+        // String marshalling is optimized where strings shorter than MAX_PATH are
+        // allocated on the stack. Longer strings have memory allocated for them.
+        pairs.push_back({
+            STR{ "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901" },
+            STR{ "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901" }
+            });
+
+        return pairs;
+    }
+
+    template <typename STR>
+    std::vector<STR> GetReversableStrings()
+    {
+        std::vector<STR> rev;
+
+        rev.push_back(STR{ "" });
+        rev.push_back(STR{ "a" });
+        rev.push_back(STR{ "abc" });
+        rev.push_back(STR{ "reversable string" });
+
+        // Long string optimization validation
+        rev.push_back(STR{ "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901" });
+
+        return rev;
+    }
+
+    void Marshal_LPString(_In_ IStringTesting* stringTesting)
+    {
+        ::printf("Marshal strings as LPStr\n");
+
+        HRESULT hr;
+
+        auto pairs = GetAddPairs<NStr>();
+        for (auto &p : pairs)
+        {
+            if (!p.first.AllAscii() || !p.second.AllAscii())
+            {
+                // LPStr doesn't support non-ascii characters
+                continue;
+            }
+
+            LPSTR tmp;
+            NStr expected{ p.first, p.second };
+            THROW_IF_FAILED(stringTesting->raw_Add_LPStr(p.first, p.second, &tmp));
+
+            NStr actual;
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected.EqualTo(actual));
+        }
+
+        auto reversable = GetReversableStrings<NStr>();
+        for (const auto &r : reversable)
+        {
+            if (!r.AllAscii())
+            {
+                // LPStr doesn't support non-ascii characters
+                continue;
+            }
+
+            LPSTR tmp;
+            NStr local{ r };
+
+            NStr actual;
+            NStr expected;
+            THROW_IF_FAILED(r.Reverse(expected));
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPStr(local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPStr_Ref(&local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+            THROW_FAIL_IF_FALSE(r == local); // Local should not be changed
+
+            local = r;
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPStr_InRef(&local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+            THROW_FAIL_IF_FALSE(r == local); // Local should not be changed
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPStr_Out(local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+
+            actual = local;
+            tmp = actual;
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPStr_OutAttr(local, tmp)); // No-op for strings
+            THROW_FAIL_IF_FALSE(local == actual);
+        }
+    }
+
+    void Marshal_LPWString(_In_ IStringTesting* stringTesting)
+    {
+        ::printf("Marshal strings as LPWStr\n");
+
+        HRESULT hr;
+
+        auto pairs = GetAddPairs<WStr>();
+        for (auto &p : pairs)
+        {
+            LPWSTR tmp;
+            WStr expected{ p.first, p.second };
+            THROW_IF_FAILED(stringTesting->raw_Add_LPWStr(p.first, p.second, &tmp));
+
+            WStr actual;
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected.EqualTo(actual));
+        }
+
+        auto reversable = GetReversableStrings<WStr>();
+        for (const auto &r : reversable)
+        {
+            LPWSTR tmp;
+            WStr local{ r };
+
+            WStr actual;
+            WStr expected;
+            THROW_IF_FAILED(r.Reverse(expected));
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPWStr(local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPWStr_Ref(&local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+            THROW_FAIL_IF_FALSE(r == local); // Local should not be changed
+
+            local = r;
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPWStr_InRef(&local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+            THROW_FAIL_IF_FALSE(r == local); // Local should not be changed
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPWStr_Out(local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+
+            actual = local;
+            tmp = actual;
+            THROW_IF_FAILED(stringTesting->raw_Reverse_LPWStr_OutAttr(local, tmp)); // No-op for strings
+            THROW_FAIL_IF_FALSE(local == actual);
+        }
+    }
+
+    void Marshal_BStrString(_In_ IStringTesting* stringTesting)
+    {
+        ::printf("Marshal strings as BStr\n");
+
+        HRESULT hr;
+
+        auto pairs = GetAddPairs<BStr>();
+        for (auto &p : pairs)
+        {
+            BSTR tmp;
+            BStr expected{ p.first, p.second };
+            THROW_IF_FAILED(stringTesting->raw_Add_BStr(p.first, p.second, &tmp));
+
+            BStr actual;
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected.EqualTo(actual));
+        }
+
+        auto reversable = GetReversableStrings<BStr>();
+        for (const auto &r : reversable)
+        {
+            BSTR tmp;
+            BStr local{ r };
+
+            BStr actual;
+            BStr expected;
+            THROW_IF_FAILED(r.Reverse(expected));
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_BStr(local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_BStr_Ref(&local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+            THROW_FAIL_IF_FALSE(r == local); // Local should not be changed
+
+            local = r;
+            THROW_IF_FAILED(stringTesting->raw_Reverse_BStr_InRef(&local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+            THROW_FAIL_IF_FALSE(r == local); // Local should not be changed
+
+            THROW_IF_FAILED(stringTesting->raw_Reverse_BStr_Out(local, &tmp));
+            actual.Attach(tmp);
+            THROW_FAIL_IF_FALSE(expected == actual);
+
+            actual = local;
+            tmp = actual;
+            THROW_IF_FAILED(stringTesting->raw_Reverse_BStr_OutAttr(local, tmp)); // No-op for strings
+            THROW_FAIL_IF_FALSE(local == actual);
+        }
+    }
+}
+
+void Run_StringTests()
+{
+    HRESULT hr;
+
+    CoreShimComActivation csact{ W("NETServer.dll"), W("StringTesting") };
+
+    ComSmartPtr<IStringTesting> stringTesting;
+    THROW_IF_FAILED(::CoCreateInstance(CLSID_StringTesting, nullptr, CLSCTX_INPROC, IID_IStringTesting, (void**)&stringTesting));
+
+    Marshal_LPString(stringTesting);
+    Marshal_LPWString(stringTesting);
+    Marshal_BStrString(stringTesting);
+}
index 6939762..27d0239 100644 (file)
@@ -20,7 +20,7 @@ private:
         return (t / l);
     }
     template<VARTYPE E>
-    HRESULT Mean(SAFEARRAY *d, long *l, double *r)
+    HRESULT Mean(SAFEARRAY *d, int *l, double *r)
     {
         HRESULT hr;
 
@@ -74,7 +74,7 @@ private:
 
 public: // IArrayTesting
     DEF_RAWFUNC(Mean_Byte_LP_PreLen)(
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ unsigned char * d,
         /*[out,retval]*/ double * pRetVal)
     {
@@ -84,7 +84,7 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_Short_LP_PreLen)(
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ short * d,
         /*[out,retval]*/ double * pRetVal)
     {
@@ -94,7 +94,7 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_UShort_LP_PreLen)(
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ unsigned short * d,
         /*[out,retval]*/ double * pRetVal)
     {
@@ -104,8 +104,8 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_Int_LP_PreLen)(
-        /*[in]*/ long len,
-        /*[in]*/ long * d,
+        /*[in]*/ int len,
+        /*[in]*/ int * d,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -114,8 +114,8 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_UInt_LP_PreLen)(
-        /*[in]*/ long len,
-        /*[in]*/ unsigned long * d,
+        /*[in]*/ int len,
+        /*[in]*/ unsigned int * d,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -124,7 +124,7 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_Long_LP_PreLen)(
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ __int64 * d,
         /*[out,retval]*/ double * pRetVal)
     {
@@ -134,7 +134,7 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_ULong_LP_PreLen)(
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ unsigned __int64 * d,
         /*[out,retval]*/ double * pRetVal)
     {
@@ -144,7 +144,7 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_Float_LP_PreLen)(
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ float * d,
         /*[out,retval]*/ double * pRetVal)
     {
@@ -154,7 +154,7 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_Double_LP_PreLen)(
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ double * d,
         /*[out,retval]*/ double * pRetVal)
     {
@@ -165,7 +165,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Byte_LP_PostLen)(
         /*[in]*/ unsigned char * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -175,7 +175,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Short_LP_PostLen)(
         /*[in]*/ short * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -185,7 +185,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_UShort_LP_PostLen)(
         /*[in]*/ unsigned short * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -194,8 +194,8 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_Int_LP_PostLen)(
-        /*[in]*/ long * d,
-        /*[in]*/ long len,
+        /*[in]*/ int * d,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -204,8 +204,8 @@ public: // IArrayTesting
         return S_OK;
     }
     DEF_RAWFUNC(Mean_UInt_LP_PostLen)(
-        /*[in]*/ unsigned long * d,
-        /*[in]*/ long len,
+        /*[in]*/ unsigned int * d,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -215,7 +215,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Long_LP_PostLen)(
         /*[in]*/ __int64 * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -225,7 +225,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_ULong_LP_PostLen)(
         /*[in]*/ unsigned __int64 * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -235,7 +235,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Float_LP_PostLen)(
         /*[in]*/ float * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -245,7 +245,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Double_LP_PostLen)(
         /*[in]*/ double * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -255,7 +255,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Byte_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -264,7 +264,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Short_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -273,7 +273,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_UShort_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -282,7 +282,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Int_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -291,7 +291,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_UInt_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -300,7 +300,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Long_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -309,7 +309,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_ULong_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -318,7 +318,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Float_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
@@ -327,7 +327,7 @@ public: // IArrayTesting
     }
     DEF_RAWFUNC(Mean_Double_SafeArray_OutLen)(
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal)
     {
         if (pRetVal == nullptr)
index 6dd7649..2b1276c 100644 (file)
@@ -10,12 +10,13 @@ class ErrorMarshalTesting : public UnknownImpl, public IErrorMarshalTesting
 {
 public: // IErrorMarshalTesting
     DEF_RAWFUNC(Throw_HResult)(
-        /*[in]*/ long hresultToReturn)
+        /*[in]*/ int hresultToReturn)
     {
         return HRESULT{ hresultToReturn };
     }
-    long STDMETHODCALLTYPE Return_As_HResult(
-        /*[in]*/ long hresultToReturn)
+
+    int STDMETHODCALLTYPE Return_As_HResult(
+        /*[in]*/ int hresultToReturn)
     {
         return hresultToReturn;
     }
index 555a38d..1f4acf9 100644 (file)
@@ -36,17 +36,17 @@ public:
         return S_OK;
     }
     DEF_RAWFUNC(Add_Int)(
-        /*[in]*/ long a,
-        /*[in]*/ long b,
-        /*[out,retval]*/ long * pRetVal)
+        /*[in]*/ int a,
+        /*[in]*/ int b,
+        /*[out,retval]*/ int * pRetVal)
     {
         *pRetVal = a + b;
         return S_OK;
     }
     DEF_RAWFUNC(Add_UInt)(
-        /*[in]*/ unsigned long a,
-        /*[in]*/ unsigned long b,
-        /*[out,retval]*/ unsigned long * pRetVal)
+        /*[in]*/ unsigned int a,
+        /*[in]*/ unsigned int b,
+        /*[out,retval]*/ unsigned int * pRetVal)
     {
         *pRetVal = a + b;
         return S_OK;
@@ -114,9 +114,9 @@ public:
         return S_OK;
     }
     DEF_RAWFUNC(Add_Int_Ref)(
-        /*[in]*/ long a,
-        /*[in]*/ long b,
-        /*[in,out]*/ long * c)
+        /*[in]*/ int a,
+        /*[in]*/ int b,
+        /*[in,out]*/ int * c)
     {
         if (*c != std::numeric_limits<std::remove_reference<decltype(*c)>::type>::max())
             return E_UNEXPECTED;
@@ -124,9 +124,9 @@ public:
         return S_OK;
     }
     DEF_RAWFUNC(Add_UInt_Ref)(
-        /*[in]*/ unsigned long a,
-        /*[in]*/ unsigned long b,
-        /*[in,out]*/ unsigned long * c)
+        /*[in]*/ unsigned int a,
+        /*[in]*/ unsigned int b,
+        /*[in,out]*/ unsigned int * c)
     {
         if (*c != std::numeric_limits<std::remove_reference<decltype(*c)>::type>::max())
             return E_UNEXPECTED;
@@ -198,17 +198,17 @@ public:
         return S_OK;
     }
     DEF_RAWFUNC(Add_Int_Out)(
-        /*[in]*/ long a,
-        /*[in]*/ long b,
-        /*[out]*/ long * c)
+        /*[in]*/ int a,
+        /*[in]*/ int b,
+        /*[out]*/ int * c)
     {
         *c = a + b;
         return S_OK;
     }
     DEF_RAWFUNC(Add_UInt_Out)(
-        /*[in]*/ unsigned long a,
-        /*[in]*/ unsigned long b,
-        /*[out]*/ unsigned long * c)
+        /*[in]*/ unsigned int a,
+        /*[in]*/ unsigned int b,
+        /*[out]*/ unsigned int * c)
     {
         *c = a + b;
         return S_OK;
index bcfdbe2..4f279e0 100644 (file)
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <xplatform.h>
+#include <cassert>
 
 //#import "Server.Contract.tlb" no_namespace
 #include <Server.Contracts.tlh>
@@ -25,6 +26,29 @@ class DECLSPEC_UUID("71CF5C45-106C-4B32-B418-43A463C6041F") ErrorMarshalTesting;
 #define IID_IStringTesting __uuidof(IStringTesting)
 #define IID_IErrorMarshalTesting __uuidof(IErrorMarshalTesting)
 
+// Class used for COM activation when using CoreShim
+struct CoreShimComActivation
+{
+    CoreShimComActivation(_In_z_ const WCHAR *assemblyName, _In_z_ const WCHAR *typeName)
+    {
+        assert(assemblyName && typeName);
+        Set(assemblyName, typeName);
+    }
+
+    ~CoreShimComActivation()
+    {
+        Set(nullptr, nullptr);
+    }
+
+private:
+    void Set(_In_opt_z_ const WCHAR *assemblyName, _In_opt_z_ const WCHAR *typeName)
+    {
+        // See CoreShim.h for usage of environment variables
+        ::SetEnvironmentVariableW(W("CORESHIM_COMACT_ASSEMBLYNAME"), assemblyName);
+        ::SetEnvironmentVariableW(W("CORESHIM_COMACT_TYPENAME"), typeName);
+    }
+};
+
 #ifndef COM_CLIENT
     #include "ComHelpers.h"
 
index cc6a303..0714047 100644 (file)
@@ -51,25 +51,25 @@ namespace Server.Contract
     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
     public interface IArrayTesting
     {
-        double Mean_Byte_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] byte[] d);
-        double Mean_Short_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] short[] d);
-        double Mean_UShort_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] ushort[] d);
-        double Mean_Int_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] int[] d);
-        double Mean_UInt_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] uint[] d);
-        double Mean_Long_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] long[] d);
-        double Mean_ULong_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] ulong[] d);
-        double Mean_Float_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] float[] d);
-        double Mean_Double_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray)] double[] d);
-
-        double Mean_Byte_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] byte[] d, int len);
-        double Mean_Short_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] short[] d, int len);
-        double Mean_UShort_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] ushort[] d, int len);
-        double Mean_Int_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] int[] d, int len);
-        double Mean_UInt_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] uint[] d, int len);
-        double Mean_Long_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] long[] d, int len);
-        double Mean_ULong_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] ulong[] d, int len);
-        double Mean_Float_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] float[] d, int len);
-        double Mean_Double_LP_PostLen([MarshalAs(UnmanagedType.LPArray)] double[] d, int len);
+        double Mean_Byte_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] byte[] d);
+        double Mean_Short_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] short[] d);
+        double Mean_UShort_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] ushort[] d);
+        double Mean_Int_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] int[] d);
+        double Mean_UInt_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] uint[] d);
+        double Mean_Long_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] long[] d);
+        double Mean_ULong_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] ulong[] d);
+        double Mean_Float_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] float[] d);
+        double Mean_Double_LP_PreLen(int len, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] double[] d);
+
+        double Mean_Byte_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] byte[] d, int len);
+        double Mean_Short_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] short[] d, int len);
+        double Mean_UShort_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ushort[] d, int len);
+        double Mean_Int_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] int[] d, int len);
+        double Mean_UInt_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] uint[] d, int len);
+        double Mean_Long_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] long[] d, int len);
+        double Mean_ULong_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ulong[] d, int len);
+        double Mean_Float_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] float[] d, int len);
+        double Mean_Double_LP_PostLen([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] double[] d, int len);
 
         double Mean_Byte_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] byte[] d, out int len);
         double Mean_Short_SafeArray_OutLen([MarshalAs(UnmanagedType.SafeArray)] short[] d, out int len);
index 9bc826d..412d352 100644 (file)
@@ -49,12 +49,12 @@ INumericTesting : IUnknown
     unsigned short Add_UShort (
         unsigned short a,
         unsigned short b );
-    long Add_Int (
-        long a,
-        long b );
-    unsigned long Add_UInt (
-        unsigned long a,
-        unsigned long b );
+    int Add_Int (
+        int a,
+        int b );
+    unsigned int Add_UInt (
+        unsigned int a,
+        unsigned int b );
     __int64 Add_Long (
         __int64 a,
         __int64 b );
@@ -80,13 +80,13 @@ INumericTesting : IUnknown
         unsigned short b,
         unsigned short * c );
     HRESULT Add_Int_Ref (
-        long a,
-        long b,
-        long * c );
+        int a,
+        int b,
+        int * c );
     HRESULT Add_UInt_Ref (
-        unsigned long a,
-        unsigned long b,
-        unsigned long * c );
+        unsigned int a,
+        unsigned int b,
+        unsigned int * c );
     HRESULT Add_Long_Ref (
         __int64 a,
         __int64 b,
@@ -116,13 +116,13 @@ INumericTesting : IUnknown
         unsigned short b,
         unsigned short * c );
     HRESULT Add_Int_Out (
-        long a,
-        long b,
-        long * c );
+        int a,
+        int b,
+        int * c );
     HRESULT Add_UInt_Out (
-        unsigned long a,
-        unsigned long b,
-        unsigned long * c );
+        unsigned int a,
+        unsigned int b,
+        unsigned int * c );
     HRESULT Add_Long_Out (
         __int64 a,
         __int64 b,
@@ -157,13 +157,13 @@ INumericTesting : IUnknown
         /*[in]*/ unsigned short b,
         /*[out,retval]*/ unsigned short * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_Int (
-        /*[in]*/ long a,
-        /*[in]*/ long b,
-        /*[out,retval]*/ long * pRetVal ) = 0;
+        /*[in]*/ int a,
+        /*[in]*/ int b,
+        /*[out,retval]*/ int * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_UInt (
-        /*[in]*/ unsigned long a,
-        /*[in]*/ unsigned long b,
-        /*[out,retval]*/ unsigned long * pRetVal ) = 0;
+        /*[in]*/ unsigned int a,
+        /*[in]*/ unsigned int b,
+        /*[out,retval]*/ unsigned int * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_Long (
         /*[in]*/ __int64 a,
         /*[in]*/ __int64 b,
@@ -193,13 +193,13 @@ INumericTesting : IUnknown
         /*[in]*/ unsigned short b,
         /*[in,out]*/ unsigned short * c ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_Int_Ref (
-        /*[in]*/ long a,
-        /*[in]*/ long b,
-        /*[in,out]*/ long * c ) = 0;
+        /*[in]*/ int a,
+        /*[in]*/ int b,
+        /*[in,out]*/ int * c ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_UInt_Ref (
-        /*[in]*/ unsigned long a,
-        /*[in]*/ unsigned long b,
-        /*[in,out]*/ unsigned long * c ) = 0;
+        /*[in]*/ unsigned int a,
+        /*[in]*/ unsigned int b,
+        /*[in,out]*/ unsigned int * c ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_Long_Ref (
         /*[in]*/ __int64 a,
         /*[in]*/ __int64 b,
@@ -229,13 +229,13 @@ INumericTesting : IUnknown
         /*[in]*/ unsigned short b,
         /*[out]*/ unsigned short * c ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_Int_Out (
-        /*[in]*/ long a,
-        /*[in]*/ long b,
-        /*[out]*/ long * c ) = 0;
+        /*[in]*/ int a,
+        /*[in]*/ int b,
+        /*[out]*/ int * c ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_UInt_Out (
-        /*[in]*/ unsigned long a,
-        /*[in]*/ unsigned long b,
-        /*[out]*/ unsigned long * c ) = 0;
+        /*[in]*/ unsigned int a,
+        /*[in]*/ unsigned int b,
+        /*[out]*/ unsigned int * c ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Add_Long_Out (
         /*[in]*/ __int64 a,
         /*[in]*/ __int64 b,
@@ -262,198 +262,198 @@ IArrayTesting : IUnknown
     //
 
     double Mean_Byte_LP_PreLen (
-        long len,
+        int len,
         unsigned char * d );
     double Mean_Short_LP_PreLen (
-        long len,
+        int len,
         short * d );
     double Mean_UShort_LP_PreLen (
-        long len,
+        int len,
         unsigned short * d );
     double Mean_Int_LP_PreLen (
-        long len,
-        long * d );
+        int len,
+        int * d );
     double Mean_UInt_LP_PreLen (
-        long len,
-        unsigned long * d );
+        int len,
+        unsigned int * d );
     double Mean_Long_LP_PreLen (
-        long len,
+        int len,
         __int64 * d );
     double Mean_ULong_LP_PreLen (
-        long len,
+        int len,
         unsigned __int64 * d );
     double Mean_Float_LP_PreLen (
-        long len,
+        int len,
         float * d );
     double Mean_Double_LP_PreLen (
-        long len,
+        int len,
         double * d );
     double Mean_Byte_LP_PostLen (
         unsigned char * d,
-        long len );
+        int len );
     double Mean_Short_LP_PostLen (
         short * d,
-        long len );
+        int len );
     double Mean_UShort_LP_PostLen (
         unsigned short * d,
-        long len );
+        int len );
     double Mean_Int_LP_PostLen (
-        long * d,
-        long len );
+        int * d,
+        int len );
     double Mean_UInt_LP_PostLen (
-        unsigned long * d,
-        long len );
+        unsigned int * d,
+        int len );
     double Mean_Long_LP_PostLen (
         __int64 * d,
-        long len );
+        int len );
     double Mean_ULong_LP_PostLen (
         unsigned __int64 * d,
-        long len );
+        int len );
     double Mean_Float_LP_PostLen (
         float * d,
-        long len );
+        int len );
     double Mean_Double_LP_PostLen (
         double * d,
-        long len );
+        int len );
     double Mean_Byte_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
     double Mean_Short_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
     double Mean_UShort_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
     double Mean_Int_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
     double Mean_UInt_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
     double Mean_Long_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
     double Mean_ULong_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
     double Mean_Float_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
     double Mean_Double_SafeArray_OutLen (
         SAFEARRAY * d,
-        long * len );
+        int * len );
 
     //
     // Raw methods provided by interface
     //
 
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Byte_LP_PreLen (
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ unsigned char * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Short_LP_PreLen (
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ short * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_UShort_LP_PreLen (
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ unsigned short * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Int_LP_PreLen (
-        /*[in]*/ long len,
-        /*[in]*/ long * d,
+        /*[in]*/ int len,
+        /*[in]*/ int * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_UInt_LP_PreLen (
-        /*[in]*/ long len,
-        /*[in]*/ unsigned long * d,
+        /*[in]*/ int len,
+        /*[in]*/ unsigned int * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Long_LP_PreLen (
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ __int64 * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_ULong_LP_PreLen (
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ unsigned __int64 * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Float_LP_PreLen (
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ float * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Double_LP_PreLen (
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[in]*/ double * d,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Byte_LP_PostLen (
         /*[in]*/ unsigned char * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Short_LP_PostLen (
         /*[in]*/ short * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_UShort_LP_PostLen (
         /*[in]*/ unsigned short * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Int_LP_PostLen (
-        /*[in]*/ long * d,
-        /*[in]*/ long len,
+        /*[in]*/ int * d,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_UInt_LP_PostLen (
-        /*[in]*/ unsigned long * d,
-        /*[in]*/ long len,
+        /*[in]*/ unsigned int * d,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Long_LP_PostLen (
         /*[in]*/ __int64 * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_ULong_LP_PostLen (
         /*[in]*/ unsigned __int64 * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Float_LP_PostLen (
         /*[in]*/ float * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Double_LP_PostLen (
         /*[in]*/ double * d,
-        /*[in]*/ long len,
+        /*[in]*/ int len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Byte_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Short_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_UShort_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Int_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_UInt_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Long_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_ULong_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Float_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
       virtual HRESULT STDMETHODCALLTYPE raw_Mean_Double_SafeArray_OutLen (
         /*[in]*/ SAFEARRAY * d,
-        /*[out]*/ long * len,
+        /*[out]*/ int * len,
         /*[out,retval]*/ double * pRetVal ) = 0;
 };
 
@@ -635,16 +635,16 @@ IErrorMarshalTesting : IUnknown
     //
 
     HRESULT Throw_HResult (
-        long hresultToReturn );
+        int hresultToReturn );
 
     //
     // Raw methods provided by interface
     //
 
       virtual HRESULT STDMETHODCALLTYPE raw_Throw_HResult (
-        /*[in]*/ long hresultToReturn ) = 0;
-      virtual long STDMETHODCALLTYPE Return_As_HResult (
-        /*[in]*/ long hresultToReturn ) = 0;
+        /*[in]*/ int hresultToReturn ) = 0;
+      virtual int STDMETHODCALLTYPE Return_As_HResult (
+        /*[in]*/ int hresultToReturn ) = 0;
 };
 
 //
index a419c65..94046af 100644 (file)
@@ -27,15 +27,15 @@ inline unsigned short INumericTesting::Add_UShort ( unsigned short a, unsigned s
     return _result;
 }
 
-inline long INumericTesting::Add_Int ( long a, long b ) {
-    long _result = 0;
+inline int INumericTesting::Add_Int ( int a, int b ) {
+    int _result = 0;
     HRESULT _hr = raw_Add_Int(a, b, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline unsigned long INumericTesting::Add_UInt ( unsigned long a, unsigned long b ) {
-    unsigned long _result = 0;
+inline unsigned int INumericTesting::Add_UInt ( unsigned int a, unsigned int b ) {
+    unsigned int _result = 0;
     HRESULT _hr = raw_Add_UInt(a, b, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
@@ -87,13 +87,13 @@ inline HRESULT INumericTesting::Add_UShort_Ref ( unsigned short a, unsigned shor
     return _hr;
 }
 
-inline HRESULT INumericTesting::Add_Int_Ref ( long a, long b, long * c ) {
+inline HRESULT INumericTesting::Add_Int_Ref ( int a, int b, int * c ) {
     HRESULT _hr = raw_Add_Int_Ref(a, b, c);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _hr;
 }
 
-inline HRESULT INumericTesting::Add_UInt_Ref ( unsigned long a, unsigned long b, unsigned long * c ) {
+inline HRESULT INumericTesting::Add_UInt_Ref ( unsigned int a, unsigned int b, unsigned int * c ) {
     HRESULT _hr = raw_Add_UInt_Ref(a, b, c);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _hr;
@@ -141,13 +141,13 @@ inline HRESULT INumericTesting::Add_UShort_Out ( unsigned short a, unsigned shor
     return _hr;
 }
 
-inline HRESULT INumericTesting::Add_Int_Out ( long a, long b, long * c ) {
+inline HRESULT INumericTesting::Add_Int_Out ( int a, int b, int * c ) {
     HRESULT _hr = raw_Add_Int_Out(a, b, c);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _hr;
 }
 
-inline HRESULT INumericTesting::Add_UInt_Out ( unsigned long a, unsigned long b, unsigned long * c ) {
+inline HRESULT INumericTesting::Add_UInt_Out ( unsigned int a, unsigned int b, unsigned int * c ) {
     HRESULT _hr = raw_Add_UInt_Out(a, b, c);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _hr;
@@ -181,189 +181,189 @@ inline HRESULT INumericTesting::Add_Double_Out ( double a, double b, double * c
 // interface IArrayTesting wrapper method implementations
 //
 
-inline double IArrayTesting::Mean_Byte_LP_PreLen ( long len, unsigned char * d ) {
+inline double IArrayTesting::Mean_Byte_LP_PreLen ( int len, unsigned char * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Byte_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Short_LP_PreLen ( long len, short * d ) {
+inline double IArrayTesting::Mean_Short_LP_PreLen ( int len, short * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Short_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_UShort_LP_PreLen ( long len, unsigned short * d ) {
+inline double IArrayTesting::Mean_UShort_LP_PreLen ( int len, unsigned short * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_UShort_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Int_LP_PreLen ( long len, long * d ) {
+inline double IArrayTesting::Mean_Int_LP_PreLen ( int len, int * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Int_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_UInt_LP_PreLen ( long len, unsigned long * d ) {
+inline double IArrayTesting::Mean_UInt_LP_PreLen ( int len, unsigned int * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_UInt_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Long_LP_PreLen ( long len, __int64 * d ) {
+inline double IArrayTesting::Mean_Long_LP_PreLen ( int len, __int64 * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Long_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_ULong_LP_PreLen ( long len, unsigned __int64 * d ) {
+inline double IArrayTesting::Mean_ULong_LP_PreLen ( int len, unsigned __int64 * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_ULong_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Float_LP_PreLen ( long len, float * d ) {
+inline double IArrayTesting::Mean_Float_LP_PreLen ( int len, float * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Float_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Double_LP_PreLen ( long len, double * d ) {
+inline double IArrayTesting::Mean_Double_LP_PreLen ( int len, double * d ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Double_LP_PreLen(len, d, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Byte_LP_PostLen ( unsigned char * d, long len ) {
+inline double IArrayTesting::Mean_Byte_LP_PostLen ( unsigned char * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Byte_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Short_LP_PostLen ( short * d, long len ) {
+inline double IArrayTesting::Mean_Short_LP_PostLen ( short * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Short_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_UShort_LP_PostLen ( unsigned short * d, long len ) {
+inline double IArrayTesting::Mean_UShort_LP_PostLen ( unsigned short * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_UShort_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Int_LP_PostLen ( long * d, long len ) {
+inline double IArrayTesting::Mean_Int_LP_PostLen ( int * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Int_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_UInt_LP_PostLen ( unsigned long * d, long len ) {
+inline double IArrayTesting::Mean_UInt_LP_PostLen ( unsigned int * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_UInt_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Long_LP_PostLen ( __int64 * d, long len ) {
+inline double IArrayTesting::Mean_Long_LP_PostLen ( __int64 * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Long_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_ULong_LP_PostLen ( unsigned __int64 * d, long len ) {
+inline double IArrayTesting::Mean_ULong_LP_PostLen ( unsigned __int64 * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_ULong_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Float_LP_PostLen ( float * d, long len ) {
+inline double IArrayTesting::Mean_Float_LP_PostLen ( float * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Float_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Double_LP_PostLen ( double * d, long len ) {
+inline double IArrayTesting::Mean_Double_LP_PostLen ( double * d, int len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Double_LP_PostLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Byte_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_Byte_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Byte_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Short_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_Short_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Short_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_UShort_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_UShort_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_UShort_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Int_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_Int_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Int_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_UInt_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_UInt_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_UInt_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Long_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_Long_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Long_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_ULong_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_ULong_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_ULong_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Float_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_Float_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Float_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _result;
 }
 
-inline double IArrayTesting::Mean_Double_SafeArray_OutLen ( SAFEARRAY * d, long * len ) {
+inline double IArrayTesting::Mean_Double_SafeArray_OutLen ( SAFEARRAY * d, int * len ) {
     double _result = 0;
     HRESULT _hr = raw_Mean_Double_SafeArray_OutLen(d, len, &_result);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
@@ -564,7 +564,7 @@ inline HRESULT IStringTesting::Reverse_BStr_OutAttr ( _bstr_t a, _bstr_t b ) {
 // interface IErrorMarshalTesting wrapper method implementations
 //
 
-inline HRESULT IErrorMarshalTesting::Throw_HResult ( long hresultToReturn ) {
+inline HRESULT IErrorMarshalTesting::Throw_HResult ( int hresultToReturn ) {
     HRESULT _hr = raw_Throw_HResult(hresultToReturn);
     if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     return _hr;
index 50e37af..df05c15 100644 (file)
@@ -1,6 +1,7 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <!-- Properties for all Interop managed test assets -->
   <PropertyGroup>
+    <InteropCommonDir>$(MSBuildThisFileDirectory)common/</InteropCommonDir>
   </PropertyGroup>
 
   <!-- Environment properties -->
@@ -9,6 +10,6 @@
 
   <!-- Required source files -->
   <ItemGroup>
-    <Compile Include="$(MSBuildThisFileDirectory)\common\Assertion.cs"/>
+    <Compile Condition="'$(IgnoreInteropAssertionFile)' != 'true'" Include="$(InteropCommonDir)Assertion.cs"/>
   </ItemGroup>
 </Project>
diff --git a/tests/src/Interop/common/ExeLauncherProgram.cs b/tests/src/Interop/common/ExeLauncherProgram.cs
new file mode 100644 (file)
index 0000000..7f643f3
--- /dev/null
@@ -0,0 +1,69 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Reflection;
+
+/// <summary>
+/// This class is used for creating a test that has an entry point
+/// that is not the test itself. For example a test that starts from
+/// a native exe instead of a managed entry point.
+/// </summary>
+public class Program
+{
+    static int Main(string[] noArgs)
+    {
+        if (Environment.OSVersion.Platform != PlatformID.Win32NT)
+        {
+            Console.WriteLine($"Exe launcher only supported on Windows environments...");
+            return 100;
+        }
+
+        string workingDir = Environment.CurrentDirectory;
+        Console.WriteLine($"Searching for exe to launch in {workingDir}...");
+
+        Assembly thisAssem = Assembly.GetEntryAssembly();
+        string startExe = string.Empty;
+        foreach (string exeMaybe in Directory.EnumerateFiles(workingDir, "*.exe"))
+        {
+            // This entry point is _not_ an option
+            if (exeMaybe.Equals(thisAssem.Location, StringComparison.OrdinalIgnoreCase))
+            {
+                continue;
+            }
+
+            startExe = exeMaybe;
+            break;
+        }
+
+        if (string.IsNullOrEmpty(startExe))
+        {
+            throw new Exception("Unable to find start EXE");
+        }
+
+        var startInfo = new ProcessStartInfo()
+        {
+            FileName = startExe,
+
+            UseShellExecute = false,
+            RedirectStandardOutput = true,
+            RedirectStandardError = true
+        };
+
+        Console.WriteLine($"Launching '{startExe}'...");
+        using (Process p = Process.Start(startInfo))
+        {
+            p.OutputDataReceived += (_, args) => Console.WriteLine(args.Data);
+            p.BeginOutputReadLine();
+
+            p.ErrorDataReceived += (_, args) => Console.Error.WriteLine(args.Data);
+            p.BeginErrorReadLine();
+
+            p.WaitForExit();
+            return p.ExitCode;
+        }
+    }
+}
\ No newline at end of file
index e0b939e..94a693e 100644 (file)
@@ -36,6 +36,7 @@
 
 //  include 
 #ifdef _WIN32
+    #define NOMINMAX
     #include <windows.h>
     #include <combaseapi.h>
 
@@ -95,6 +96,28 @@ inline void CoreClrFree(void *p)
 #endif
 }
 
+inline void *CoreClrBstrAlloc(size_t cb)
+{
+#ifdef _WIN32
+    // A null is automatically applied in the SysAllocStringByteLen API.
+    // Remove a single OLECHAR for the implied null.
+    // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/api/oleauto/nf-oleauto-sysallocstringbytelen
+    if (cb >= sizeof(OLECHAR))
+        cb -= sizeof(OLECHAR);
+
+    return ::SysAllocStringByteLen(nullptr, static_cast<UINT>(cb));
+#else
+    return nullptr;
+#endif
+}
+
+inline void CoreClrBstrFree(void *p)
+{
+#ifdef _WIN32
+    return ::SysFreeString((BSTR)p);
+#endif
+}
+
 // redirected types not-windows only
 #ifndef  _WIN32