Add PInvoke/Decimal tests (#19301)
authorZeng Jiang <v-jiazen@microsoft.com>
Sat, 17 Nov 2018 00:25:43 +0000 (08:25 +0800)
committerJeremy Koritzinsky <jkoritzinsky@gmail.com>
Sat, 17 Nov 2018 00:25:43 +0000 (16:25 -0800)
* Add PInvoke/Decimal Tests

* Remove tests that fail on both .NET Framework and .NET Core.

* Refactor away the Windows-only headers.

* Make Decimal tests build on Linux.

* Remove some dead code.

* Clean up formatting.

* Don't run Decimal <--> Currency tests off-Windows.

* Make calling conventions and parameter/return marshalling more explicit

tests/src/Interop/CMakeLists.txt
tests/src/Interop/PInvoke/Decimal/PInvoke/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/Decimal/PInvoke/DecNative.cpp [new file with mode: 0644]
tests/src/Interop/PInvoke/Decimal/PInvoke/DecimalTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/Decimal/PInvoke/DecimalTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/Decimal/ReversePInvoke/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/Decimal/ReversePInvoke/DecimalTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/Decimal/ReversePInvoke/DecimalTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/Decimal/ReversePInvoke/RevNative.cpp [new file with mode: 0644]
tests/src/Interop/common/types.h
tests/src/Interop/common/xplatform.h

index 901bd9e..bfebf73 100644 (file)
@@ -12,6 +12,8 @@ list(APPEND LINK_LIBRARIES_ADDITIONAL platformdefines)
 SET(CLR_INTEROP_TEST_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
 
 include_directories(common)
+add_subdirectory(PInvoke/Decimal/PInvoke)
+add_subdirectory(PInvoke/Decimal/ReversePInvoke)
 add_subdirectory(PInvoke/ArrayWithOffset)
 add_subdirectory(PInvoke/DllImportPath)
 add_subdirectory(PInvoke/BestFitMapping/Char)
diff --git a/tests/src/Interop/PInvoke/Decimal/PInvoke/CMakeLists.txt b/tests/src/Interop/PInvoke/Decimal/PInvoke/CMakeLists.txt
new file mode 100644 (file)
index 0000000..edb87ee
--- /dev/null
@@ -0,0 +1,14 @@
+#VCXPROJ 
+cmake_minimum_required (VERSION 2.6) 
+project (DecNative) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") 
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    DecNative.cpp 
+) 
+# Additional files to reference: 
+# add the executable 
+add_library (DecNative SHARED ${SOURCES}) 
+target_link_libraries(DecNative ${LINK_LIBRARIES_ADDITIONAL}) 
+# add the install targets 
+install (TARGETS DecNative DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/Decimal/PInvoke/DecNative.cpp b/tests/src/Interop/PInvoke/Decimal/PInvoke/DecNative.cpp
new file mode 100644 (file)
index 0000000..ee236d1
--- /dev/null
@@ -0,0 +1,178 @@
+// 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 <iostream>
+#include <xplatform.h>
+#include "platformdefines.h"
+
+DECIMAL g_DECIMAL_MaxValue = { 0, {{ 0, 0 }}, 0xffffffff, {{0xffffffff, 0xffffffff}} };
+DECIMAL g_DECIMAL_MinValue  = { 0, {{ 0, DECIMAL_NEG }}, 0xffffffff, {{0xffffffff, 0xffffffff}} };
+DECIMAL g_DECIMAL_Zero = { 0 };
+
+CY g_CY_MaxValue = { { 0xffffffff, 0x7fffffff } };
+CY g_CY_MinValue = { { (LONG)0x00000000, (LONG)0x80000000 } };
+CY g_CY_Zero = { { 0 } };
+
+typedef struct _Stru_Exp_DecAsCYAsFld
+{
+    WCHAR wc;
+    CY cy;
+} Stru_Exp_DecAsCYAsFld;
+
+typedef struct _Stru_Seq_DecAsLPStructAsFld
+{
+    DOUBLE dblVal;
+    WCHAR cVal;
+    DECIMAL* lpDec;
+} Stru_Seq_DecAsLPStructAsFld;
+
+void DecDisplay(const DECIMAL& dec)
+{
+    std::cout << "\twReserved" << "\t\t" << dec.wReserved << "\n"
+        << "\tscale" << "\t\t" << dec.scale << "\n"
+        << "\tsign" << "\t\t" << dec.sign << "\n"
+        << "\tsignscale" << "\t\t" << dec.signscale << "\n"
+        << "\tHi32" << "\t\t" << dec.Hi32 << "\n"
+        << "\tLo32" << "\t\t" << dec.Lo32 << "\n"
+        << "\tMid32" << "\t\t" << dec.Mid32 << "\n"
+        << "\tLo64" << "\t\t" << dec.Lo64 << std::endl;
+}
+
+template<typename T>
+bool operator==(const T& t1, const T& t2)
+{
+    return 0 == memcmp((void*)&t1, (void*)&t2, sizeof(T));
+}
+
+template<typename T>
+bool Equals(LPCSTR err_id, T expected, T actual)
+{
+    if(expected == actual)
+        return true;
+    else
+    {
+        std::cout << "\t#Native Side Err# -- " << err_id 
+            << "\n\tExpected = " << expected << std::endl;
+        std::wcout << "\tActual is = " << actual << std::endl;
+
+        return false;
+    }
+}
+
+bool DecEqualsToExpected(LPCSTR err_id, const DECIMAL& expected, const DECIMAL& actual)
+{
+    if(expected == actual)
+        return true;
+    else
+    {
+        std::cout << "\t#Native Side Err # -- " << err_id 
+            << "DECIMAL Expected is :" << std::endl;
+        DecDisplay(expected);
+
+        std::cout << "\t" << "____________________________________" << std::endl;
+
+        std::cout << "\tDECIMAL Actual is :" << std::endl;
+        DecDisplay(actual);
+
+        return false;
+    }
+}
+
+bool CYEqualsToExpected(LPCSTR err_id, const CY& expected, const CY& actual)
+{
+    if(expected == actual)
+        return true;
+    else
+    {
+        std::cout << "\t#Native Side Err# -- " << err_id 
+            << "\n\tCY Expected is :" << "Hi = " << expected.Hi
+            << "Lo = " << expected.Lo << std::endl;
+
+        std::cout << "\tCY Actual is :" << "Hi = " << actual.Hi
+            << "Lo = " << actual.Lo << std::endl;
+
+        return false;
+    }
+}
+
+// DECIMAL
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE TakeDecAsInOutParamAsLPStructByRef(DECIMAL** lppDec)
+{
+    if(DecEqualsToExpected("001.01", g_DECIMAL_MaxValue, **lppDec))
+    {
+        **lppDec = g_DECIMAL_MinValue;
+        return true;
+    }
+    else
+        return false;
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE TakeDecAsOutParamAsLPStructByRef(DECIMAL** lppDec)
+{
+    if(*lppDec)
+    {
+        std::cout << "\t#Native Side Err# -- 001.02 DECIMAL* is not NULL" << std::endl;
+        return false;
+    }
+    else
+    {
+        *lppDec = (DECIMAL*)CoreClrAlloc(sizeof(DECIMAL));
+        **lppDec = g_DECIMAL_MinValue;
+
+        return true;
+    }
+}
+
+extern "C" DLL_EXPORT DECIMAL STDMETHODCALLTYPE RetDec()
+{
+    return g_DECIMAL_MaxValue;
+}
+
+// CY
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE TakeCYAsInOutParamAsLPStructByRef(CY* lpCy)
+{
+    if(CYEqualsToExpected("002.01", g_CY_MaxValue, *lpCy))
+    {
+        *lpCy = g_CY_MinValue;
+        return true;
+    }
+    else
+        return false;
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE TakeCYAsOutParamAsLPStructByRef(CY* lpCy)
+{
+    if(g_CY_Zero == *lpCy) 
+    {        
+        *lpCy = g_CY_MinValue;
+
+        return true;
+    }
+    else
+    {
+        std::cout << "\t#Native Side Err# -- 002.02 CY is not clear up." << std::endl;
+        return false;
+    }
+}
+
+extern "C" DLL_EXPORT CY STDMETHODCALLTYPE RetCY()
+{
+    return g_CY_MinValue;
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE TakeStru_Exp_DecAsCYAsFldByInOutRef(Stru_Exp_DecAsCYAsFld* s)
+{
+    if(CYEqualsToExpected("001.04.01", g_CY_Zero, s->cy) && Equals("001.04.02", W('\0'), s->wc))
+    {
+        s->cy = g_CY_MaxValue;
+        s->wc = W('C');
+
+        return true;
+    }
+    else
+    {
+        std::cout << "\t#Native Side Err# -- 002.02 Stru_Exp_DecAsCYAsFld is not clear up." << std::endl;
+        return false;
+    }
+}
diff --git a/tests/src/Interop/PInvoke/Decimal/PInvoke/DecimalTest.cs b/tests/src/Interop/PInvoke/Decimal/PInvoke/DecimalTest.cs
new file mode 100644 (file)
index 0000000..eeed3d6
--- /dev/null
@@ -0,0 +1,188 @@
+// 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
+#region Struct Def
+[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
+public struct Stru_Exp_DecAsCYAsFld
+{
+    [FieldOffset(0)]
+    public char wc;
+
+    [FieldOffset(8)]
+    [MarshalAs(UnmanagedType.Currency)]
+    public decimal cy;
+}
+
+[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+public struct Stru_Seq_DecAsLPStructAsFld
+{
+    public double dblVal;
+
+    public char cVal;
+
+    [MarshalAs(UnmanagedType.LPStruct)]
+    public decimal dec;
+}
+#endregion
+
+public class CMain
+{
+    //DECIMAL
+    [DllImport("DecNative")]
+    static extern bool TakeDecAsInOutParamAsLPStructByRef([MarshalAs(UnmanagedType.LPStruct), In, Out] ref decimal dec);
+    [DllImport("DecNative")]
+    static extern bool TakeDecAsOutParamAsLPStructByRef([MarshalAs(UnmanagedType.LPStruct), Out] out decimal dec);
+    [DllImport("DecNative")]
+    [return: MarshalAs(UnmanagedType.LPStruct)]
+    static extern decimal RetDec();
+
+    //CY
+    [DllImport("DecNative")]
+    static extern bool TakeCYAsInOutParamAsLPStructByRef([MarshalAs(UnmanagedType.Currency), In, Out] ref decimal cy);
+    [DllImport("DecNative")]
+    static extern bool TakeCYAsOutParamAsLPStructByRef([MarshalAs(UnmanagedType.Currency), Out] out decimal cy);
+    [DllImport("DecNative")]
+    [return: MarshalAs(UnmanagedType.Currency)]
+    static extern decimal RetCY();
+    [DllImport("DecNative")]
+    static extern bool TakeStru_Exp_DecAsCYAsFldByInOutRef([Out] out Stru_Exp_DecAsCYAsFld s);
+
+    static int fails = 0;
+    static decimal CY_MAX_VALUE = 922337203685477.5807M;
+    static decimal CY_MIN_VALUE = -922337203685477.5808M;
+
+    static bool MarshalAsLPStruct()
+    {
+        Console.WriteLine("MarshalAsLPStruct started.");
+        // DECIMAL
+        decimal dec = decimal.MaxValue;
+        if (!TakeDecAsInOutParamAsLPStructByRef(ref dec))
+        {
+            Console.WriteLine("Test Failed: TakeDecAsInOutParamAsLPStructByRef : Returned false");
+            return false;
+        }
+        if (decimal.MinValue != dec)
+        {
+            Console.WriteLine($"Test Failed: Expected 'decimal.MinValue'. Got {dec}.");
+            return false;
+        }
+
+        dec = decimal.Zero;
+        if (!TakeDecAsOutParamAsLPStructByRef(out dec))
+        {
+            Console.WriteLine("Test Failed: TakeDecAsOutParamAsLPStructByRef : Returned false");
+            return false;
+        }
+        if (decimal.MinValue != dec)
+        {
+            Console.WriteLine($"Test Failed: Expected 'decimal.MinValue'. Got {dec}.");
+            return false;
+        }
+
+        bool exceptionThrown = false;
+        try
+        {
+            RetDec();
+        }
+        catch (MarshalDirectiveException)
+        {
+            exceptionThrown = true;
+        }
+
+        if (!exceptionThrown)
+        {
+            Console.WriteLine("Expected MarshalDirectiveException is not thrown");
+            return false;
+        }
+
+        Console.WriteLine("MarshalAsLPStruct end.");
+        return true;
+    }
+
+    static bool MarshalAsCurrencyScenario()
+    {
+        Console.WriteLine("MarshalAsCurrencyScenario started.");
+        //CY
+        decimal cy = CY_MAX_VALUE;
+        if (!TakeCYAsInOutParamAsLPStructByRef(ref cy))
+        { 
+            Console.WriteLine("Test Failed: TakeCYAsInOutParamAsLPStructByRef : Returned false");
+            return false;
+        }
+        if (CY_MIN_VALUE != cy)
+        { 
+            Console.WriteLine($"Test Failed: Expected 'CY_MIN_VALUE'. Got {cy}.");
+            return false;
+        }
+
+        cy = decimal.MaxValue;
+        if (!TakeCYAsOutParamAsLPStructByRef(out cy))
+        { 
+            Console.WriteLine("Test Failed: TakeCYAsOutParamAsLPStructByRef : Returned false");
+            return false;
+        }
+        if (CY_MIN_VALUE != cy)
+        { 
+            Console.WriteLine($"Test Failed: Expected 'CY_MIN_VALUE'. Got {cy}.");
+            return false;
+        }
+
+
+        bool exceptionThrown = false;
+        try
+        {
+            RetCY();
+        }
+        catch (MarshalDirectiveException)
+        {
+            exceptionThrown = true;
+        }
+
+        if (!exceptionThrown)
+        {
+            Console.WriteLine("Expected MarshalDirectiveException is not thrown");
+            return false;
+        }
+
+        Stru_Exp_DecAsCYAsFld s = new Stru_Exp_DecAsCYAsFld();
+        s.cy = CY_MAX_VALUE;
+        s.wc = 'I';
+        if (!TakeStru_Exp_DecAsCYAsFldByInOutRef(out s))
+        {
+            Console.WriteLine("Test Failed: TakeStru_Exp_DecAsCYAsFldByInOutRef : Returned false");
+            return false;
+        }
+        if (!TakeStru_Exp_DecAsCYAsFldByInOutRef(out s))
+            if (CY_MAX_VALUE != s.cy)
+            {
+                Console.WriteLine($"Test Failed: Expected 'CY_MAX_VALUE'. Got {s.cy}.");
+                return false;
+            }
+        if ('C' != s.wc)
+        {
+            Console.WriteLine($"Test Failed: Expected 'C'. Got {s.wc}.");
+            return false;
+        }
+
+        Console.WriteLine("MarshalAsCurrencyScenario end.");
+        return true;
+    }
+
+    static int Main()
+    {
+        bool success = true;
+        success = success && MarshalAsLPStruct();
+        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+        {
+            success = success && MarshalAsCurrencyScenario();
+        }
+
+        return success ? 100 : 101;
+    }
+}
+#pragma warning restore 618
diff --git a/tests/src/Interop/PInvoke/Decimal/PInvoke/DecimalTest.csproj b/tests/src/Interop/PInvoke/Decimal/PInvoke/DecimalTest.csproj
new file mode 100644 (file)
index 0000000..be7a167
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>DecimalTest</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="DecimalTest.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/PInvoke/Decimal/ReversePInvoke/CMakeLists.txt b/tests/src/Interop/PInvoke/Decimal/ReversePInvoke/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a644cb4
--- /dev/null
@@ -0,0 +1,13 @@
+#VCXPROJ 
+cmake_minimum_required (VERSION 2.6) 
+project (RevNative) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") 
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    RevNative.cpp 
+) 
+# add the executable 
+add_library (RevNative SHARED ${SOURCES}) 
+target_link_libraries(RevNative ${LINK_LIBRARIES_ADDITIONAL}) 
+# add the install targets 
+install (TARGETS RevNative DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/Decimal/ReversePInvoke/DecimalTest.cs b/tests/src/Interop/PInvoke/Decimal/ReversePInvoke/DecimalTest.cs
new file mode 100644 (file)
index 0000000..cc2f2f8
--- /dev/null
@@ -0,0 +1,339 @@
+// 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
+[StructLayout(LayoutKind.Sequential)]
+public struct Stru_Seq_DecAsStructAsFld
+{
+    public int number;
+
+    [MarshalAs(UnmanagedType.Struct)]
+    public decimal dec;
+}
+
+[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
+public struct Stru_Exp_DecAsCYAsFld
+{
+    [FieldOffset(0)]
+    public char wc;
+
+    [FieldOffset(8)]
+    [MarshalAs(UnmanagedType.Currency)]
+    public decimal dec;
+}
+
+public class CMain
+{
+    #region Func Sig   
+
+    // Dec As Struct
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_TakeDecByInOutRef([MarshalAs(UnmanagedType.FunctionPtr)] Dele_DecInOutRef dele);
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_TakeDecByOutRef([MarshalAs(UnmanagedType.FunctionPtr)] Dele_DecOutRef dele);
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_DecRet([MarshalAs(UnmanagedType.FunctionPtr)] Dele_DecRet dele);
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_TakeStru_Seq_DecAsStructAsFldByInOutRef([MarshalAs(UnmanagedType.FunctionPtr)] Dele_Stru_Seq_DecAsStructAsFldInOutRef dele);
+
+    // Dec As CY
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_TakeCYByInOutRef([MarshalAs(UnmanagedType.FunctionPtr)] Dele_CYInOutRef dele);
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_TakeCYByOutRef([MarshalAs(UnmanagedType.FunctionPtr)] Dele_CYOutRef dele);
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_CYRet([MarshalAs(UnmanagedType.FunctionPtr)] Dele_CYRet dele);
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_TakeStru_Exp_DecAsCYAsFldByOutRef([MarshalAs(UnmanagedType.FunctionPtr)] Dele_Stru_Exp_DecAsCYAsFldOutRef dele);
+
+    // Dec As LPStruct
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_TakeDecByInOutRefAsLPStruct([MarshalAs(UnmanagedType.FunctionPtr)] Dele_DecInOutRefAsLPStruct dele);
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_TakeDecByOutRefAsLPStruct([MarshalAs(UnmanagedType.FunctionPtr)] Dele_DecOutRefAsLPStruct dele);
+    //************** ReverseCall Return Int From Net **************//
+    [DllImport("RevNative")]
+    static extern bool ReverseCall_IntRet([MarshalAs(UnmanagedType.FunctionPtr)] Dele_IntRet dele);
+
+    #endregion
+
+    #region Delegate Set
+
+    // Dec As Struct
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate bool Dele_DecInOutRef([MarshalAs(UnmanagedType.Struct), In, Out]ref decimal dec);
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate bool Dele_DecOutRef([MarshalAs(UnmanagedType.Struct), Out]out decimal dec);
+    [return: MarshalAs(UnmanagedType.Struct)]
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate decimal Dele_DecRet();
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate bool Dele_Stru_Seq_DecAsStructAsFldInOutRef([In, Out]ref Stru_Seq_DecAsStructAsFld s);
+
+    // Dec As CY
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate bool Dele_CYInOutRef([MarshalAs(UnmanagedType.Currency), In, Out]ref decimal dec);
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate bool Dele_CYOutRef([MarshalAs(UnmanagedType.Currency), Out]out decimal dec);
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    [return: MarshalAs(UnmanagedType.Currency)]
+    delegate decimal Dele_CYRet();
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate bool Dele_Stru_Exp_DecAsCYAsFldOutRef([Out]out Stru_Exp_DecAsCYAsFld s);
+
+    // Dec As LPStruct
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate bool Dele_DecInOutRefAsLPStruct([MarshalAs(UnmanagedType.LPStruct), In, Out]ref decimal dec);
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate bool Dele_DecOutRefAsLPStruct([MarshalAs(UnmanagedType.LPStruct), Out]out decimal dec);
+
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    [return: MarshalAs(UnmanagedType.LPStruct)]
+    delegate decimal Dele_DecAsLPStructRet();
+
+    //************** ReverseCall Return Int From Net **************//
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    delegate int Dele_IntRet();
+
+    #endregion
+
+    #region AUX For Testing
+    const decimal CY_MAX_VALUE = 922337203685477.5807M;
+    const decimal CY_MIN_VALUE = -922337203685477.5808M;
+
+    static bool Equals<T>(T expected, T actual)
+    {
+        if (expected.Equals(actual))
+            return true;
+        else
+            return false;
+    }
+
+    #endregion
+
+    #region Method For Testing
+
+    //Dec As Struct
+    static bool TakeDecByInOutRef([MarshalAs(UnmanagedType.Struct), In, Out] ref decimal dec)
+    {
+        if (Equals(decimal.MaxValue, dec))
+        {
+            dec = decimal.MinValue;
+            return true;
+        }
+        else
+            return false;
+    }
+
+    static bool TakeDecByOutRef([MarshalAs(UnmanagedType.Struct), Out] out decimal dec)
+    {
+        dec = decimal.Zero;
+
+        return true;
+    }
+
+    [return: MarshalAs(UnmanagedType.Struct)]
+    static decimal DecRet()
+    {
+        return decimal.MinValue;
+    }
+
+    static bool TakeStru_Seq_DecAsStructAsFldByInOutRef([In, Out] ref Stru_Seq_DecAsStructAsFld s)
+    {
+        if (Equals(decimal.MaxValue, s.dec) && Equals(1, s.number))
+        {
+            s.dec = decimal.MinValue;
+            s.number = 2;
+            return true;
+        }
+        else
+            return false;
+    }
+
+    // Dec As CY
+    static bool TakeCYByInOutRef([MarshalAs(UnmanagedType.Currency), In, Out] ref decimal cy)
+    {
+        if (Equals(CY_MAX_VALUE, cy))
+        {
+            cy = CY_MIN_VALUE;
+            return true;
+        }
+        else
+            return false;
+    }
+
+    [return: MarshalAs(UnmanagedType.Currency)]
+    static decimal CYRet()
+    {
+        return CY_MIN_VALUE;
+    }
+
+    static bool TakeCYByOutRef([MarshalAs(UnmanagedType.Currency), Out] out decimal dec)
+    {
+        dec = decimal.Zero;
+
+        return true;
+    }
+
+    static bool ReverseCall_TakeStru_Exp_DecAsCYAsFldByOutRef([Out] out Stru_Exp_DecAsCYAsFld s)
+    {
+        s.dec = CY_MAX_VALUE;
+        s.wc = 'C';
+
+        return true;
+    }
+
+    //Dec As LPStruct
+    static bool TakeDecByInOutRefAsLPStruct([MarshalAs(UnmanagedType.LPStruct), In, Out] ref decimal dec)
+    {
+        if (Equals(decimal.MaxValue, dec))
+        {
+            dec = decimal.MinValue;
+            return true;
+        }
+        else
+            return false;
+    }
+
+    static bool TakeDecByOutRefAsLPStruct([MarshalAs(UnmanagedType.LPStruct), Out] out decimal dec)
+    {
+        dec = decimal.Zero;
+
+        return true;
+    }
+
+    [return: MarshalAs(UnmanagedType.LPStruct)]
+    static decimal DecAsLPStructRet()
+    {
+        return decimal.MinValue;
+    }
+
+    //************** ReverseCall Return Int From Net **************//
+    [return: MarshalAs(UnmanagedType.I4)]
+    static int IntRet()
+    {
+        return 0x12345678;
+    }
+
+    #endregion
+
+    static bool AsStruct()
+    {
+        Console.WriteLine("AsStruct started.");
+        // Dec As Struct
+        if (!ReverseCall_TakeDecByInOutRef(new Dele_DecInOutRef(TakeDecByInOutRef)))
+        {
+            Console.WriteLine("Test Failed: Decimal <-> DECIMAL, Marshal As Struct/Param, Passed By In / Out / Ref .");
+            return false;
+        }
+        if (!ReverseCall_TakeDecByOutRef(new Dele_DecOutRef(TakeDecByOutRef)))
+        {
+            Console.WriteLine("Test Failed: Decimal <-> DECIMAL, Marshal As Struct/Param, Passed By Out / Ref .");
+            return false;
+        }
+        if (!ReverseCall_DecRet(new Dele_DecRet(DecRet)))
+        {
+            Console.WriteLine("Test Failed: Decimal <-> DECIMAL, Marshal As Struct/RetVal .");
+            return false;
+        }
+        if (!ReverseCall_TakeStru_Seq_DecAsStructAsFldByInOutRef(new Dele_Stru_Seq_DecAsStructAsFldInOutRef(TakeStru_Seq_DecAsStructAsFldByInOutRef)))
+        {
+            Console.WriteLine("Test Failed: Decimal <-> DECIMAL, Marshal As Struct/Field, Passed By In / Out / Ref .");
+            return false;
+        }
+
+        Console.WriteLine("AsStruct end.");
+        return true;
+    }
+
+    static bool AsCY()
+    {
+        Console.WriteLine("AsCY started.");
+        // Dec As CY
+        if (!(ReverseCall_TakeCYByInOutRef(new Dele_CYInOutRef(TakeCYByInOutRef))))
+        {
+            Console.WriteLine("Test Failed: (ReverseCall_TakeCYByInOutRef(new Dele_CYInOutRef(TakeCYByInOutRef)))");
+            return false;
+        }
+        if (!(ReverseCall_TakeCYByOutRef(new Dele_CYOutRef(TakeCYByOutRef))))
+        {
+            Console.WriteLine("Test Failed: (ReverseCall_TakeCYByOutRef(new Dele_CYOutRef(TakeCYByOutRef)))");
+            return false;
+        }
+
+        bool exceptionThrown = false;
+
+        try
+        {
+            ReverseCall_CYRet(new Dele_CYRet(CYRet));
+        }
+        catch (MarshalDirectiveException)
+        {
+            exceptionThrown = true;
+        }
+        if (!exceptionThrown)
+        {
+            Console.WriteLine("Expected MarshalDirectiveException from TakeDecAsInOutParamAsLPStructByRef(ref dec) not thrown");
+            return false;
+        }
+
+        if (!(ReverseCall_TakeStru_Exp_DecAsCYAsFldByOutRef(new Dele_Stru_Exp_DecAsCYAsFldOutRef(ReverseCall_TakeStru_Exp_DecAsCYAsFldByOutRef))))
+        {
+            Console.WriteLine("Test Failed: (ReverseCall_TakeStru_Exp_DecAsCYAsFldByOutRef(new Dele_Stru_Exp_DecAsCYAsFldOutRef(ReverseCall_TakeStru_Exp_DecAsCYAsFldByOutRef)))");
+            return false;
+        }
+
+        Console.WriteLine("AsCY end.");
+        return true;
+    }
+
+    static bool AsLPStruct()
+    {
+        Console.WriteLine("AsLPStruct started.");
+
+        if (!ReverseCall_TakeDecByInOutRefAsLPStruct(new Dele_DecInOutRefAsLPStruct(TakeDecByInOutRefAsLPStruct)))
+        {
+            Console.WriteLine("Test Failed: Decimal <-> DECIMAL, Marshal As LPStruct/Param, Passed By In / Out / Ref");
+            return false;
+        }
+        if (!ReverseCall_TakeDecByOutRefAsLPStruct(new Dele_DecOutRefAsLPStruct(TakeDecByOutRefAsLPStruct)))
+        {
+            Console.WriteLine("Test Failed: Decimal <-> DECIMAL, Marshal As LPStruct/Param, Passed By Out / Ref");
+            return false;
+        }
+
+
+        Console.WriteLine("AsLPStruct end.");
+        return true;
+    }
+
+    static bool AsInt()
+    {
+        if (!ReverseCall_IntRet(new Dele_IntRet(IntRet)))
+        {
+            Console.WriteLine("Test Failed: RET INT");
+            return false;
+        }
+
+        return true;
+    }
+
+    static int Main()
+    {
+        bool success = true;
+        success = success && AsStruct();
+        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+        {
+            success = success && AsCY();
+        }
+        success = success && AsLPStruct();
+        success = success && AsInt();
+
+        return success ? 100 : 101;
+    }
+}
+#pragma warning restore 618
diff --git a/tests/src/Interop/PInvoke/Decimal/ReversePInvoke/DecimalTest.csproj b/tests/src/Interop/PInvoke/Decimal/ReversePInvoke/DecimalTest.csproj
new file mode 100644 (file)
index 0000000..be7a167
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>DecimalTest</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="DecimalTest.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/PInvoke/Decimal/ReversePInvoke/RevNative.cpp b/tests/src/Interop/PInvoke/Decimal/ReversePInvoke/RevNative.cpp
new file mode 100644 (file)
index 0000000..6f716e8
--- /dev/null
@@ -0,0 +1,243 @@
+// 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 <iostream>
+#include <xplatform.h>
+#include "platformdefines.h"
+
+DECIMAL g_DECIMAL_MaxValue = { 0, {{ 0, 0 }}, 0xffffffff, {{0xffffffff, 0xffffffff}} };
+DECIMAL g_DECIMAL_MinValue  = { 0, {{ 0, DECIMAL_NEG }}, 0xffffffff, {{0xffffffff, 0xffffffff }}};
+DECIMAL g_DECIMAL_Zero = { 0 };
+
+CY g_CY_MaxValue = { {0xffffffff, 0x7fffffff} };
+CY g_CY_MinValue = { {(LONG)0x00000000, (LONG)0x80000000} };
+CY g_CY_Zero = { {0} };
+
+typedef struct _Stru_Seq_DecAsStructAsFld
+{
+    int number;
+    DECIMAL dec;
+} Stru_Seq_DecAsStructAsFld;
+
+typedef struct _Stru_Exp_DecAsCYAsFld
+{
+    WCHAR wc;
+    CY cy;
+} Stru_Exp_DecAsCYAsFld;
+
+typedef struct _Stru_Seq_DecAsLPStructAsFld
+{
+    DOUBLE dblVal;
+    CHAR cVal;
+    DECIMAL* dec;
+} Stru_Seq_DecAsLPStructAsFld;
+
+// As Struct
+typedef BOOL (STDMETHODCALLTYPE *Fp_Dec)(DECIMAL*);
+typedef DECIMAL (STDMETHODCALLTYPE *Fp_RetDec)();
+typedef BOOL (STDMETHODCALLTYPE *Fp_Stru_Seq_DecAsStructAsFld)(Stru_Seq_DecAsStructAsFld*);
+// As CY
+typedef BOOL (STDMETHODCALLTYPE *Fp_CY)(CY*);
+typedef CY (STDMETHODCALLTYPE *Fp_RetCY)();
+typedef BOOL (STDMETHODCALLTYPE *Fp_Stru_Exp_DecAsCYAsFld)(Stru_Exp_DecAsCYAsFld*);
+// As LPStruct
+typedef BOOL (STDMETHODCALLTYPE *Fp_DecAsLPStruct)(DECIMAL**);
+typedef DECIMAL* (STDMETHODCALLTYPE *Fp_RetDecAsLPStruct)();
+typedef BOOL (STDMETHODCALLTYPE *Fp_Stru_Seq_DecAsLPStructAsFld)(Stru_Seq_DecAsLPStructAsFld*);
+
+void DecDisplay(const DECIMAL& dec)
+{
+    std::cout << "\twReserved" << "\t\t" << dec.wReserved << "\n"
+        << "\tscale" << "\t\t" << dec.scale << "\n"
+        << "\tsign" << "\t\t" << dec.sign << "\n"
+        << "\tsignscale" << "\t\t" << dec.signscale << "\n"
+        << "\tHi32" << "\t\t" << dec.Hi32 << "\n"
+        << "\tLo32" << "\t\t" << dec.Lo32 << "\n"
+        << "\tMid32" << "\t\t" << dec.Mid32 << "\n"
+        << "\tLo64" << "\t\t" << dec.Lo64 << std::endl;
+}
+
+template<typename T>
+bool operator==(const T& t1, const T& t2)
+{
+    return 0 == memcmp((void*)&t1, (void*)&t2, sizeof(T));
+}
+
+template<typename T>
+bool Equals(LPCSTR err_id, T expected, T actual)
+{
+    if(expected == actual)
+        return true;
+    else
+    {
+        std::cout << "\t#Native Side Err# -- " << err_id 
+            << "\n\tExpected = " << expected << std::endl;
+        std::wcout << "\tActual is = " << actual << std::endl;
+
+        return false;
+    }
+}
+
+bool IntEqualsToExpected(LPCSTR err_id, int number, int expected)
+{
+    
+    if(number == expected)
+    {
+        return true;
+    }
+    else
+    {
+        std::wcout << "\t#Native Side Err# -- " << err_id 
+                  << "\n\tnumber Expected = " << expected << std::endl;
+
+        std::wcout << "\tnumber Actual is = " << number << std::endl;
+
+        return false;
+    }
+}
+bool DecEqualsToExpected(LPCSTR err_id, const DECIMAL& expected, const DECIMAL& actual)
+{
+    if(expected == actual)
+        return true;
+    else
+    {
+        std::cout << "\t#Native Side Err # -- " << err_id 
+            << "DECIMAL Expected is :" << std::endl;
+        DecDisplay(expected);
+
+        std::cout << "\t" << "____________________________________" << std::endl;
+
+        std::cout << "\tDECIMAL Actual is :" << std::endl;
+        DecDisplay(actual);
+
+        return false;
+    }
+}
+
+bool CYEqualsToExpected(LPCSTR err_id, const CY& expected, const CY& actual)
+{
+    if(expected == actual)
+        return true;
+    else
+    {
+        std::cout << "\t#Native Side Err# -- " << err_id 
+            << "\n\tCY Expected is :" << "Hi = " << expected.Hi
+            << "Lo = " << expected.Lo << std::endl;
+
+        std::cout << "\tCY Actual is :" << "Hi = " << actual.Hi
+            << "Lo = " << actual.Lo << std::endl;
+
+        return false;
+    }
+}
+
+template<typename T>
+T* RetSpecificTypeInstancePtr(T tVal)
+{
+    T* lpT = (T*)CoreClrAlloc(sizeof(T));
+    *lpT = tVal;
+    return lpT;
+}
+
+// As Struct
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_TakeDecByInOutRef(Fp_Dec fp)
+{
+    DECIMAL* lpDec = RetSpecificTypeInstancePtr(g_DECIMAL_MaxValue);
+
+    if((*fp)(lpDec))
+        return DecEqualsToExpected("001.01", g_DECIMAL_MinValue, *lpDec);
+    else
+        return false;
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_TakeDecByOutRef(Fp_Dec fp)
+{
+    DECIMAL* lpDec = RetSpecificTypeInstancePtr(g_DECIMAL_MaxValue);
+
+    if((*fp)(lpDec))
+        return DecEqualsToExpected("001.02", g_DECIMAL_Zero, *lpDec);
+    else
+        return false;
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_DecRet(Fp_RetDec fp)
+{
+    return DecEqualsToExpected("001.03", g_DECIMAL_MinValue, (*fp)());
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_TakeStru_Seq_DecAsStructAsFldByInOutRef(Fp_Stru_Seq_DecAsStructAsFld fp)
+{
+    Stru_Seq_DecAsStructAsFld s = { 1, g_DECIMAL_MaxValue };
+
+    if((*fp)(&s))
+        return DecEqualsToExpected("001.04", g_DECIMAL_MinValue, s.dec) && IntEqualsToExpected("001.05", s.number, 2);
+    else
+        return false;
+}
+
+// As CY
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_TakeCYByInOutRef(Fp_CY fp)
+{
+    CY* lpCy = RetSpecificTypeInstancePtr(g_CY_MaxValue);
+
+    if((*fp)(lpCy))
+        return CYEqualsToExpected("002.01", g_CY_MinValue, *lpCy);
+    else
+        return false;
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_TakeCYByOutRef(Fp_CY fp)
+{
+    CY* lpCy = RetSpecificTypeInstancePtr(g_CY_MaxValue);
+
+    if((*fp)(lpCy))
+        return CYEqualsToExpected("002.02", g_CY_Zero, *lpCy);
+    else
+        return false;
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_CYRet(Fp_RetCY fp)
+{
+    return CYEqualsToExpected("002.03", g_CY_MinValue, (*fp)());
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_TakeStru_Exp_DecAsCYAsFldByOutRef(Fp_Stru_Exp_DecAsCYAsFld fp)
+{
+    Stru_Exp_DecAsCYAsFld s = { 0 };
+
+    if((*fp)(&s))
+        return CYEqualsToExpected("002.04", g_CY_MaxValue, s.cy) && Equals("002.05", W('C'), s.wc);
+    else
+        return false;
+}
+
+// As LPStrcut
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_TakeDecByInOutRefAsLPStruct(Fp_DecAsLPStruct fp)
+{
+    DECIMAL* lpDec = RetSpecificTypeInstancePtr(g_DECIMAL_MaxValue);
+    DECIMAL**    lppDec = &lpDec;
+
+    if((*fp)(lppDec))
+        return DecEqualsToExpected("003.01", g_DECIMAL_MinValue, **lppDec);
+    else
+        return false;
+}
+
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_TakeDecByOutRefAsLPStruct(Fp_DecAsLPStruct fp)
+{
+    DECIMAL* lpDecAsLPStruct = RetSpecificTypeInstancePtr(g_DECIMAL_MaxValue);
+    DECIMAL** lppDecAsLPStruct = &lpDecAsLPStruct;
+
+    if((*fp)(lppDecAsLPStruct))
+        return DecEqualsToExpected("003.02", g_DECIMAL_Zero, **lppDecAsLPStruct);
+    else
+        return false;
+}
+
+//************** ReverseCall Return Int From Net **************//
+typedef int (*Fp_RetInt)();
+extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE ReverseCall_IntRet(Fp_RetInt fp)
+{
+    return 0x12345678 == (*fp)();
+}
index 140150a..4bec762 100755 (executable)
@@ -27,11 +27,12 @@ typedef ptrdiff_t INT_PTR;
 typedef size_t UINT_PTR;
 
 typedef unsigned long long ULONG64;
+typedef unsigned long long LONG64;
 typedef double DOUBLE;
 typedef float FLOAT;
-typedef signed long long LONG64, *PLONG64;
 typedef int INT, *LPINT;
 typedef unsigned int UINT;
+typedef int LONG;
 typedef char CHAR, *PCHAR;
 typedef unsigned short USHORT;
 typedef signed short SHORT;
@@ -40,6 +41,56 @@ typedef int LONG;
 
 typedef size_t SIZE_T;
 
+typedef union tagCY {
+    struct {
+#if BIGENDIAN
+        LONG    Hi;
+        LONG   Lo;
+#else
+        LONG   Lo;
+        LONG    Hi;
+#endif
+    };
+    LONG64 int64;
+} CY, *LPCY;
+
+typedef CY CURRENCY;
+
+typedef struct tagDEC {
+    // Decimal.cs treats the first two shorts as one long
+    // And they seriable the data so we need to little endian
+    // seriliazation
+    // The wReserved overlaps with Variant's vt member
+#if BIGENDIAN
+    union {
+        struct {
+            BYTE sign;
+            BYTE scale;
+        };
+        USHORT signscale;
+    };
+    USHORT wReserved;
+#else
+    USHORT wReserved;
+    union {
+        struct {
+            BYTE scale;
+            BYTE sign;
+        };
+        USHORT signscale;
+    };
+#endif
+    LONG Hi32;
+    union {
+        struct {
+            LONG Lo32;
+            LONG Mid32;
+        };
+        ULONG64 Lo64;
+    };
+} DECIMAL, *LPDECIMAL;
+
+
 #ifndef TRUE
 #define TRUE 1
 #endif
index 85f0850..a051692 100644 (file)
@@ -198,6 +198,18 @@ int wmemcmp(LPCWSTR str1, LPCWSTR str2,size_t len)
     return wcsncmp(str1, str2, len);
 }
 
+#define DECIMAL_NEG ((BYTE)0x80)
+#define DECIMAL_SCALE(dec)       ((dec).u.u.scale)
+#define DECIMAL_SIGN(dec)        ((dec).u.u.sign)
+#define DECIMAL_SIGNSCALE(dec)   ((dec).u.signscale)
+#define DECIMAL_LO32(dec)        ((dec).v.v.Lo32)
+#define DECIMAL_MID32(dec)       ((dec).v.v.Mid32)
+#define DECIMAL_HI32(dec)        ((dec).Hi32)
+#define DECIMAL_LO64_GET(dec)    ((dec).v.Lo64)
+#define DECIMAL_LO64_SET(dec,value)   {(dec).v.Lo64 = value; }
+
+#define DECIMAL_SETZERO(dec) {DECIMAL_LO32(dec) = 0; DECIMAL_MID32(dec) = 0; DECIMAL_HI32(dec) = 0; DECIMAL_SIGNSCALE(dec) = 0;}
+
 #endif //!_Win32
 
 #endif // __XPLAT_H__