我正在开发一个项目,该项目结合使用 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<double> 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 代码之间的指针和内存管理方式有关。我正确锁定了图像的位,但我想知道是否存在任何内存对齐问题或者像素操作是否可能导致问题。
我尝试过重组,但没有结果...
mov rdx, rcx ; pointer to original image (originalImage) ... xor edx, edx ; Clear edx (higher part of rax) div r8d ; Divide eax by r8d ... mov r11, rdx ; pointer to original image
div r8d
指令不是“eax除以r8d”,而是组合EDX:EDX除以R8D。; Clear edx (higher part of rax)
这显示了对 x86 寄存器的误解。 EDX是RDX寄存器的低32位。 RAX 是一个完全不同的寄存器。