none
IPGlobalProperties.GetActiveTcpConnections() を使うとメモリが増える RRS feed

  • 質問

  • お世話になります。

    Windows10 PCでC#(.NET5)を用いてTCP通信プログラムを作っています。

    その中で、PCのTCP接続の一覧を取得したくなり、下記のコードでTCP接続の一覧を取得したのですが、

    VisualStudioの診断ツールのプロセスメモリがじわじわと増えていっていることを発見しました。

    メモリ使用量のスナップショットをとって確認したのですが、

    オブジェクト、ヒープサイズともに全く増えておらず、プロセスメモリのグラフだけが、じわじわと右肩上がりで増えていきます。

    デバッグ段階で、可能な限り最小コードで実行して見たのですが、同様にメモリが増加してしまいました。(下記コード)

    using System;
    using System.Net.NetworkInformation;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
                while (true)
                {
                    TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();
                }
            }
        }
    }

    そこでお伺いしたいのですが、

    どうすればメモリが増えないようにできるのでしょうか?

    根本的にこのメソッドの使用方法が間違っているとかがあれば、ご指摘いただければと思います。

    よろしくお願いいたします。


    2021年2月9日 6:19

回答

  • ライブラリでメモリーの解放漏れしているので、このライブラリを使うなら修正されるまで待つしかありません。
    あるいは、IPv6が有効ではない環境だとリークする部分が実行されないので、IPv4環境に限定してやればリークしません。

    どうしても急ぎであるなら、ソースコードは公開されているので、必要な個所を抜き出して問題個所を修正してやれば回避できないこともないかも。

    // Licensed to the .NET Foundation under one or more agreements.
    // The .NET Foundation licenses this file to you under the MIT license.
    namespace Interop
    {
        using System.Net.Sockets;
        using System.Runtime.InteropServices;
    
        class Test
        {
            public static IPEndPoint[] GetActiveTcpListeners()
            {
                List<IPEndPoint> list = new List<IPEndPoint>();
                List<SystemTcpConnectionInformation> connections = GetAllTcpConnections();
                foreach (TcpConnectionInformation connection in connections)
                {
                    if (connection.State == TcpState.Listen)
                    {
                        list.Add(connection.LocalEndPoint);
                    }
                }
    
                return list.ToArray();
            }
    
            ///
            /// Gets the active TCP connections. Uses the native GetTcpTable API.
            private static unsafe List<SystemTcpConnectionInformation> GetAllTcpConnections()
            {
                uint size = 0;
                uint result = 0;
                List<SystemTcpConnectionInformation> tcpConnections = new List<SystemTcpConnectionInformation>();
    
                // Check if it supports IPv4 for IPv6 only modes.
                if (Socket.OSSupportsIPv4)
                {
                    // Get the buffer size needed.
                    result = Interop.IpHlpApi.GetTcpTable(IntPtr.Zero, ref size, true);
    
                    while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER)
                    {
                        // Allocate the buffer and get the TCP table.
                        IntPtr buffer = Marshal.AllocHGlobal((int)size);
                        try
                        {
                            result = Interop.IpHlpApi.GetTcpTable(buffer, ref size, true);
    
                            if (result == Interop.IpHlpApi.ERROR_SUCCESS)
                            {
                                var span = new ReadOnlySpan<byte>((byte*)buffer, (int)size);
    
                                // The table info just gives us the number of rows.
                                ref readonly Interop.IpHlpApi.MibTcpTable tcpTableInfo = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcpTable>(span);
    
                                if (tcpTableInfo.numberOfEntries > 0)
                                {
                                    // Skip over the tableinfo to get the inline rows.
                                    span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpTable));
    
                                    for (int i = 0; i < tcpTableInfo.numberOfEntries; i++)
                                    {
                                        tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcpRow>(span)));
                                        span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpRow));
                                    }
                                }
                            }
                        }
                        finally
                        {
                            Marshal.FreeHGlobal(buffer);
                        }
                    }
    
                    // If we don't have any ipv4 interfaces detected, just continue.
                    if (result != Interop.IpHlpApi.ERROR_SUCCESS && result != Interop.IpHlpApi.ERROR_NO_DATA)
                    {
                        throw new NetworkInformationException((int)result);
                    }
                }
    
                if (Socket.OSSupportsIPv6)
                {
                    // Get the buffer size needed.
                    size = 0;
                    result = Interop.IpHlpApi.GetExtendedTcpTable(IntPtr.Zero, ref size, true,
                                                                            (uint)AddressFamily.InterNetworkV6,
                                                                            Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0);
    
                    while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER)
                    {
                        // Allocate the buffer and get the TCP table.
                        IntPtr buffer = Marshal.AllocHGlobal((int)size);
                        try
                        {
                            result = Interop.IpHlpApi.GetExtendedTcpTable(buffer, ref size, true,
                                                                                    (uint)AddressFamily.InterNetworkV6,
                                                                                    Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0);
                            if (result == Interop.IpHlpApi.ERROR_SUCCESS)
                            {
                                var span = new ReadOnlySpan<byte>((byte*)buffer, (int)size);
    
                                // The table info just gives us the number of rows.
                                ref readonly Interop.IpHlpApi.MibTcp6TableOwnerPid tcpTable6OwnerPid = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcp6TableOwnerPid>(span);
    
                                if (tcpTable6OwnerPid.numberOfEntries > 0)
                                {
                                    // Skip over the tableinfo to get the inline rows.
                                    span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6TableOwnerPid));
    
                                    for (int i = 0; i < tcpTable6OwnerPid.numberOfEntries; i++)
                                    {
                                        tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcp6RowOwnerPid>(span)));
    
                                        // We increment the pointer to the next row.
                                        span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6RowOwnerPid));
                                    }
                                }
                            }
                        }
                        finally //ここが間違ってる
                        {
                            Marshal.FreeHGlobal(buffer);
                        }
                    }
    
                    // If we don't have any ipv6 interfaces detected, just continue.
                    if (result != Interop.IpHlpApi.ERROR_SUCCESS && result != Interop.IpHlpApi.ERROR_NO_DATA)
                    {
                        throw new NetworkInformationException((int)result);
                    }
                }
    
                return tcpConnections;
            }
        }
    }
    
    
    namespace Interop
    {
        using System.Runtime.InteropServices;
        public class Libraries
        {
            public const string IpHlpApi = "Iphlpapi.dll";
        }
    
        // Represents an active TCP connection.
        internal class SystemTcpConnectionInformation : TcpConnectionInformation
        {
            private readonly IPEndPoint _localEndPoint;
            private readonly IPEndPoint _remoteEndPoint;
            private readonly TcpState _state;
    
            internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcpRow row)
            {
                _state = row.state;
    
                // Port is returned in Big-Endian - most significant bit on left.
                // Unfortunately, its done at the word level and not the DWORD level.
                int localPort = row.localPort1 << 8 | row.localPort2;
                int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2);
    
                _localEndPoint = new IPEndPoint(row.localAddr, (int)localPort);
                _remoteEndPoint = new IPEndPoint(row.remoteAddr, (int)remotePort);
            }
    
            // IPV6 version of the Tcp row.
            internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcp6RowOwnerPid row)
            {
                _state = row.state;
    
                // Port is returned in Big-Endian - most significant bit on left.
                // Unfortunately, its done at the word level and not the DWORD level.
                int localPort = row.localPort1 << 8 | row.localPort2;
                int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2);
    
                _localEndPoint = new IPEndPoint(new IPAddress(row.localAddrAsSpan, row.localScopeId), (int)localPort);
                _remoteEndPoint = new IPEndPoint(new IPAddress(row.remoteAddrAsSpan, row.remoteScopeId), (int)remotePort);
            }
    
            public override TcpState State { get { return _state; } }
    
            public override IPEndPoint LocalEndPoint { get { return _localEndPoint; } }
    
            public override IPEndPoint RemoteEndPoint { get { return _remoteEndPoint; } }
        }
    
        class IpHlpApi
        {
            [DllImport(Interop.Libraries.IpHlpApi)]
            internal static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref uint dwOutBufLen, bool order,
                                                    uint IPVersion, TcpTableClass tableClass, uint reserved);
    
            [DllImport(Interop.Libraries.IpHlpApi)]
            internal static extern uint GetTcpTable(IntPtr pTcpTable, ref uint dwOutBufLen, bool order);
    
            public const uint ERROR_SUCCESS = 0;
            //public const uint ERROR_INVALID_FUNCTION = 1;
            //public const uint ERROR_NO_SUCH_DEVICE = 2;
            //public const uint ERROR_INVALID_DATA = 13;
            //public const uint ERROR_INVALID_PARAMETER = 87;
            //public const uint ERROR_BUFFER_OVERFLOW = 111;
            public const uint ERROR_INSUFFICIENT_BUFFER = 122;
            public const uint ERROR_NO_DATA = 232;
            //public const uint ERROR_IO_PENDING = 997;
            //public const uint ERROR_NOT_FOUND = 1168;
    
            internal struct MibTcpTable
            {
                internal uint numberOfEntries;
            }
            [StructLayout(LayoutKind.Sequential)]
            internal struct MibTcpRow
            {
                internal TcpState state;
                internal uint localAddr;
                internal byte localPort1;
                internal byte localPort2;
                // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
                // There are reports where the high order bytes have garbage in them.
                internal byte ignoreLocalPort3;
                internal byte ignoreLocalPort4;
                internal uint remoteAddr;
                internal byte remotePort1;
                internal byte remotePort2;
                // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
                // There are reports where the high order bytes have garbage in them.
                internal byte ignoreRemotePort3;
                internal byte ignoreRemotePort4;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            internal unsafe struct MibTcp6RowOwnerPid
            {
                internal fixed byte localAddr[16];
                internal uint localScopeId;
                internal byte localPort1;
                internal byte localPort2;
                // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
                // There are reports where the high order bytes have garbage in them.
                internal byte ignoreLocalPort3;
                internal byte ignoreLocalPort4;
                internal fixed byte remoteAddr[16];
                internal uint remoteScopeId;
                internal byte remotePort1;
                internal byte remotePort2;
                // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
                // There are reports where the high order bytes have garbage in them.
                internal byte ignoreRemotePort3;
                internal byte ignoreRemotePort4;
                internal TcpState state;
                internal uint owningPid;
    
                internal ReadOnlySpan<byte> localAddrAsSpan => MemoryMarshal.CreateSpan(ref localAddr[0], 16);
                internal ReadOnlySpan<byte> remoteAddrAsSpan => MemoryMarshal.CreateSpan(ref remoteAddr[0], 16);
            }
    
            internal enum TcpTableClass
            {
                TcpTableBasicListener = 0,
                TcpTableBasicConnections = 1,
                TcpTableBasicAll = 2,
                TcpTableOwnerPidListener = 3,
                TcpTableOwnerPidConnections = 4,
                TcpTableOwnerPidAll = 5,
                TcpTableOwnerModuleListener = 6,
                TcpTableOwnerModuleConnections = 7,
                TcpTableOwnerModuleAll = 8
            }
    
            internal struct MibTcp6TableOwnerPid
            {
                internal uint numberOfEntries;
            }
    
        }
    
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク take060238 2021年2月10日 0:23
    2021年2月9日 16:21

