Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert exception help context parsing to managed #70251

Merged
merged 15 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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
27 changes: 27 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,5 +274,32 @@ private bool CanSetRemoteStackTrace()

return true;
}

// used by vm
internal string? GetHelpContext(out uint helpContext)
{
helpContext = 0;
string? helpFile = HelpLink;

int poundPos, digitEnd;

if (helpFile is null || (poundPos = helpFile.LastIndexOf('#')) == -1)
{
return helpFile;
}

for (digitEnd = poundPos + 1; digitEnd < helpFile.Length; digitEnd++)
{
if (char.IsWhiteSpace(helpFile[digitEnd]))
break;
}

if (uint.TryParse(helpFile.AsSpan(poundPos + 1, digitEnd - poundPos - 1), out helpContext))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be culture invariant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK the default format of uint32 isn't affected by culture at all. Negative sign, digit separator and decimal point are not allowed.

{
helpFile = helpFile.Substring(0, poundPos);
}

return helpFile;
}
}
}
141 changes: 8 additions & 133 deletions src/coreclr/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,69 +41,6 @@

#include "arraynative.inl"

/*===================================IsDigit====================================
**Returns a bool indicating whether the character passed in represents a **
**digit.
==============================================================================*/
bool IsDigit(WCHAR c, int radix, int *result)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(result));
}
CONTRACTL_END;

if (IS_DIGIT(c)) {
*result = DIGIT_TO_INT(c);
}
else if (c>='A' && c<='Z') {
//+10 is necessary because A is actually 10, etc.
*result = c-'A'+10;
}
else if (c>='a' && c<='z') {
//+10 is necessary because a is actually 10, etc.
*result = c-'a'+10;
}
else {
*result = -1;
}

if ((*result >=0) && (*result < radix))
return true;

return false;
}

INT32 wtoi(_In_reads_(length) WCHAR* wstr, DWORD length)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(wstr));
PRECONDITION(length >= 0);
}
CONTRACTL_END;

DWORD i = 0;
int value;
INT32 result = 0;

while ( (i < length) && (IsDigit(wstr[i], 10 ,&value)) ) {
//Read all of the digits and convert to a number
result = result*10 + value;
i++;
}

return result;
}



//
//
// EXCEPTION NATIVE
Expand Down Expand Up @@ -371,79 +308,17 @@ static void GetExceptionHelp(OBJECTREF objException, BSTR *pbstrHelpFile, DWORD

GCPROTECT_BEGIN(objException);

// read Exception.HelpLink property
MethodDescCallSite getHelpLink(METHOD__EXCEPTION__GET_HELP_LINK, &objException);
// call managed code to parse help context
MethodDescCallSite getHelpContext(METHOD__EXCEPTION__GET_HELP_CONTEXT, &objException);

ARG_SLOT GetHelpLinkArgs[] = { ObjToArgSlot(objException)};
*pbstrHelpFile = BStrFromString(getHelpLink.Call_RetSTRINGREF(GetHelpLinkArgs));
ARG_SLOT GetHelpContextArgs[] =
{
ObjToArgSlot(objException),
PtrToArgSlot(pdwHelpContext)
};
*pbstrHelpFile = BStrFromString(getHelpContext.Call_RetSTRINGREF(GetHelpContextArgs));

GCPROTECT_END();

// parse the help file to check for the presence of helpcontext
int len = SysStringLen(*pbstrHelpFile);
int pos = len;
WCHAR *pwstr = *pbstrHelpFile;
if (pwstr) {
BOOL fFoundPound = FALSE;

for (pos = len - 1; pos >= 0; pos--) {
if (pwstr[pos] == W('#')) {
fFoundPound = TRUE;
break;
}
}

if (fFoundPound) {
int PoundPos = pos;
int NumberStartPos = -1;
BOOL bNumberStarted = FALSE;
BOOL bNumberFinished = FALSE;
BOOL bInvalidDigitsFound = FALSE;

_ASSERTE(pwstr[pos] == W('#'));

// Check to see if the string to the right of the pound a valid number.
for (pos++; pos < len; pos++) {
if (bNumberFinished) {
if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
bInvalidDigitsFound = TRUE;
break;
}
}
else if (bNumberStarted) {
if (COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
bNumberFinished = TRUE;
}
else if (!COMCharacter::nativeIsDigit(pwstr[pos])) {
bInvalidDigitsFound = TRUE;
break;
}
}
else {
if (COMCharacter::nativeIsDigit(pwstr[pos])) {
NumberStartPos = pos;
bNumberStarted = TRUE;
}
else if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
bInvalidDigitsFound = TRUE;
break;
}
}
}

if (bNumberStarted && !bInvalidDigitsFound) {
// Grab the help context and remove it from the help file.
*pdwHelpContext = (DWORD)wtoi(&pwstr[NumberStartPos], len - NumberStartPos);

// Allocate a new help file string of the right length.
BSTR strOld = *pbstrHelpFile;
*pbstrHelpFile = SysAllocStringLen(strOld, PoundPos);
SysFreeString(strOld);
if (!*pbstrHelpFile)
COMPlusThrowOM();
}
}
}
}

