1

I have this C++ function (unmanaged code) in a C++/CLI-project.

    BMP Image;
    Image.ReadFromFile(filePath);
    HsvColor hsvPixel;
    RGBApixel startPixel;
    for (int i = 0; i < Image.TellWidth(); i++) {
        for (int j = 0; j < Image.TellHeight(); j++) {


            startPixel = *(Image(i, j));
            hsvPixel = RgbToHsv(startPixel);


            RGBApixel finalPixel = HsvToRgb(hsvPixel);
            *(Image(i, j)) = finalPixel;

        }
    }

(HsvColor and RGBApixel are both structs out of unsigned chars. Image(i, j) returns a pointer to a RGBApixel.)

The problematic line is hsvPixel = RgbToHsv(startPixel);

I have two problems with this line:

  1. It does sometimes cause an System.AccessViolationException, but not always. The error message also says that it was attempted to write into protected memory. When I execute the code step by step with the debugger, the RgbToHsv function returns a value, then it goes to some system function I can't see, and then it crashes before it goes back to the code block posted here.

  2. It doesn't initialize the hsvPixel with the correct value. I followed it in debug mode and it does return an HsvColor object with correct values, but after the line has been executed the unsigned chars of the HsvColor struct are set to completely different value than the one returned by the function.

I would be very grateful for some help.

I also tried to execute only the RgbToHsv method alone without doing any assignments. When I do this, the loop runs through fine most of the times, but crashes sometimes too with System.AccessViolationException, only that that is much rarer than when doing the assignment to hsvPixel.

Code for RGBAPixel:

typedef struct RGBApixel {
    ebmpBYTE Blue;
    ebmpBYTE Green;
    ebmpBYTE Red;
    ebmpBYTE Alpha;
} RGBApixel; 

An ebmpBYTE is a typedef-ed unsigned char.

Code for both methods:

RGBApixel HsvToRgb(HsvColor hsv)
{
    RGBApixel rgb;
    unsigned char region, remainder, p, q, t;

    if (hsv.Saturation == 0)
    {
        rgb.Red = hsv.Value;
        rgb.Green = hsv.Value;
        rgb.Blue = hsv.Value;
        rgb.Alpha = 1;
        return rgb;
    }

    region = hsv.Hue / 43;
    remainder = (hsv.Hue - (region * 43)) * 6;

    p = (hsv.Value * (255 - hsv.Saturation)) >> 8;
    q = (hsv.Value * (255 - ((hsv.Saturation * remainder) >> 8))) >> 8;
    t = (hsv.Value * (255 - ((hsv.Saturation * (255 - remainder)) >> 8))) >> 8;

    switch (region)
    {
    case 0:
        rgb.Red = hsv.Value; rgb.Green = t; rgb.Blue = p;
        break;
    case 1:
        rgb.Red = q; rgb.Green = hsv.Value; rgb.Blue = p;
        break;
    case 2:
        rgb.Red = p; rgb.Green = hsv.Value; rgb.Blue = t;
        break;
    case 3:
        rgb.Red = p; rgb.Green = q; rgb.Blue = hsv.Value;
        break;
    case 4:
        rgb.Red = t; rgb.Green = p; rgb.Blue = hsv.Value;
        break;
    default:
        rgb.Red = hsv.Value; rgb.Green = p; rgb.Blue = q;
        break;
    }

    return rgb;
}

HsvColor RgbToHsv(RGBApixel rgb)
{
    HsvColor hsv;
    unsigned char rgbMin, rgbMax;

    rgbMin = rgb.Red < rgb.Green ? (rgb.Red < rgb.Blue ? rgb.Red : rgb.Blue) : (rgb.Green < rgb.Blue ? rgb.Green : rgb.Blue);
    rgbMax = rgb.Red > rgb.Green ? (rgb.Red > rgb.Blue ? rgb.Red : rgb.Blue) : (rgb.Green > rgb.Blue ? rgb.Green : rgb.Blue);

    hsv.Value = rgbMax;
    if (hsv.Value == 0)
    {
        hsv.Hue = 0;
        hsv.Saturation = 0;
        return hsv;
    }

    hsv.Saturation = 255 * long(rgbMax - rgbMin) / hsv.Value;
    if (hsv.Saturation == 0)
    {
        hsv.Hue = 0;
        return hsv;
    }

    if (rgbMax == rgb.Red)
        hsv.Hue = 0 + 43 * (rgb.Green - rgb.Blue) / (rgbMax - rgbMin);
    else if (rgbMax == rgb.Green)
        hsv.Hue = 85 + 43 * (rgb.Blue - rgb.Red) / (rgbMax - rgbMin);
    else
        hsv.Hue = 171 + 43 * (rgb.Red - rgb.Green) / (rgbMax - rgbMin);

    return hsv;
}

