Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/Tests/UnitTest/ExceptionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using WinRT;
using Xunit;

namespace UnitTest
{
public class ExceptionTests
{
[Fact]
public void TestGetExceptionForHR_WithValidHResult_ReturnsSystemFormattedException()
{
const int RPC_E_WRONG_THREAD = unchecked((int)0x8001010E);

Exception exception = ExceptionHelpers.GetExceptionForHR(RPC_E_WRONG_THREAD);
Assert.NotNull(exception);
Assert.False(string.IsNullOrWhiteSpace(exception.Message));
if (CultureInfo.CurrentUICulture.Name == "en-US")
{
Assert.Equal("The application called an interface that was marshalled for a different thread. (0x8001010E)", exception.Message);
}
}
}
}
23 changes: 22 additions & 1 deletion src/WinRT.Runtime/ExceptionHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,27 @@ private static Exception GetExceptionForHR(int hr, bool useGlobalErrorState, boo
}
}

if (string.IsNullOrWhiteSpace(errorMessage))
{
char* message = default;
if (Platform.FormatMessageW(0x13FF /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK */,
null,
(uint)hr,
0,
&message,
0,
null) > 0)
{
errorMessage = $"{new string(message)}(0x{hr:X8})";

// LocalHandle isn't needed since FormatMessage uses LMEM_FIXED,
// and while we can use Marshal.FreeHGlobal since it uses LocalFree internally,
// it's not guranteed that this behavior stays the same in the future,
// especially considering the method's name, so it's safer to use LocalFree directly.
Platform.LocalFree(message);
}
}

switch (hr)
{
case E_CHANGED_STATE:
Expand Down Expand Up @@ -348,7 +369,7 @@ private static Exception GetExceptionForHR(int hr, bool useGlobalErrorState, boo
break;

default:
ex = new COMException(errorMessage, hr);
ex = !string.IsNullOrEmpty(errorMessage) ? new COMException(errorMessage, hr) : new COMException($"0x{hr:X8}", hr);
break;
}

Expand Down
6 changes: 6 additions & 0 deletions src/WinRT.Runtime/Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ public static extern unsafe int WindowsCreateStringReference(

[DllImport("api-ms-win-core-com-l1-1-0.dll")]
public static extern unsafe int CoCreateFreeThreadedMarshaler(IntPtr outer, IntPtr* marshalerPtr);

[DllImport("kernel32.dll")]
public static extern unsafe uint FormatMessageW(uint dwFlags, void* lpSource, uint dwMessageId, uint dwLanguageId, char** lpBuffer, uint nSize, void* pArguments);

[DllImport("kernel32.dll")]
public static extern unsafe void* LocalFree(void* hMem);
}

// Handcrafted P/Invoke with TFM-specific handling, or thin high-level abstractions (eg. 'TryGetProcAddress'/'GetProcAddress')
Expand Down
2 changes: 1 addition & 1 deletion src/cswinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -5883,7 +5883,7 @@ return (eventSource.Subscribe, eventSource.Unsubscribe);
}
}

std::string get_vmethod_delegate_type(writer& w, MethodDef const& method, std::string vmethod_name)
std::string get_vmethod_delegate_type(writer& w, MethodDef const& method, std::string)
{
method_signature signature{ method };
if (is_special(method))
Expand Down