// NOTE: caller cleans up any partially initialized BSTRs in pED
Expand Down
6 changes: 4 additions & 2 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,13 @@ DEFINE_FIELD_U(_xptrs, ExceptionObject, _xptrs)
DEFINE_FIELD_U(_xcode, ExceptionObject, _xcode)
DEFINE_FIELD_U(_HResult, ExceptionObject, _HResult)
DEFINE_CLASS(EXCEPTION, System, Exception)
DEFINE_METHOD(EXCEPTION, INTERNAL_PRESERVE_STACK_TRACE, InternalPreserveStackTrace, IM_RetVoid)
#ifdef FEATURE_COMINTEROP
DEFINE_METHOD(EXCEPTION, GET_CLASS_NAME, GetClassName, IM_RetStr)
DEFINE_PROPERTY(EXCEPTION, MESSAGE, Message, Str)
DEFINE_PROPERTY(EXCEPTION, SOURCE, Source, Str)
DEFINE_PROPERTY(EXCEPTION, HELP_LINK, HelpLink, Str)
DEFINE_METHOD(EXCEPTION, INTERNAL_PRESERVE_STACK_TRACE, InternalPreserveStackTrace, IM_RetVoid)
DEFINE_METHOD(EXCEPTION, GET_HELP_CONTEXT, GetHelpContext, IM_RefInt_RetStr)
#endif


DEFINE_CLASS(SYSTEM_EXCEPTION, System, SystemException)
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/metasig.h
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,9 @@ DEFINE_METASIG(SM(Str_RetArrStr, s, a(s)))
// Execution Context
DEFINE_METASIG_T(SM(SyncCtx_ArrIntPtr_Bool_Int_RetInt, C(SYNCHRONIZATION_CONTEXT) a(I) F i, i))

// Exception
DEFINE_METASIG(IM(RefInt_RetStr, r(i), s))

#ifdef FEATURE_COMINTEROP
// The signature of the method System.Runtime.InteropServices.ICustomQueryInterface.GetInterface
DEFINE_METASIG_T(IM(RefGuid_OutIntPtr_RetCustomQueryInterfaceResult, r(g(GUID)) r(I), g(CUSTOMQUERYINTERFACERESULT)))
Expand Down
17 changes: 0 additions & 17 deletions src/coreclr/vm/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1982,23 +1982,6 @@ BOOL COMCharacter::nativeIsWhiteSpace(WCHAR c)
#endif // !TARGET_UNIX
}

/*================================nativeIsDigit=================================
**The locally available version of IsDigit. Designed to be called by other
**native methods. The work is mostly done by GetCharacterInfoHelper
**Args: c -- the character to check.
**Returns: true if c is whitespace, false otherwise.
**Exceptions: Only those thrown by GetCharacterInfoHelper.
==============================================================================*/
BOOL COMCharacter::nativeIsDigit(WCHAR c)
{
WRAPPER_NO_CONTRACT;
#ifndef TARGET_UNIX
return((GetCharacterInfoHelper(c, CT_CTYPE1) & C1_DIGIT)!=0);
#else // !TARGET_UNIX
return iswdigit(c);
#endif // !TARGET_UNIX
}

BOOL RuntimeFileNotFound(HRESULT hr)
{
LIMITED_METHOD_CONTRACT;
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/vm/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,6 @@ class COMCharacter {
public:
//These are here for support from native code. They are never called from our managed classes.
static BOOL nativeIsWhiteSpace(WCHAR c);
static BOOL nativeIsDigit(WCHAR c);
};

// ======================================================================================
Expand Down
27 changes: 18 additions & 9 deletions src/tests/Interop/COM/NETClients/Primitives/ErrorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public void Run()
{
this.VerifyExpectedException();
this.VerifyReturnHResult();
this.VerifyHelpLink();
}

private void VerifyExpectedException()
Expand All @@ -40,21 +41,29 @@ private void VerifyReturnHResult()

var hrs = new[]
{
unchecked((int)0x80004001),
unchecked((int)0x80004003),
unchecked((int)0x80070005),
unchecked((int)0x80070057),
unchecked((int)0x8000ffff),
-1,
1,
2
};
unchecked((int)0x80004001),
unchecked((int)0x80004003),
unchecked((int)0x80070005),
unchecked((int)0x80070057),
unchecked((int)0x8000ffff),
-1,
1,
2
};

