Коммит a5c6b847 создал по автору Charles Williams's avatar Charles Williams
Просмотр файлов

Load sources.

владелец 29ff9df6
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
namespace Disposable
{
public class AsyncResourceHolder : IAsyncDisposable
{
private bool _connectionOpen = false;
private int _id;
private static int _nextId = 1;
public AsyncResourceHolder()
{
_id = _nextId++;
Console.WriteLine($"[Async#{_id}] Initializing connection synchronously (for demo simplicity)...");
// В реальном коде, открытие тоже могло бы быть асинхронным,
// но для демонстрации DisposeAsync, сделаем конструктор синхронным.
_connectionOpen = true;
Console.WriteLine($"[Async#{_id}] Connection {_id} opened.");
}
public async Task SendDataAsync(string data)
{
if (!_connectionOpen)
throw new InvalidOperationException($"[Async#{_id}] Connection not open or disposed.");
Console.WriteLine($"[Async#{_id}] Sending data asynchronously: '{data}'...");
await Task.Delay(500); // Имитация асинхронной отправки данных
Console.WriteLine($"[Async#{_id}] Data '{data}' sent.");
}
// Реализация IAsyncDisposable
public async ValueTask DisposeAsync()
{
Console.WriteLine($"[Async#{_id}] DisposeAsync() called. Closing connection asynchronously...");
if (_connectionOpen)
{
await Task.Delay(750); // Имитация асинхронного закрытия/очистки
_connectionOpen = false;
Console.WriteLine($"[Async#{_id}] Connection {_id} closed asynchronously.");
}
else
{
Console.WriteLine($"[Async#{_id}] Connection {_id} was already closed.");
}
}
// Опционально: Реализация IDisposable для совместимости
// Если вы реализуете оба интерфейса, Dispose() должен быть осторожен.
// Часто он просто вызывает DisposeAsync().AsTask().Wait() или AsTask().GetAwaiter().GetResult(),
// что, как мы знаем, может привести к дедлокам.
// Если ваш ресурс требует асинхронной очистки, лучше избегать синхронного Dispose()
// или по крайней мере ясно документировать его ограничения.
// public void Dispose()
// {
// Console.WriteLine($"[Async#{_id}] Synchronous Dispose() called. Waiting for async cleanup...");
// DisposeAsync().AsTask().Wait(); // Блокировка!
// GC.SuppressFinalize(this);
// Console.WriteLine($"[Async#{_id}] Synchronous Dispose() finished.");
// }
}
}
namespace Disposable
{
public class BadResourceHolder
{
private FileStream _file;
private string _filePath;
public BadResourceHolder(string filePath)
{
_filePath = filePath;
_file = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
Console.WriteLine($"[Bad] Resource acquired: {_filePath}");
}
public void WriteSomeData(string data)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data + Environment.NewLine);
_file.Write(bytes, 0, bytes.Length);
Console.WriteLine($"[Bad] Data written to {_filePath}");
}
// Здесь нет Dispose() и нет финализатора.
// Файл будет закрыт только при завершении приложения или когда GC по какой-то причине решит,
// что FileStream больше не нужен, но это будет очень недетерминированно.
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disposable
{
public class SomeClass : IDisposable
{
private int _counter = 0;
private bool _isDisposed = false;
private readonly Lock _balanceLock = new();
public void DoWork()
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(SomeClass));
}
using (_balanceLock.EnterScope())
{
_counter++;
Console.WriteLine($"DoWork — Counter: {_counter}");
}
}
public void Dispose()
{
if (!_isDisposed)
{
Console.WriteLine("SomeClass is being disposed");
_isDisposed = true;
}
}
}
public struct SomeStruct : IDisposable
{
public SomeClass ObjectToDispose { get; set; }
public void Dispose()
{
Console.WriteLine($"{nameof(SomeStruct)} is being disposed");
ObjectToDispose.Dispose();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disposable
{
public class ClassWithFinalyzer : IDisposable
{
private int _counter = 0;
private readonly int _someData1;
private readonly int _someData2;
private bool _isDisposed = false;
public ClassWithFinalyzer(bool isGood = true)
{
Console.WriteLine("Constructor is called");
_someData1 = 100;
if (!isGood) throw new InvalidOperationException("Crash");
_someData2 = _someData1 + 100;
}
public void DoWork()
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(ClassWithFinalyzer));
}
_counter++;
Console.WriteLine($"Do work — Counter: {_counter}");
}
~ClassWithFinalyzer()
{
Console.WriteLine("Finalyzer is called");
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
Console.WriteLine("SuppressFinalize is called");
}
protected virtual void Dispose(bool manual)
{
Console.WriteLine("Dispose is called");
if (!_isDisposed)
{
if (manual)
{
// Здесь освобождаем управляемые ресурсы, например, можно вызвать методы
// Dispose внутренних объектов класса.
Console.WriteLine("Free some managed resources...");
}
// Дальше освобождаем неуправляемые ресурсы.
Console.WriteLine("SomeClass is being disposed with state {0}", _someData2);
_isDisposed = true;
}
}
}
}
namespace Disposable
{
public class GoodResourceHolder : IDisposable
{
private FileStream _file;
private string _filePath;
private bool _disposed = false;
public GoodResourceHolder(string filePath)
{
_filePath = filePath;
_file = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
Console.WriteLine($"[Good] Resource acquired: {_filePath}");
}
public void WriteSomeData(string data)
{
if (_disposed)
throw new ObjectDisposedException(nameof(GoodResourceHolder), "Cannot write to a disposed resource.");
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data + Environment.NewLine);
_file.Write(bytes, 0, bytes.Length);
Console.WriteLine($"[Good] Data written to {_filePath}");
}
// Публичный метод Dispose, вызываемый пользователем или using
public void Dispose()
{
Dispose(true); // Передаем, что вызван явно (disposing = true)
GC.SuppressFinalize(this); // Говорим GC не вызывать финализатор, т.к. мы уже очистились
Console.WriteLine($"[Good] Dispose() called for {_filePath}");
}
// Защищенный виртуальный метод для фактической очистки
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Освобождаем управляемые ресурсы (в данном случае, FileStream)
_file?.Dispose();
Console.WriteLine($"[Good] Managed resource (FileStream) disposed for {_filePath}.");
}
// Здесь можно было бы освободить неуправляемые ресурсы напрямую,
// но FileStream сам заботится о своем нативном дескрипторе.
// В следующих секциях увидим прямую работу с неуправляемыми ресурсами.
_disposed = true;
}
}
// Финализатор (деструктор) - как страховка, если Dispose не вызван явно.
// Обсудим подробнее в следующей секции.
~GoodResourceHolder()
{
Console.WriteLine($"[Good] Finalizer called for {_filePath}. Dispose(false)");
Dispose(false); // Передаем, что вызван финализатором (disposing = false)
}
}
}
namespace Disposable
{
internal class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("--- Демонстрация 1: Утечка без IDisposable ---");
string badFilePath = "bad_log.txt";
try
{
BadResourceHolder badHolder = new BadResourceHolder(badFilePath);
badHolder.WriteSomeData("This data should be in bad_log.txt");
// Здесь мы забываем закрыть файл!
// badHolder._file.Close(); // Если бы мы это сделали, утечки бы не было.
Console.WriteLine($"[Bad] File '{badFilePath}' is theoretically still open.");
// Попробуйте удалить bad_log.txt вручную. Если он еще открыт, получите ошибку.
System.IO.File.Delete(badFilePath); // Вызовет ошибку, если файл еще открыт
}
catch (Exception ex)
{
Console.WriteLine($"[Error in BadResourceHolder demo]: {ex.Message}");
Console.WriteLine("This error indicates the file might still be in use.");
}
Console.WriteLine("-------------------------------------------\n");
System.Threading.Thread.Sleep(500); // Дадим немного времени
// --- Демонстрация 2: Корректное освобождение с IDisposable и using ---
Console.WriteLine("--- Демонстрация 2: Корректное освобождение с IDisposable и using ---");
string goodFilePath = "good_log.txt";
try
{
using (var goodHolder = new GoodResourceHolder(goodFilePath))
{
goodHolder.WriteSomeData("This data should be in good_log.txt");
Console.WriteLine($"[Good] File '{goodFilePath}' is in use within using block.");
} // <-- Здесь goodHolder.Dispose() вызывается автоматически
Console.WriteLine($"[Good] File '{goodFilePath}' is now closed. Trying to delete...");
System.IO.File.Delete(goodFilePath); // Теперь это должно сработать!
Console.WriteLine($"[Good] File '{goodFilePath}' successfully deleted.");
}
catch (Exception ex)
{
Console.WriteLine($"[Error in GoodResourceHolder demo]: {ex.Message}");
}
Console.WriteLine("-------------------------------------------\n");
// --- Демонстрация 3: using декларация (C# 8.0+) ---
Console.WriteLine("--- Демонстрация 3: using декларация (C# 8.0+) ---");
string anotherFilePath = "another_log.txt";
Demo3(anotherFilePath);
Console.ReadKey(); // Ждем, чтобы показать, что файл `another_log.txt` будет удален после выхода из Main()
System.IO.File.Delete(anotherFilePath);
Console.WriteLine($"[Another] File '{anotherFilePath}' deleted upon exit.");
Console.WriteLine("-------------------------------------------\n");
// --- Демонстрация: Disposable struct
Console.WriteLine("--- Демонстрация: Disposable struct");
DisposableClassAndStruct();
Console.ReadKey();
//return;
Console.WriteLine("-------------------------------------------\n");
// --- Демонстрация: Finalize or not finalize
Console.WriteLine("--- Демонстрация: Finalize or not finalize");
FinalizeOrNot(isDirectExecution: true, isGood: true);
Console.ReadKey();
//return
Console.WriteLine("-------------------------------------------\n");
// --- Демонстрация: Restore Object
Console.WriteLine("--- Демонстрация: Restore Object");
RestoreObject(isForRestore: true);
Console.ReadKey();
//return;
Console.WriteLine("-------------------------------------------\n");
Console.WriteLine("--- Демонстрация 4: Асинхронное освобождение с IAsyncDisposable и await using ---");
Console.WriteLine("Начинаем использование AsyncResourceHolder...");
try
{
await using (var asyncHolder = new AsyncResourceHolder()) // <-- Здесь await using
{
await asyncHolder.SendDataAsync("First message");
await asyncHolder.SendDataAsync("Second message");
Console.WriteLine("[Async Demo] Inside await using block. Resource is active.");
// asyncHolder.DisposeAsync() будет вызван автоматически здесь.
}
Console.WriteLine("[Async Demo] Outside await using block. Resource should be closed.");
}
catch (Exception ex)
{
Console.WriteLine($"[Async Demo Error]: {ex.Message}");
}
Console.WriteLine("--- Демонстрация 4 завершена ---\n");
// Пример использования с множественными await using декларациями
Console.WriteLine("--- Демонстрация 5: Множественные await using декларации ---");
await using var asyncHolder1 = new AsyncResourceHolder();
await using var asyncHolder2 = new AsyncResourceHolder();
await asyncHolder1.SendDataAsync("From holder 1");
await asyncHolder2.SendDataAsync("From holder 2");
Console.WriteLine("[Async Demo Multiple] All holders active. DisposeAsync will be called in reverse order.");
// asyncHolder2.DisposeAsync() будет вызван первым, затем asyncHolder1.DisposeAsync()
// при выходе из метода Main.
Console.WriteLine("\nНажмите любую клавишу для завершения программы...");
Console.ReadKey();
Console.WriteLine("Программа завершается.");
// Так как asyncHolder1 и asyncHolder2 объявлены как using var,
// DisposeAsync() для них будет вызван после этой точки при выходе из Main.
// В идеальном мире, мы всегда используем using
using (var holder = new SafeResourceHolder())
{
// Работаем с ресурсом
Console.WriteLine("Working with resource...");
} // Здесь автоматически вызывается Dispose(), и финализатор не нужен.
}
private static void Demo3(string anotherFilePath)
{
using var anotherHolder = new GoodResourceHolder(anotherFilePath);
anotherHolder.WriteSomeData("This is from a using declaration.");
Console.WriteLine($"[Another] File '{anotherFilePath}' is in use.");
// В конце метода Main() `anotherHolder.Dispose()` будет вызван автоматически.
// Но для демонстрации удаления, мы можем вызвать GC.Collect() (для примера, в реальном коде так не делать)
Console.WriteLine($"[Another] Will attempt to delete '{anotherFilePath}' after Main completes.");
// Не удаляйте здесь, так как using декларация еще не завершилась.
// Для полной уверенности в очистке после using декларации
// System.GC.Collect();
// System.GC.WaitForPendingFinalizers();
// System.IO.File.Delete(anotherFilePath); // Это сработает после завершения метода
}
private static void DisposableClassAndStruct()
{
Console.WriteLine("Create SomeStruct");
SomeStruct someStruct = new SomeStruct()
{
ObjectToDispose = new SomeClass()
};
try
{
someStruct.ObjectToDispose.DoWork();
someStruct.Dispose();
someStruct.ObjectToDispose.DoWork();
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
Console.WriteLine("Ok");
}
private static void FinalizeOrNot(bool isDirectExecution, bool isGood)
{
Console.WriteLine(
"Direct execution: {0} with Error in Constructor: {1}",
isDirectExecution ? "Yes" : "No",
isGood ? "No" : "Yes");
if (isDirectExecution)
{
try
{
using ClassWithFinalyzer obj = new(isGood);
obj.DoWork();
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}", ex.Message);
}
}
else
{
SomeCall(isGood);
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End");
// Additional Definition:
static void SomeCall(bool isGood)
{
try
{
using ClassWithFinalyzer obj = new(isGood);
obj.DoWork();
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}", ex.Message);
}
}
}
static void RestoreObject(bool isForRestore)
{
Console.WriteLine("Mode for restore: {0}", isForRestore);
CallWithRestoring(isForRestore, "'Test Object'");
GC.Collect();
GC.WaitForPendingFinalizers();
bool isRestored = TestRestored();
if (isRestored)
{
Console.WriteLine("Delete the object again.");
ClassWithRestoring.ToDelete();
GC.Collect();
GC.WaitForPendingFinalizers();
_ = TestRestored();
}
Console.WriteLine("End");
// Additional Definitions:
static void CallWithRestoring(bool isForRestore, string parameter)
{
ClassWithRestoring obj = new(isForRestore, parameter);
}
static bool TestRestored()
{
bool isRestored = ClassWithRestoring.IsRestored();
Console.WriteLine(
"We have restored object: {0}",
isRestored ? "Yes" : "No"
);
if (isRestored)
{
Console.WriteLine("We have the object {0}.", ClassWithRestoring.GetRestored()?.Name);
}
return isRestored;
}
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disposable
{
public sealed class ClassWithRestoring
{
public static void ToDelete() => _restoredQueue.Clear();
public static bool IsRestored() => _restoredQueue.Count > 0;
public static ClassWithRestoring? GetRestored()
{
if (_restoredQueue.TryDequeue(out object? result))
{
return (ClassWithRestoring)result;
}
return null;
}
private static readonly ConcurrentQueue<object> _restoredQueue = new();
private bool _isForRestore;
public string Name { get; private set; }
public ClassWithRestoring(bool isForRestore = false, string name = "")
{
_isForRestore = isForRestore;
Name = name;
Console.WriteLine(
"Constructor is called, we planned to restore: {0} for this instance: {1}",
isForRestore ? "Yes" : "No",
name);
}
~ClassWithRestoring()
{
Console.WriteLine("Finalyzer is called");
if (_isForRestore)
{
Console.WriteLine("Restoring object");
// Восстанавливаем объект (разово).
_isForRestore = false;
// ConcurrentQueue, потому что финализатор вызывается из отдельного потока.
_restoredQueue.Enqueue(this);
// Чтобы финализатор выполнился повторно при следующем удалении объекта
// Для каждого восстановления нужно вызвать только один раз.
GC.ReRegisterForFinalize(this);
}
else
{
Console.WriteLine("Finish Finalyzer");
}
}
}
}
namespace Disposable
{
public class SafeResourceHolder : IDisposable
{
private IntPtr _unmanagedHandle; // Неуправляемый ресурс
private bool _disposed = false;
public SafeResourceHolder()
{
_unmanagedHandle = OpenSomeNativeResource();
Console.WriteLine($"MySafeResourceHolder created. Handle: {_unmanagedHandle}");
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // Очень важно!
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Освобождение управляемых ресурсов, если они есть
Console.WriteLine("Disposing managed resources (if any).");
}
// Освобождение неуправляемых ресурсов
Console.WriteLine($"Releasing unmanaged handle: {_unmanagedHandle}");
CloseSomeNativeResource(_unmanagedHandle);
_unmanagedHandle = IntPtr.Zero; // Обнуляем, чтобы избежать повторного использования
_disposed = true;
}
}
// Финализатор как страховка!
~SafeResourceHolder()
{
Console.WriteLine($"MySafeResourceHolder finalizer called as a fallback. Releasing handle: {_unmanagedHandle}");
Dispose(false); // Вызываем Dispose с параметром false, так как GC вызвал финализатор
}
private IntPtr OpenSomeNativeResource() => new IntPtr(456);
private void CloseSomeNativeResource(IntPtr handle) => Console.WriteLine($"Native resource {handle} closed by Dispose.");
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
using System.Runtime.InteropServices;
namespace InvalidHandle
{
internal class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes, // IntPtr.Zero для NULL
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile); // IntPtr.Zero для NULL
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
public const uint GENERIC_READ = 0x80000000;
public const uint OPEN_EXISTING = 3;
public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
static void Main(string[] args)
{
DemonstrateInvalidHandle();
}
public static void DemonstrateInvalidHandle()
{
// Попытка открыть несуществующий файл
IntPtr fileHandle = CreateFile("non_existent_file.txt", GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (fileHandle == INVALID_HANDLE_VALUE)
{
Console.WriteLine("Failed to open file. Handle is INVALID_HANDLE_VALUE.");
// Отсутствие проверки здесь приведет к ошибке при попытке использовать fileHandle
}
else
{
Console.WriteLine($"Successfully opened file. Handle: {fileHandle}");
CloseHandle(fileHandle);
}
}
public class LeakyFileHandle // АНТИПАТТЕРН!
{
private IntPtr _fileHandle;
public LeakyFileHandle(string filePath)
{
_fileHandle = CreateFile(filePath, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (_fileHandle == INVALID_HANDLE_VALUE)
{
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
Console.WriteLine($"LeakyFileHandle created. Handle: {_fileHandle}");
}
// Нет Dispose, нет финализатора -> утечка!
// ~LeakyFileHandle() { CloseHandle(_fileHandle); } // Это нужно, но лучше IDisposable
}
// new LeakyFileHandle("some_file.txt"); // Ресурс будет утекать
public static void DemonstrateDoubleFree()
{
IntPtr tempHandle = CreateFile("test_file.txt", GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (tempHandle == INVALID_HANDLE_VALUE)
{
Console.WriteLine("Could not open test_file.txt for double-free demo.");
return;
}
Console.WriteLine($"Opened test_file.txt. Handle: {tempHandle}");
CloseHandle(tempHandle); // Первое освобождение
Console.WriteLine($"First close successful for handle: {tempHandle}");
// Попытка использовать tempHandle после освобождения
// Это может привести к Use-After-Free или просто ошибке
// Например, ReadFile(tempHandle, ...)
CloseHandle(tempHandle); // Второе освобождение (Double-Free) - опасно!
Console.WriteLine($"Second close attempted for handle: {tempHandle}. This is a double-free risk!");
}
}
}
using System.Runtime.InteropServices;
namespace NativeBufferHandle
{
public class NativeBufferHandle : SafeHandle
{
public NativeBufferHandle() : base(IntPtr.Zero, true) { }
// Конструктор, который принимает уже выделенный указатель
public NativeBufferHandle(IntPtr preAllocatedPtr, bool ownsHandle = true) : base(IntPtr.Zero, ownsHandle)
{
SetHandle(preAllocatedPtr);
}
public override bool IsInvalid => handle == IntPtr.Zero;
// Этот метод будет вызван при освобождении SafeHandle (например, при сборке мусора)
protected override bool ReleaseHandle()
{
if (!IsInvalid)
{
Console.WriteLine($"Освобождаем неуправляемый буфер по адресу: {handle}");
Marshal.FreeHGlobal(handle); // Используем Marshal.FreeHGlobal для освобождения памяти, выделенной через Marshal.AllocHGlobal
handle = IntPtr.Zero;
return true;
}
return false;
}
// Для демонстрации, можем добавить свойство для размера буфера
public long Size { get; set; }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
using System.Runtime.InteropServices;
using System.Text;
namespace NativeBufferHandle
{
internal class Program
{
static void Main(string[] args)
{
string message = "Привет, мир! This is a test string.";
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
long bufferSize = messageBytes.Length + 1; // +1 для нулевого символа в конце (C-style строка)
// Используем using для гарантированного освобождения SafeHandle
using (NativeBufferHandle bufferHandle = new NativeBufferHandle())
{
try
{
// 2. Выделяем неуправляемую память с помощью Marshal.AllocHGlobal
// В реальных сценариях вы могли бы использовать P/Invoke для вызова нативных функций выделения памяти
IntPtr nativeBufferPtr = Marshal.AllocHGlobal((IntPtr)bufferSize);
bufferHandle.SetHandle(nativeBufferPtr);
bufferHandle.Size = bufferSize;
Console.WriteLine($"Выделен неуправляемый буфер по адресу: {bufferHandle.handle} размером: {bufferHandle.Size} байт.");
// 3. Записываем данные в неуправляемую память с помощью NativeMemory
unsafe
{
byte* pBuffer = (byte*)bufferHandle.handle;
// Копируем байты сообщения
NativeMemory.Copy(messageBytes.AsSpan(), pBuffer, (nuint)messageBytes.Length);
// Добавляем нулевой символ в конец (опционально, но хорошая практика для строк)
pBuffer[messageBytes.Length] = 0;
Console.WriteLine("Данные записаны в неуправляемый буфер.");
// 4. Читаем данные из неуправляемой памяти с помощью NativeMemory
Span<byte> readBuffer = stackalloc byte[(int)bufferSize];
NativeMemory.Copy(pBuffer, readBuffer, (nuint)bufferSize);
// Находим конец строки (нулевой символ)
int nullTerminatorIndex = readBuffer.IndexOf((byte)0);
if (nullTerminatorIndex != -1)
{
readBuffer = readBuffer.Slice(0, nullTerminatorIndex);
}
string readMessage = Encoding.UTF8.GetString(readBuffer);
Console.WriteLine($"Прочитанная строка из неуправляемого буфера: \"{readMessage}\"");
}
}
catch (Exception ex)
{
Console.WriteLine($"Произошла ошибка: {ex.Message}");
}
} // Здесь SafeHandle будет автоматически освобожден, даже если произошла ошибка
Console.WriteLine("Приложение завершено. SafeHandle должен был быть освобожден.");
Console.ReadKey();
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
using System.Runtime.InteropServices;
namespace NativeHandles
{
public class NativeHandles
{
// [DllImport] для GetCurrentProcess - возвращает псевдо-дескриптор текущего процесса
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetCurrentProcess();
// Функция для закрытия дескриптора (для реальных дескрипторов,
// GetCurrentProcess возвращает псевдо-дескриптор, который не нужно закрывать)
// Эта функция будет использоваться в следующих примерах для реальных хэндлов
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
public static void Main(string[] args)
{
IntPtr processHandle = IntPtr.Zero; // Инициализируем нулевым значением
try
{
processHandle = GetCurrentProcess();
Console.WriteLine($"Current process handle (pseudo-handle): {processHandle}");
// Важно: GetCurrentProcess возвращает псевдо-дескриптор, который не нужно закрывать.
// Если бы это был реальный дескриптор, полученный, например, от CreateFile,
// мы бы вызвали CloseHandle(processHandle);
}
finally
{
// В случае реального дескриптора:
// if (processHandle != IntPtr.Zero && IsRealHandle(processHandle))
// {
// CloseHandle(processHandle);
// Console.WriteLine($"Closed handle: {processHandle}");
// }
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
using System.Runtime.InteropServices;
namespace NativeInvoke
{
public class NativeMethods
{
// [DllImport] атрибут указывает, из какой DLL мы импортируем функцию.
// "kernel32.dll" - это одна из основных системных библиотек Windows.
// Метод должен быть статическим и extern (реализация находится вовне).
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern void OutputDebugString(string lpOutputString);
// Другой пример: получение текущего системного времени
[DllImport("kernel32.dll")]
public static extern void GetSystemTime(ref SYSTEMTIME lpSystemTime);
// Структура для GetSystemTime (должна точно соответствовать нативной)
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
static void Main(string[] args)
{
// Вызов нативной функции
OutputDebugString("Hello from P/Invoke! This message goes to debug output.\n");
Console.WriteLine("OutputDebugString called. Check your debugger's output window.");
// Получение системного времени
SYSTEMTIME st = new();
GetSystemTime(ref st);
Console.WriteLine($"Current System Time (UTC): {st.wHour:D2}:{st.wMinute:D2}:{st.wSecond:D2}");
Console.ReadKey(); // Ждем, чтобы увидеть результат перед выходом
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Exe</OutputType>
</PropertyGroup>
</Project>
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать