C#, помощь по проекту asm. System.AccessViolationException, как это исправить?C#

Место общения программистов C#
Ответить
Anonymous
 C#, помощь по проекту asm. System.AccessViolationException, как это исправить?

Сообщение Anonymous »

Я работаю над проектом, который имитирует дейтеранопию (тип нарушения цветового зрения) с использованием комбинации C# и языка ассемблера (ASM). Код ASM создает DLL, которая обрабатывает данные изображения, и у меня возникает ошибка System.AccessViolationException с сообщением: «Попытка чтения или записи защищенной памяти. Это часто указывает на то, что другая память повреждена».
Подробности проекта:
Языки программирования: C# для основного приложения и язык ассемблера (ASM) для обработка изображений.
Функциональность: приложение позволяет пользователям загружать изображение, обрабатывать его (с помощью модуля asm или модуля C#) для имитации дейтеранопии и сохранять обработанное изображение.
Вот краткий обзор соответствующего кода:
Код ASM (ModuleAsm.asm):

Код: Выделить всё

.code
DeuteranopiaAsm proc

SimulateDeuteranopiaASM:
; Save registers
push rbp
mov rbp, rsp
sub rsp, 32

mov r9, rdx             ; pixelCount
mov r8d, edi            ; threadCount (passed through edi)
mov rdx, rcx            ; pointer to original image (originalImage)
mov rcx, rdi            ; pointer to processed image (processedImage)

; Calculate the portion size for each thread
xor rax, rax            ; Clear rax before division
mov eax, r9d            ; Move pixelCount to eax (32-bit register for division)
xor edx, edx            ; Clear edx (higher part of rax)
div r8d                 ; Divide eax by r8d (threadCount in r8d, 32-bit register)
mov r10d, eax           ; Store the result (portion size) in r10d

; Parallel processing
mov eax, r10d
mov r11, rdx            ; pointer to original image
mov rdx, rcx            ; pointer to processed image

ProcessLoop:
cmp r9d, 0
je Done

; Load original pixel data using 8-bit registers
movzx eax, byte ptr [r11]      ; Load red channel (R) into eax (extended to 32-bit register)
movzx ecx, byte ptr [r11 + 1]  ; Load green channel (G) into ecx (extended to 32-bit register)
movzx edx, byte ptr [r11 + 2]  ; Load blue channel (B) into edx (extended to 32-bit register)

; Simulate deuteranopia (green color blindness)
imul eax, eax, 625         ; Transform red channel
imul ecx, ecx, 375         ; Transform green channel
add eax, ecx
shr eax, 10                ; Divide by 1024
mov [rdx + 2], al          ; Save transformed red channel to processed image

imul ecx, ecx, 7           ; Further transform green channel
shr ecx, 3                 ; Divide by 8
mov [rdx + 1], cl          ; Save transformed green channel

imul edx, edx, 8           ; Transform blue channel
shr edx, 3                 ; Divide by 8
mov [rdx], dl              ; Save transformed blue channel

; Move to the next pixel
add r11, 3                 ; Move pointer in original image to the next pixel
add rdx, 3                 ; Move pointer in processed image to the next pixel
dec r9d                    ; Decrement pixel count
jmp ProcessLoop            ; Repeat loop

Done:
;  Restore registers
add rsp, 32
pop rbp
ret
DeuteranopiaAsm endp
end

Код C# (MainWindow.xaml.cs):

Код: Выделить всё

using System;
using System.Windows;
using System.Windows.Input;
using Microsoft.Win32;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace Projekt
{
public partial class MainWindow : Window
{
// Importing the ASM function (updated with the correct path)
[DllImport(@"C:\Users\kacpe\Desktop\home\Programing\studia\ASM-SEM-5\Projekt\x64\Debug\ModuleAsm.dll")]
public static extern void DeuteranopiaAsm(IntPtr originalImage, IntPtr processedImage, int pixelCount, int stride, int threadCount);

private Bitmap _originalImage;
private Bitmap _processedImage;

public MainWindow()
{
InitializeComponent();
int processorThreads = Environment.ProcessorCount;
threadSlider.Value = processorThreads;
threadCount.Text = $"Selected threads: {processorThreads}";
}

private void Exit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}

private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ButtonState == MouseButtonState.Pressed)
{
this.DragMove();
}
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.WindowStartupLocation = WindowStartupLocation.Manual;
var screenWidth = SystemParameters.PrimaryScreenWidth;
var screenHeight = SystemParameters.PrimaryScreenHeight;
this.Left = (screenWidth - this.Width) / 2;
this.Top = (screenHeight - this.Height) / 2;
}

private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs  e)
{
if (threadCount != null)
{
int threadValue = (int)threadSlider.Value;
threadCount.Text = $"Thread count: {threadValue}";
}
}

private void ChooseImage_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog
{
Filter = "Image files (*.jpg, *.png)|*.jpg;*.png",
Title = "Choose an image"
};

if (openFileDialog.ShowDialog() == true)
{
imagePathTextBox.Text = openFileDialog.FileName;
_originalImage = new Bitmap(openFileDialog.FileName);
MessageBox.Show("Image loaded successfully.");
}
}

