C#、asm 项目帮助。 System.AccessViolationException,如何解决这个问题?

问题描述 投票:0回答:1

我正在开发一个项目,该项目结合使用 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 代码之间的指针和内存管理方式有关。我正确锁定了图像的位,但我想知道是否存在任何内存对齐问题或者像素操作是否可能导致问题。

附加信息:

  • 我正在使用 .NET Framework 并针对 x64 架构。
  • DLL 正在正确加载,但在图像处理步骤中发生崩溃。
  • 如果您能深入了解可能导致此异常的常见陷阱,尤其是在 C# 和 ASM 之间的互操作场景中,我将不胜感激。

我尝试过重组,但没有结果...

c# xaml assembly 64-bit nasm
1个回答
0
投票

您丢失了指向原始图像的指针

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。
只需将相关指针存储在R11中即可开始

; Clear edx (higher part of rax)

这显示了对 x86 寄存器的误解。 EDX是RDX寄存器的低32位。 RAX 是一个完全不同的寄存器。

© www.soinside.com 2019 - 2024. All rights reserved.