foreach (var hr in hrs)
{
Assert.Equal(hr, this.server.Return_As_HResult(hr));
Assert.Equal(hr, this.server.Return_As_HResult_Struct(hr).hr);
}
}

private void VerifyHelpLink()
{
string helpLink = "C:\\Windows\\system32\\dummy.hlp";
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
uint helpContext = 5678;
var ex = Assert.Throws<COMException>(() => { this.server.Throw_HResult_HelpLink(unchecked((int)-1), helpLink, helpContext); });
Assert.Equal($"{helpLink}#{helpContext}", ex.HelpLink);
}
}
}
9 changes: 9 additions & 0 deletions src/tests/Interop/COM/NETServer/ErrorMarshalTesting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,13 @@ public Server.Contract.HResult Return_As_HResult_Struct(int hresultToReturn)
{
return new Server.Contract.HResult { hr = hresultToReturn };
}

public void Throw_HResult_HelpLink(int hresultToReturn, string helpLink, uint helpContext)
{
Marshal.GetExceptionForHR(hresultToReturn);

Exception e = Marshal.GetExceptionForHR(hresultToReturn);
e.HelpLink = $"{helpLink}#{helpContext}";
throw e;
}
}
43 changes: 43 additions & 0 deletions src/tests/Interop/COM/NativeClients/Primitives/ErrorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,48 @@ namespace
THROW_FAIL_IF_FALSE(hr == hrMaybe);
}
}

void VerifyHelpContext(_In_ IErrorMarshalTesting *et)
{
::printf("Verify expected helplink and context\n");

HRESULT hrs[] =
{
E_NOTIMPL,
E_POINTER,
E_ACCESSDENIED,
E_INVALIDARG,
E_UNEXPECTED,
HRESULT{-1},
S_FALSE,
HRESULT{2}
};

BSTR helpLink = SysAllocString(OLESTR("C:\\Windows\\system32\\dummy.hlp"));
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved

for (int i = 0; i < ARRAY_SIZE(hrs); ++i)
{
HRESULT hr = hrs[i];
DWORD helpContext = (DWORD)(i + 0x1234);
HRESULT hrMaybe = et->Throw_HResult_HelpLink(hr, helpLink, helpContext);
THROW_FAIL_IF_FALSE(hr == hrMaybe);

IErrorInfo* pErrInfo;
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
THROW_IF_FAILED(GetErrorInfo(0, &pErrInfo));

BSTR helpLinkMaybe;
THROW_IF_FAILED(pErrInfo->GetHelpFile(&helpLinkMaybe));
THROW_FAIL_IF_FALSE(VarBstrCmp(helpLink, helpLinkMaybe, LANG_ENGLISH, 0) == VARCMP_EQ);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
SysFreeString(helpLinkMaybe);

DWORD helpContextMaybe;
THROW_IF_FAILED(pErrInfo->GetHelpContext(&helpContextMaybe));
THROW_FAIL_IF_FALSE(helpContext == helpContextMaybe);
pErrInfo->Release();
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
}

SysFreeString(helpLink);
}
}

void Run_ErrorTests()
Expand All @@ -89,4 +131,5 @@ void Run_ErrorTests()
VerifyExpectedException(errorMarshal);
VerifyReturnHResult(errorMarshal);
VerifyReturnHResultStruct(errorMarshal);
VerifyHelpContext(errorMarshal);
}
19 changes: 19 additions & 0 deletions src/tests/Interop/COM/NativeServer/ErrorMarshalTesting.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ class ErrorMarshalTesting : public UnknownImpl, public IErrorMarshalTesting
return hresultToReturn;
}

DEF_FUNC(Throw_HResult_HelpLink)(
/*[in]*/ int hresultToReturn,
/*[in]*/ BSTR helpLink,
/*[in]*/ DWORD helpContext)
{
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
ICreateErrorInfo* pCreateErrInfo;
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
CreateErrorInfo(&pCreateErrInfo);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
pCreateErrInfo->SetHelpFile(helpLink);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
pCreateErrInfo->SetHelpContext(helpContext);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved

IErrorInfo* pErrInfo;
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
pCreateErrInfo->QueryInterface(IID_IErrorInfo, (void**)&pErrInfo);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
SetErrorInfo(0, pErrInfo);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
pErrInfo->Release();
pCreateErrInfo->Release();
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved

return HRESULT{ hresultToReturn };
}

public: // IUnknown
STDMETHOD(QueryInterface)(
/* [in] */ REFIID riid,
Expand Down
2 changes: 2 additions & 0 deletions src/tests/Interop/COM/ServerContracts/Server.Contracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ public interface IErrorMarshalTesting

[PreserveSig]
HResult Return_As_HResult_Struct(int hresultToReturn);

void Throw_HResult_HelpLink(int hresultToReturn, string helpLink, uint helpContext);
}

public enum IDispatchTesting_Exception
Expand Down
Loading