private void ProcessImage_Click(object sender, RoutedEventArgs e)
{
if (_originalImage == null)
{
MessageBox.Show("Please select an image first.");
return;
}

int threadCount = (int)threadSlider.Value;

_processedImage = new Bitmap(_originalImage.Width, _originalImage.Height);

if (asmRadioButton.IsChecked == true)
{
Rectangle rect = new Rectangle(0, 0, _originalImage.Width, _originalImage.Height);
BitmapData originalData = _originalImage.LockBits(rect, ImageLockMode.ReadOnly, _originalImage.PixelFormat);
BitmapData processedData = _processedImage.LockBits(rect, ImageLockMode.WriteOnly, _processedImage.PixelFormat);

int pixelCount = _originalImage.Width * _originalImage.Height;
int stride = originalData.Stride;

IntPtr originalPtr = originalData.Scan0;
IntPtr processedPtr = processedData.Scan0;

// Call to the ASM function
DeuteranopiaAsm(originalPtr, processedPtr, pixelCount, stride, threadCount);

_originalImage.UnlockBits(originalData);
_processedImage.UnlockBits(processedData);

MessageBox.Show("Image processed in ASM.");
}
else if (cSharpRadioButton.IsChecked == true)
{
_processedImage = SimulateDeuteranopia(_originalImage, threadCount);
MessageBox.Show("Image processed in C#.");
}
}

private void SaveImage_Click(object sender, RoutedEventArgs e)
{
if (_processedImage == null)
{
MessageBox.Show("Please process the image first.");
return;
}

SaveFileDialog saveFileDialog = new SaveFileDialog
{
Filter = "Image files (*.jpg, *.png)|*.jpg;*.png",
Title = "Save image"
};

if (saveFileDialog.ShowDialog() == true)
{
_processedImage.Save(saveFileDialog.FileName);
MessageBox.Show("Image saved successfully.");
}
}

public static Bitmap SimulateDeuteranopia(Bitmap original, int threads)
{
int width = original.Width;
int height = original.Height;
Bitmap simulatedImage = new Bitmap(width, height, original.PixelFormat);

Rectangle rect = new Rectangle(0, 0, original.Width, original.Height);
BitmapData originalData = original.LockBits(rect, ImageLockMode.ReadOnly, original.PixelFormat);
BitmapData simulatedData = simulatedImage.LockBits(rect, ImageLockMode.WriteOnly, simulatedImage.PixelFormat);

int bytesPerPixel = Image.GetPixelFormatSize(original.PixelFormat) / 8;
int stride = originalData.Stride;
IntPtr originalScan0 = originalData.Scan0;
IntPtr simulatedScan0 = simulatedData.Scan0;

byte[] originalPixels = new byte[stride * height];
byte[] simulatedPixels = new byte[stride * height];

Marshal.Copy(originalScan0, originalPixels, 0, originalPixels.Length);

Parallel.For(0, threads, threadIndex =>
{
int partitionSize = height / threads;
int start = threadIndex * partitionSize;
int end = (threadIndex == threads - 1) ? height :  start + partitionSize;

for (int y = start; y < end; y++)
{
for (int x = 0; x < width; x++)
{
int pixelIndex = y * stride + x * bytesPerPixel;

byte originalB = originalPixels[pixelIndex];
byte originalG = originalPixels[pixelIndex + 1];
byte originalR = originalPixels[pixelIndex + 2];

// Simulate color transformation for deuteranopia
int newR = (int)(originalR * 0.625 + originalG * 0.375);
int newG = (int)(originalG * 0.7);
int newB = (int)(originalB * 0.8);

newR = Clamp(newR, 0, 255);
newG = Clamp(newG, 0, 255);
newB = Clamp(newB, 0, 255);

simulatedPixels[pixelIndex] = (byte)newB;
simulatedPixels[pixelIndex + 1] = (byte)newG;
simulatedPixels[pixelIndex + 2] = (byte)newR;
}
}
});

Marshal.Copy(simulatedPixels, 0, simulatedScan0, simulatedPixels.Length);

original.UnlockBits(originalData);
simulatedImage.UnlockBits(simulatedData);

return simulatedImage;
}

public static int Clamp(int value, int min, int max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
}
}

Описание проблемы:
Исключение AccessViolationException обычно возникает, когда код пытается получить доступ к запрещенной памяти. Я подозреваю, что это может быть связано с тем, как указатели и память управляются между кодом C# и ASM. Я правильно блокирую фрагменты изображений, но мне интересно, есть ли какие-либо проблемы с выравниванием памяти или могут ли проблемы быть вызваны манипуляциями с пикселями.
Дополнительная информация:
  • Я использую .NET Framework и ориентируюсь на архитектуру x64.
  • DLL загружается правильно, но на этапе обработки изображения происходят сбои. .
  • Буду признателен за любую информацию об распространенных ошибках, которые могут привести к этому исключению, особенно в сценариях взаимодействия между C# и ASM.
Я пробовал реструктуризировать, но это ни к чему не приводит...

Подробнее здесь: https://stackoverflow.com/questions/791 ... o-fix-this
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»