すべての返信

  • ライブラリでメモリーの解放漏れしているので、このライブラリを使うなら修正されるまで待つしかありません。
    あるいは、IPv6が有効ではない環境だとリークする部分が実行されないので、IPv4環境に限定してやればリークしません。

    どうしても急ぎであるなら、ソースコードは公開されているので、必要な個所を抜き出して問題個所を修正してやれば回避できないこともないかも。

    // Licensed to the .NET Foundation under one or more agreements.
    // The .NET Foundation licenses this file to you under the MIT license.
    namespace Interop
    {
        using System.Net.Sockets;
        using System.Runtime.InteropServices;
    
        class Test
        {
            public static IPEndPoint[] GetActiveTcpListeners()
            {
                List<IPEndPoint> list = new List<IPEndPoint>();
                List<SystemTcpConnectionInformation> connections = GetAllTcpConnections();
                foreach (TcpConnectionInformation connection in connections)
                {
                    if (connection.State == TcpState.Listen)
                    {
                        list.Add(connection.LocalEndPoint);
                    }
                }
    
                return list.ToArray();
            }
    
            ///
            /// Gets the active TCP connections. Uses the native GetTcpTable API.
            private static unsafe List<SystemTcpConnectionInformation> GetAllTcpConnections()
            {
                uint size = 0;
                uint result = 0;
                List<SystemTcpConnectionInformation> tcpConnections = new List<SystemTcpConnectionInformation>();
    
                // Check if it supports IPv4 for IPv6 only modes.
                if (Socket.OSSupportsIPv4)
                {
                    // Get the buffer size needed.
                    result = Interop.IpHlpApi.GetTcpTable(IntPtr.Zero, ref size, true);
    
                    while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER)
                    {
                        // Allocate the buffer and get the TCP table.
                        IntPtr buffer = Marshal.AllocHGlobal((int)size);
                        try
                        {
                            result = Interop.IpHlpApi.GetTcpTable(buffer, ref size, true);
    
                            if (result == Interop.IpHlpApi.ERROR_SUCCESS)
                            {
                                var span = new ReadOnlySpan<byte>((byte*)buffer, (int)size);
    
                                // The table info just gives us the number of rows.
                                ref readonly Interop.IpHlpApi.MibTcpTable tcpTableInfo = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcpTable>(span);
    
                                if (tcpTableInfo.numberOfEntries > 0)
                                {
                                    // Skip over the tableinfo to get the inline rows.
                                    span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpTable));
    
                                    for (int i = 0; i < tcpTableInfo.numberOfEntries; i++)
                                    {
                                        tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcpRow>(span)));
                                        span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpRow));
                                    }
                                }
                            }
                        }
                        finally
                        {
                            Marshal.FreeHGlobal(buffer);
                        }
                    }
    
                    // If we don't have any ipv4 interfaces detected, just continue.
                    if (result != Interop.IpHlpApi.ERROR_SUCCESS && result != Interop.IpHlpApi.ERROR_NO_DATA)
                    {
                        throw new NetworkInformationException((int)result);
                    }
                }
    
                if (Socket.OSSupportsIPv6)
                {
                    // Get the buffer size needed.
                    size = 0;
                    result = Interop.IpHlpApi.GetExtendedTcpTable(IntPtr.Zero, ref size, true,
                                                                            (uint)AddressFamily.InterNetworkV6,
                                                                            Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0);
    
                    while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER)
                    {
                        // Allocate the buffer and get the TCP table.
                        IntPtr buffer = Marshal.AllocHGlobal((int)size);
                        try
                        {
                            result = Interop.IpHlpApi.GetExtendedTcpTable(buffer, ref size, true,
                                                                                    (uint)AddressFamily.InterNetworkV6,
                                                                                    Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0);
                            if (result == Interop.IpHlpApi.ERROR_SUCCESS)
                            {
                                var span = new ReadOnlySpan<byte>((byte*)buffer, (int)size);
    
                                // The table info just gives us the number of rows.
                                ref readonly Interop.IpHlpApi.MibTcp6TableOwnerPid tcpTable6OwnerPid = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcp6TableOwnerPid>(span);
    
                                if (tcpTable6OwnerPid.numberOfEntries > 0)
                                {
                                    // Skip over the tableinfo to get the inline rows.
                                    span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6TableOwnerPid));
    
                                    for (int i = 0; i < tcpTable6OwnerPid.numberOfEntries; i++)
                                    {
                                        tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcp6RowOwnerPid>(span)));
    
                                        // We increment the pointer to the next row.
                                        span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6RowOwnerPid));
                                    }
                                }
                            }
                        }
                        finally //ここが間違ってる
                        {
                            Marshal.FreeHGlobal(buffer);
                        }
                    }
    
                    // If we don't have any ipv6 interfaces detected, just continue.
                    if (result != Interop.IpHlpApi.ERROR_SUCCESS && result != Interop.IpHlpApi.ERROR_NO_DATA)
                    {
                        throw new NetworkInformationException((int)result);
                    }
                }
    
                return tcpConnections;
            }
        }
    }
    
    
    namespace Interop
    {
        using System.Runtime.InteropServices;
        public class Libraries
        {
            public const string IpHlpApi = "Iphlpapi.dll";
        }
    
        // Represents an active TCP connection.
        internal class SystemTcpConnectionInformation : TcpConnectionInformation
        {
            private readonly IPEndPoint _localEndPoint;
            private readonly IPEndPoint _remoteEndPoint;
            private readonly TcpState _state;
    
            internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcpRow row)
            {
                _state = row.state;
    
                // Port is returned in Big-Endian - most significant bit on left.
                // Unfortunately, its done at the word level and not the DWORD level.
                int localPort = row.localPort1 << 8 | row.localPort2;
                int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2);
    
                _localEndPoint = new IPEndPoint(row.localAddr, (int)localPort);
                _remoteEndPoint = new IPEndPoint(row.remoteAddr, (int)remotePort);
            }
    
            // IPV6 version of the Tcp row.
            internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcp6RowOwnerPid row)
            {
                _state = row.state;
    
                // Port is returned in Big-Endian - most significant bit on left.
                // Unfortunately, its done at the word level and not the DWORD level.
                int localPort = row.localPort1 << 8 | row.localPort2;
                int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2);
    
                _localEndPoint = new IPEndPoint(new IPAddress(row.localAddrAsSpan, row.localScopeId), (int)localPort);
                _remoteEndPoint = new IPEndPoint(new IPAddress(row.remoteAddrAsSpan, row.remoteScopeId), (int)remotePort);
            }
    
            public override TcpState State { get { return _state; } }
    
            public override IPEndPoint LocalEndPoint { get { return _localEndPoint; } }
    
            public override IPEndPoint RemoteEndPoint { get { return _remoteEndPoint; } }
        }
    
        class IpHlpApi
        {
            [DllImport(Interop.Libraries.IpHlpApi)]
            internal static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref uint dwOutBufLen, bool order,
                                                    uint IPVersion, TcpTableClass tableClass, uint reserved);
    
            [DllImport(Interop.Libraries.IpHlpApi)]
            internal static extern uint GetTcpTable(IntPtr pTcpTable, ref uint dwOutBufLen, bool order);
    
            public const uint ERROR_SUCCESS = 0;
            //public const uint ERROR_INVALID_FUNCTION = 1;
            //public const uint ERROR_NO_SUCH_DEVICE = 2;
            //public const uint ERROR_INVALID_DATA = 13;
            //public const uint ERROR_INVALID_PARAMETER = 87;
            //public const uint ERROR_BUFFER_OVERFLOW = 111;
            public const uint ERROR_INSUFFICIENT_BUFFER = 122;
            public const uint ERROR_NO_DATA = 232;
            //public const uint ERROR_IO_PENDING = 997;
            //public const uint ERROR_NOT_FOUND = 1168;
    
            internal struct MibTcpTable
            {
                internal uint numberOfEntries;
            }
            [StructLayout(LayoutKind.Sequential)]
            internal struct MibTcpRow
            {
                internal TcpState state;
                internal uint localAddr;
                internal byte localPort1;
                internal byte localPort2;
                // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
                // There are reports where the high order bytes have garbage in them.
                internal byte ignoreLocalPort3;
                internal byte ignoreLocalPort4;
                internal uint remoteAddr;
                internal byte remotePort1;
                internal byte remotePort2;
                // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
                // There are reports where the high order bytes have garbage in them.
                internal byte ignoreRemotePort3;
                internal byte ignoreRemotePort4;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            internal unsafe struct MibTcp6RowOwnerPid
            {
                internal fixed byte localAddr[16];
                internal uint localScopeId;
                internal byte localPort1;
                internal byte localPort2;
                // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
                // There are reports where the high order bytes have garbage in them.
                internal byte ignoreLocalPort3;
                internal byte ignoreLocalPort4;
                internal fixed byte remoteAddr[16];
                internal uint remoteScopeId;
                internal byte remotePort1;
                internal byte remotePort2;
                // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
                // There are reports where the high order bytes have garbage in them.
                internal byte ignoreRemotePort3;
                internal byte ignoreRemotePort4;
                internal TcpState state;
                internal uint owningPid;
    
                internal ReadOnlySpan<byte> localAddrAsSpan => MemoryMarshal.CreateSpan(ref localAddr[0], 16);
                internal ReadOnlySpan<byte> remoteAddrAsSpan => MemoryMarshal.CreateSpan(ref remoteAddr[0], 16);
            }
    
            internal enum TcpTableClass
            {
                TcpTableBasicListener = 0,
                TcpTableBasicConnections = 1,
                TcpTableBasicAll = 2,
                TcpTableOwnerPidListener = 3,
                TcpTableOwnerPidConnections = 4,
                TcpTableOwnerPidAll = 5,
                TcpTableOwnerModuleListener = 6,
                TcpTableOwnerModuleConnections = 7,
                TcpTableOwnerModuleAll = 8
            }
    
            internal struct MibTcp6TableOwnerPid
            {
                internal uint numberOfEntries;
            }
    
        }
    
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク take060238 2021年2月10日 0:23
    2021年2月9日 16:21
  • .NET Framework 4.8 時点では問題なく、.NET 5.0 のプレビュー中にバグっているのですね。

    https://github.com/dotnet/runtime/commit/9c82a36c23235c4d50954cb33a4d5d89b787a1aa#diff-35de7b31330089f70ddb6d21a92db1483e2b3f82358860c5ea0073ee5f90d1f9

    それまでにusing ステートメントに書き換わったが、Unity との相互運用性の問題から try-finally に戻りつつも、一部は catch になってしまったと…。

    2021年2月9日 21:37
    モデレータ
  • ご回答ありがとうございます。

     

    >ライブラリでメモリーの解放漏れしているので、このライブラリを使うなら修正されるまで待つしかありません。

    ライブラリ側での問題なのですね。

    少し難易度が高そうですが、教えていただいた方法を試してみようかと思います。

    (無理そうなら別の手段を模索します)

     

    ありがとうございました。

    2021年2月10日 0:23
  • ご回答ありがとうございます。

     

    >それまでにusing ステートメントに書き換わったが、Unity との相互運用性の問題から try-finally に戻りつつも、一部は catch になってしまったと…。

    catchとfinallyの書き間違いとは。。。

    流れで記述していると気が付かないかもしれないですね。(自分もやってしまうかもです)

    2021年2月10日 0:28