2

In C# program I have an implementation of the following function:

int Test();

Which is defined in the native part of a program (which is written in C++) as follows:

HRESULT Test();

or possibly:

DWORD Test();

So while implementing the functions I need to return HRESULT and other standard Windows codes from the C# part. At this moment I've implemented it as follows:

public int Test()
{
  return 0x00000102; // WAIT_TIMEOUT
}

The question is how can I use the human-readable codes in C#? I want to write something like: return WAIT_TIMEOUT; in the same manner as I can do it in C++.

I'm seeking a solution which doesn't require to download external libraries or something like that. Only standard .NET.

And by the way WAIT_TIMEOUT isn't a HRESULT, but I hope you understand the question - the question isn't about exceptional situations, but about standard Windows constants. I'm just trying to use standard Windows defines in C# (E_NOTIMPL, WAIT_OBJECT_0, S_FALSE, etc.)

Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • 2
    Use a Enumeration or Constants. – Florian Sep 15 '15 at 10:55
  • 1
    The C++ function `HRESULT Test` generally corresponds to C# `void Test`, with an exception. It does depend on the specified contract, but absent other documentation the C++ side contract is that failure is communicated by a negative `HRESULT`. And in C#, that failure is communicated via an exception. – Cheers and hth. - Alf Sep 15 '15 at 11:02
  • @thefiloe, I glad to use them, but I can't find them for C#. Should I define each constant by hand? I thought Microsoft already done this job for .NET. – Kirill V. Lyadvinsky Sep 15 '15 at 11:03
  • I've modified the question a little. I'm trying to find a way (that is suitable for both situations) to use all these defines which are already exist in PSDK headers. Is it possible in C#? – Kirill V. Lyadvinsky Sep 15 '15 at 11:22
  • `S_FALSE` is generally not used, but where it's used (e.g. by `CoInitializeEx`) it corresponds to a boolean return, or, it can just be ignored. Worth keeping in mind that it's numerically identical to an error code, I think it was file not found or something like that. To avoid such conflation you can wrap simple error codes as `HRESULT`s; there is a dedicated facility for that, and a C++ side macro to do it. – Cheers and hth. - Alf Sep 15 '15 at 11:30
  • Sorry, it's `S_TRUE` that (in a problematic way) is the same value as a Windows error code. Anyway. ;-) – Cheers and hth. - Alf Sep 15 '15 at 11:41
  • 1
    @Cheers: `S_FALSE` is used very frequently. All `IEnumXxx` interfaces (like [IEnumString](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687257.aspx)) use it as a sentinel, to report that the end of the controlled sequence has been reached (see [IEnumString::Next](https://msdn.microsoft.com/en-us/library/windows/desktop/ms693735.aspx) for example). – IInspectable Sep 15 '15 at 15:02

3 Answers3

3
   return 0x00000102; // WAIT_TIMEOUT

You need to start by fixing your code, that is not a proper HRESULT. They have the high bit set to indicate that an error condition was encountered. Right now your C++ code will assume that your C# method did not fail, normally done by using the SUCCEEDED() or FAILED() macros.

Have a look-see at the WinError.h SDK include file. It documents the structure of an HRESULT and has definitions for the well-known winapi error codes and HRESULTs. It also has a definition for HRESULT_FROM_WIN32(), a helper macro that packages a win32 error code into an appropriate HRESULT.

You can do the same in C# code like this:

static class Win32Interop { 
    const int WAIT_TIMEOUT = 0x102;

    static void ThrowWin32Error(int err) {
        unchecked {
            int hr = (int)0x80070000 + err;
            System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr);
        }
    }
}

Now you can write:

public void test() {
    ThrowWin32Error(WAIT_TIMEOUT);
}

Doing it in this seemingly elaborate way ensures that the exception has a reasonably descriptive Message property and that you can provide richer error info in your C++ code beyond the stark HRESULT value. Use the IErrorInfo interface to do so. Also note that many built-in .NET exceptions already have the proper HResult value. So simply throwing that exception is enough. Not otherwise simple to discover, you'd have to look at the Reference Source.

Do keep in mind that using Win32 error codes that were not actually produced by win32 functions is very misleading. Anybody that tries to diagnose 0x80070102 will always look for a failing operating system call. If your C# code doesn't have one then pretty serious head-scratching ensues. I will therefore recommend you do it this way:

public void test() {
    throw new TimeoutException();
}

Which produces COR_E_TIMEOUT (0x80131505) in your C++ code. The kind of HRESULTs you need to get familiar with anyway when you call C# code. These error codes are defined in the CorError.h SDK include file.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    Sharp eye on the `HRESULT` value not being valid. – MicroVirus Sep 15 '15 at 11:33
  • "WinError.h": the way I use to look at it is type "#include " in Visual Studio, right click the header name, and open the header. Can this be done in a simpler way? I think, it should not be *necessary* to type the "#include" part. – Cheers and hth. - Alf Sep 15 '15 at 11:36
  • Looks good. Tried `System.ComponentModel.Win32Exception(WAIT_TIMEOUT)` and got E_FAIL in the C++ code as a result. What I'm doing wrong? – Kirill V. Lyadvinsky Sep 15 '15 at 11:58
  • @KirillV.Lyadvinsky: are you calling the C# function from C++, and if so, exactly how? [Also btw. sorry, I've removed my earlier comment to your question. It was evidently based on an incorrect impression.] – Cheers and hth. - Alf Sep 15 '15 at 12:06
  • And as I understand there's no standard .NET class which contains constants defined for the standard PSDK macros? – Kirill V. Lyadvinsky Sep 15 '15 at 12:06
  • @Cheersandhth.-Alf, I pass the pointer to a function as a callback to the C++ function. After that C++ code can call that function. That's in short. And by the way, `throw new TimeoutException();` works as expected. – Kirill V. Lyadvinsky Sep 15 '15 at 12:10
  • @KirillV.Lyadvinsky: Well, that intended usage appears to make this answer mostly irrelevant. So, to be clear, you want to return an `HRESULT` or `DWORD` from C# function to C++ code, and for this you would rather have named constants, like the VSConstants, available in C#? – Cheers and hth. - Alf Sep 15 '15 at 12:15
  • @Cheersandhth.-Alf, exactly! The problem is that `VSConstants` doesn't contain everything. – Kirill V. Lyadvinsky Sep 15 '15 at 12:18
  • Sorry, Win32Exception does do what I hoped it does, it doesn't set the HResult property from the error code. Post updated. – Hans Passant Sep 15 '15 at 12:27
  • Anyway I need to get `0x102` in the C++ code. I achieved it already. The question is that I don't want to manually define each PSDK code which I use in my C# program. – Kirill V. Lyadvinsky Sep 15 '15 at 12:35
  • Well, that's why I recommended you don't do that. Using a win32 error code for code that doesn't actually make a Win32 api call is very misleading anyway. Do use exceptions for exceptional cases and not for flow control. If timeouts are very likely then simply have the method return a bool instead. – Hans Passant Sep 15 '15 at 12:37
2

I think you're looking for WinAPI defines for C#. Not sure if this is enough, but overall afair this site has some other defines and stuff like that for C#.

Ivan0x32
  • 413
  • 1
  • 7
  • 18
  • I see only Window message constants there, not HRESULT names? – Cheers and hth. - Alf Sep 15 '15 at 11:00
  • Can't find `WAIT_TIMEOUT` there. – Kirill V. Lyadvinsky Sep 15 '15 at 11:08
  • 1
    Sorry, had to downvote for promoting the crappiest interop site there is. pinvoke.net is usually wrong. It's what you get when you have .NET developers try to wrap their heads around such mundane concepts as alignment constraints. Don't use pinvoke.net. Don't promote pinvoke.net. And don't provide link-only answers on stackoverflow. – IInspectable Sep 15 '15 at 12:33
2

Option 1:

Use an enumeration. You can find some error codes here: Common HRESULT values.

Option 2:

Use the VSConstants class, the error codes are already defined there as int constants.

Option 3:

To be more .NET conform, your public C# methods should rather throw .NET-like exceptions instead of using error codes. To do so, you can either use the Marshal.ThrowExceptionForHR(int) method, or you can do some mapping based on this page.

György Kőszeg
  • 17,093
  • 6
  • 37
  • 65