Implementation of Image(i, j):

RGBApixel* BMP::operator()(int i, int j)
{
 using namespace std;
 bool Warn = false;
 if( i >= Width )
 { i = Width-1; Warn = true; }
 if( i < 0 )
 { i = 0; Warn = true; }
 if( j >= Height )
 { j = Height-1; Warn = true; }
 if( j < 0 )
 { j = 0; Warn = true; }
 if( Warn && EasyBMPwarnings )
 {
  cout << "EasyBMP Warning: Attempted to access non-existent pixel;" << endl
       << "                 Truncating request to fit in the range [0,"
       << Width-1 << "] x [0," << Height-1 << "]." << endl;
 }  
 return &(Pixels[i][j]);
}

The declaration of Pixels (from the EasyBMP library):

RGBApixel** Pixels;

Edit: I used a debugger to look at how the RgbToHsv function executes. The startPixel is always the same the first time the loop runs (as it should be). But when I click "step into" in Visual Studio and see how the code executes in the function, the parameter ("rgb") is always completely different to startPixel! I am new to debugging, so I might be interpreting things incorrectly. But now I am even more confused. Here is a picture.

I also should mention the result of the code when it's run. It outputs the Image, but the output picture is just a random single color (entirely blue for example), whereas it should be identical to the input picture.

Pux
  • 421
  • 3
  • 18
  • 1
    You're going to need to show us more code. At a minimum, the definitions of `HsvColor` and `RGBApixel`. We would also need to know what `*Image(i, j)` is doing: If those parentheses are correct, it's obviously not a dumb struct. We don't care about the math in converting HSV to RGB, but we probably do need to know how that method is declared. – David Yaw Oct 03 '17 at 17:48
  • @DavidYaw I added the things you asked for. – Pux Oct 03 '17 at 18:09
  • @Pux You should add code for `HsvToRgb` and `RgbToHsv` functions. Also implementation details of `operator()(int i, int j)` is needed. – MKR Oct 03 '17 at 19:22
  • @MKR Alright, I added that too. Thanks for helping. – Pux Oct 03 '17 at 19:48
  • @Pus What is declaration of `Pixels`? – MKR Oct 03 '17 at 20:15
  • @MKR Added it to the post. – Pux Oct 03 '17 at 20:22
  • @Pus Everything looks fine except the loop where `Image` is declared and operations are made. Please share the code of that loop as well. – MKR Oct 03 '17 at 21:06
  • OK, I added some more code from the original function. – Pux Oct 03 '17 at 21:27
  • @MKR I added some more information. – Pux Oct 04 '17 at 08:25
  • You are likely battling a calling convention mismatch. The default for code compiled by the C++/CLI compiler is `__clrcall`, that is redrum on C functions that were built with `__cdecl`. You have to let the compiler know that the other code was not built the same way. We can't see the #includes you use, consider putting #pragma managed(push, off) before them and #pragma managed(pop) after them. – Hans Passant Oct 04 '17 at 09:15
  • Oh, that was it! I deleted all my #pragma managed etc. commands and set instead all the cpp files that should be compiled as native code to compile without clr like you wrote [here](https://stackoverflow.com/questions/18037232/c-cli-correct-way-to-use-pragma-managed-unmanaged), and now it doesn't crash anymore. Thank you so much! – Pux Oct 04 '17 at 10:06

1 Answers1

1

The exact problem was the one @HansPassant described in his comment:

You are likely battling a calling convention mismatch. The default for code compiled by the C++/CLI compiler is __clrcall, that is redrum on C functions that were built with __cdecl. You have to let the compiler know that the other code was not built the same way. We can't see the #includes you use, consider putting #pragma managed(push, off) before them and #pragma managed(pop) after them.

What I did to solve the problem was deleting all #pragma unmanaged/#pragma managed lines and instead setting all cpp files that should be compiled in native code to not support clr like he described here.

Pux
  • 421
  • 3
  • 18