user5462062
user5462062

Reputation:

Implement function from C++ in C# (MAKE_HRESULT - Windows function)

I have such code in C++

#define dppHRESULT(Code) \
    MAKE_HRESULT(1, 138, (Code))

        long x = dppHRESULT(101);

result being x = -2138439579.

MAKE_HRESULT is a windows function and defined as

#define MAKE_HRESULT(sev,fac,code) \
    ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )

I need to replicate this in C#. So I wrote this code:

  public static long MakeHResult(uint facility, uint errorNo)
        {
            // Make HR
            uint result = (uint)1 << 31;
            result |= (uint)facility << 16;
            result |=  (uint)errorNo;

            return (long) result;
        }

And call like:

    // Should return type be long actually??
    long test = DppUtilities.MakeHResult(138, 101);

But I get different result, test = 2156527717.

Why? Can someone please help me replicate that C++ function also in C#? Such that I get similar output on similar inputs?


Alternative implementation.

If I use this implementation

  public static long MakeHResult(ulong facility, ulong errorNo)
        {
            // Make HR
            long result = (long)1 << 31;
            result |= (long)facility << 16;
            result |= (long)errorNo;

            return (long) result;
        }

this works on input 101. But if I input -1, then C++ returns -1 as result while C# returns 4294967295. Why?

I would really appreciate some help as I am stuck with it.

Upvotes: 0

Views: 1035

Answers (3)

A.Konzel
A.Konzel

Reputation: 1980

I've rewritten the function to be the C# equivalent.

static int MakeHResult(uint facility, uint errorNo)
{
    // Make HR
    uint result = 1U << 31;
    result |= facility << 16;
    result |= errorNo;

    return unchecked((int)result);
}

C# is more strict about signed/unsigned conversions, whereas the original C code didn't pay any mind to it. Mixing signed and unsigned types usually leads to headaches.

As Ben Voigt mentions in his answer, there is a difference in type naming between the two languages. long in C is actually int in C#. They both refer to 32-bit types.

The U in 1U means "this is an unsigned integer." (Brief refresher: signed types can store negative numbers, unsigned types cannot.) All the arithmetic in this function is done unsigned, and the final value is simply cast to a signed value at the end. This is the closest approximation to the original C macro posted.

unchecked is required here because otherwise C# will not allow you to convert the value if it's out of range of the target type, even if the bits are identical. Switching between signed and unsigned will generally require this if you don't mind that the values differ when you deal with negative numbers.

Upvotes: 1

Ben Voigt
Ben Voigt

Reputation: 283614

In Windows C++ compilers, long is 32-bits. In C#, long is 64-bits. Your C# conversion of this code should not contain the type keyword long at all.

SaxxonPike has provided the correct translation, but his explanation(s) are missing this vital information.

Your intermediate result is a 32-bit unsigned integer. In the C++ version, the cast is to a signed 32-bit value, resulting in the high bit being reinterpreted as a sign bit. SaxxonPike's code does this as well. The result is negative if the intermediate value had its most significant bit set.

In the original code in the question, the cast is to a 64-bit signed version, which preserves the old high bit as a normal binary digit, and adds a new sign bit (always zero). Thus the result is always positive. Even though the low 32-bits exactly match the 32-bit result in C++, in the C# version returning long, what would be the sign bit in C++ isn't treated as a sign bit.

In the new attempt in the question, the same thing happens (sign bit in the 64-bit number is always zero), but it happens in intermediate calculations instead of at the end.

Upvotes: 1

A.Konzel
A.Konzel

Reputation: 1980

You're calculating it inside an unsigned type (uint). So shifts are going to behave accordingly. Try using int instead and see what happens.

The clue here is that 2156527717 as an unsigned int is the same as -2138439579 as a signed int. They are literally the same bits.

Upvotes: 0

Related Questions