4

What is the c++ approach for upgrading these c style casts for HtmlHelp API?

void CMeetingScheduleAssistantApp::DisplayHelpTopic(CString strTopic)
{
    CString strURL = _T("https://help-msa.publictalksoftware.co.uk/") + strTopic;

    if (theApp.UseDownloadedHelpDocumentation())
    {
        // CHM files use 3 letter suffix
        strTopic.Replace(_T(".html"), _T(".htm"));
        HtmlHelp((DWORD_PTR)(LPCTSTR)strTopic, HH_DISPLAY_TOPIC);
    }
    else
        ShellExecute(nullptr, nullptr, strURL, nullptr, nullptr, SW_SHOWDEFAULT);
}

Specifically:

HtmlHelp((DWORD_PTR)(LPCTSTR)strTopic, HH_DISPLAY_TOPIC);


In answer to the question in the comments:

enter image description here

It is odd because the official docs for x86 HtmlHelpA / HtmlHelpW do indicate different calls.

It doesn't seem to be listed as part of the CWinApp class in the docs.

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • Isn't the `HtmlHelp` call missing two parameters? – Ted Lyngmo Nov 25 '21 at 17:07
  • @TedLyngmo This is how my app has fired the help for years. Works every time. So I don't need to add any new paramaters. Just to update the way the casting is done. – Andrew Truckle Nov 25 '21 at 17:10
  • This is typical Windows stuff. Best to stick with the idioms that were formed thirty years ago. – Pete Becker Nov 25 '21 at 17:10
  • @PeteBecker So leave it as it is and add a pragma suppression – Andrew Truckle Nov 25 '21 at 17:11
  • 2
    @AndrewTruckle Can you click on `HtmlHelp` and _go to definition_ to see what the definition actually is? Is it a macro? If so, to what? – Ted Lyngmo Nov 25 '21 at 17:12
  • 2
    `HtmlHelp` has been overridden either in a MFC class or one of the application base classes of `CMeetingScheduleAssistantApp` So I think there is no point in recommending any "fix" until we see the actual function signature. The first parameter of the Win32API version of `HtmlHelp` is a (optional) `HWND` see - https://learn.microsoft.com/en-us/windows/win32/api/htmlhelp/nf-htmlhelp-htmlhelpa – Richard Critten Nov 25 '21 at 17:16
  • @RichardCritten not overridden but either class has the method HtmlHelp. Having a method is not overriding or overloading a free function. – 273K Nov 25 '21 at 17:22
  • @S.M. you are right bad choice of language. Just out of time for an edit. – Richard Critten Nov 25 '21 at 17:23
  • My biggest bet right now is on `reinterpret_cast(static_cast(strTopic))` - but seeing the definition of `HtmlHelp` would ... help – Ted Lyngmo Nov 25 '21 at 17:27
  • @S.M. I wouldn't call function-style casts _better_. They do exactly the same as the C style casts so I doubt those will get rid of the warnings. The explicit casts are generally considered _better_. "_The functional cast expression ... is exactly equivalent to the corresponding C-style cast expression._" [cppreference](https://en.cppreference.com/w/cpp/language/explicit_cast) – Ted Lyngmo Nov 25 '21 at 17:41
  • @S.M. I'm wrong about a lot of things. Which thing is it this time? :-) – Ted Lyngmo Nov 25 '21 at 17:42
  • @S.M. Because it tells the compiler that that is exactly what you want, so the warning is removed. I also used a `static_cast` where I _think_ it's appropriate. Even if I used `reinterpret_cast` in both places it's better because it tells the compiler and a reader of the code that, yes, I do want this brute cast. – Ted Lyngmo Nov 25 '21 at 17:48
  • @Ted you tell the same with using function style casts. – 273K Nov 25 '21 at 17:50
  • @S.M. Sorry, I don't understand. A function-style cast is exactly the same as a C style cast. It doesn't provide any more information than the C style cast - which is what generates the warning. Perhaps a functions-style cast _is_ enough to make the warning go away - but it doesn't tell a reader of the code (human or machine) anything more than the C style cast does. – Ted Lyngmo Nov 25 '21 at 17:54

2 Answers2

4

reinterpret_cast will always trigger a code analysys warning

In that case, you could invent your own pointer cast function. A smart compiler will most probably optimize this away:

#include <cstring>

template<class D, class T>
D ptr_cast(T* x) {
    D rv;
    static_assert(sizeof rv == sizeof x);
    std::memcpy(&rv, &x, sizeof rv);
    return rv;
}

Then

HtmlHelp( ptr_cast<DWORD_PTR>(strTopic.GetString()),
          HH_DISPLAY_TOPIC);

In g++ and clang++, explicitly instantiating the function

template DWORD_PTR ptr_cast<DWORD_PTR,char>(char*);

makes it into the assembly code

unsigned long ptr_cast<unsigned long, char>(char*):
        mov     rax, rdi
        ret

When inlined and optimized (as it will be in your code) the function call is gone and only the mov (assignment) to the destination DWORD_PTR is left, which is exactly the trace a reinterpret_cast would leave. In MSVC, inlining it results in the similar:

lea     rdx, OFFSET FLAT:`string'
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
1

(LPCTSTR)strTopic calls CString::operator LPCTSTR(). Then (DWORD_PTR) converts pointer LPCTSTR to DWORD_PTR. There is no possible direct conversion from CString object to DWORD_PTR due to different source and target sizes.

C++ type casts can be the same short length as C type casts

HtmlHelp(DWORD_PTR(LPCTSTR(strTopic)), HH_DISPLAY_TOPIC);

MFC CWnd and CWinApp classes have the methods, that get the first parameter of the type DWORD_PTR

virtual void HtmlHelp(
    DWORD_PTR dwData,
    UINT nCmd = 0x000F);
273K
  • 29,503
  • 10
  • 41
  • 64