Menu

Problems using TListViewCtrl

gobrad
2023-01-02
2023-01-05
  • gobrad

    gobrad - 2023-01-02

    Hello

    I am having some problems using TListViewCtrl and I hope that someone can help...

    My environment

    OWLNext 6.44, Visual Studio 2019, 64-bit, fairly mature application using OWLNext for a long time.

    My problem

    I am trying to integrate TListViewCtrl in some of dialogs I am using. To do so, I've took a look at examples ("classes\listviewwnd.cpp" and some online resources, like "List View Control tips and tricks"). My implementation looks almost the same as in the "classes" example. The only difference I see is that I am embedding TListViewCtrl in a TDialog instead in a plain TWindow.

    In the SetupWindow method of my dialog, on the first call of:

    InsertColumn(0, TLvColumn("Column 1", 80));

    the application crashes with the following callstack:

    std::_Container_base12::_Orphan_all() Line 1203 C++
    std::vector<char,std::allocator<char>>::_Tidy() Line 1750   C++
    std::vector<char,std::allocator<char>>::~vector<char,std::allocator<char>>() Line 701   C++
    owl::TLvColumn::~TLvColumn()    C++
    CalcDlg::SetupWindow() Line 211 C++
    

    Compiled with the same compiler and against the same OWLNext, the "classes" examples work without problems. I've already tried comparing compiler settings in both projects, but did not find any clue.

    Does anybody have an idea what the cause of the problem could be? I would really appreciate any hints or ideas...

    Best regards
    Goran Obradovic


    Moderator: Added links and formatting.

     

    Last edit: Vidar Hasfjord 2023-01-02
    • Ognyan Chernokozhev

      Hello,

      Do you have a minimal example with the problem that you can post, so we can help debugging it?

      Is the ListView defined in the dialog resource, or is it created dynamically?

      In the Classes example, there is a TListViewCtrl control in a dialog - in the Resizable dialog example, and the InsertColumn calls work just fine.

      Jogy

       
      • gobrad

        gobrad - 2023-01-02

        Hello Ognyan,

        Thank you for looking into this.

        Since my source code was actually copy/paste from examples, I thought that minimal example would not make much sense.

        Now I've managed to narrow the problem to this:

        {
          TLvColumn("Column 1", 80);
        }
        

        This anywhere in my application causes a crash in TLvColumn destructor (presumably) during destruction of the "Buffer" member (std::vector<tchar>). So, even without any dialogs, resources, TListViewCtrl etc.

        So, I presume that it must be some compiler/linker option or setting which makes the difference. My application is not built as unicode application, just the same as my examples project.

        It is strange that all other OWLNext classes/functions are working perfectly in the same application.

        Best regards,
        Goran


        Moderator: Fixed formatting.

         

        Last edit: Vidar Hasfjord 2023-01-02
  • Vidar Hasfjord

    Vidar Hasfjord - 2023-01-03

    @gobrad wrote:

    Now I've managed to narrow the problem to this: {TLvColumn("Column 1", 80);}

    Since you can reproduce the crash by putting this code anywhere in your application, while the Classes example works fine — even with the exact same code, presumably — there may, as you say, be a mismatch in compiler settings somewhere.

    One possibility I can see is that you have a mismatch in the Microsoft C++ runtime library usage in your application and OWLNext. While the TLvColumn constructor is implemented as a non-inlined function within OWLNext, TLvColumn has no explicit destructor, so the destruction may be inlined within your application. This becomes an issue if OWLNext uses a different instance of the runtime library than your application.

    In particular, if you link dynamically to the runtime library (see Configuration Properties | C/C++ | Code Generation | Runtime Library), OWLNext must do so as well (which means you must use the DLL build option). And the same goes for static linking (preferred).

    Also, make sure that you have rebuilt OWLNext, if you have upgraded Visual Studio since the last build, to ensure your application and OWLNext both use the same version of the runtime library.

     

    Last edit: Vidar Hasfjord 2023-01-03
    • gobrad

      gobrad - 2023-01-03

      Vidar, thank you for your hints.

      I've tried adding a destructor in TLvColumn, and it really fixed the problem. So, your assumption was correct.

      All my builds use static linking, and after checking several time all compiler/linker settings, I could not find any significant differences between my project and the examples. OWLNext libraries are up-to-date, and all projects are using the same libraries in the same way.

      One more interesting observation: After turning on additional compiler/linker checks (SDL checks), I get the following compiler warning/error on that TLvColumn line:

      Error C4789 buffer 'column' of size 64 bytes will be overrun;
        8 bytes will be written starting at offset 72
        file: C:\...\VC\Tools\MSVC\14.29.30133\include\vector
        line: 1756
      

      (Note that 'column' is not my variable.)

      Then, I simply moved my include

      #include "owl/listviewctrl.h"
      

      before all other includes, and it fixed the problem! It turned out that it has to be before the include of "windows.h". As soon as I put it afterwards, I get the same error again.

      Of course, that is not a real solution, and shows that there is some deeper problem. Btw, changing include order in the owl example project the same way, did not make any difference. I presume that the problem is the size of Windows LVCOLUMN class (base class of TLvColumn). Its size depends on some defines, which are changed inside "windows.h". So, the code generated before the "windows.h" include does not match the code created afterwards.

      Best regards
      Goran


      Moderator: Fixed formatting, spelling.

       
      👍
      1

      Last edit: Vidar Hasfjord 2023-01-03
  • Vidar Hasfjord

    Vidar Hasfjord - 2023-01-03

    @gobrad wrote:

    It turned out [that OWL headers must be included] before the include of "windows.h".

    Ah, yes, that is an unwritten rule! You have to let OWL include "windows.h".

    The OWL headers will include "windows.h" (indirectly, usually by including "owl/defs.h" directly or indirectly, which in turn includes "owl/wsysinc.h", which includes "owl/private/wsysinc.h", which includes "windows.h"). OWL defines a few Windows macros that affect how "windows.h" is included. It also enforces 8-byte packing.

    Of course, that is not a real solution, and shows that there is some deeper problem.

    The problem is that the difference in include order changes the definition of the entities the compiler sees at different points — in this case, within the OWLNext library and within your application — hence breaking the C++ One Definition Rule. By ensuring that "windows.h" is configured and included the same way everywhere, with identical compiler settings for code generation, you prevent this particular breakage of this general rule.

    I will add FAQ items to explain this.

    Edit: This issue is now explained in the FAQ:

     

    Last edit: Vidar Hasfjord 2023-01-03
    • gobrad

      gobrad - 2023-01-05

      Thanks Vidar!

      I investigated the problem a bit further: The most important difference was that my project uses:

      #define _WIN32_WINNT  _WIN32_WINNT_VISTA
      

      and OWLNext uses (in wsysinc.h):

      #define _WIN32_WINNT _WIN32_WINNT_WINXP
      

      I am not sure if the logic in wsysinc.h is compatible with the way Windows SDK defines _WIN32_WINNT:
      If you just install the latest SDK and compile OWLNext and a simple application without setting _WIN32_WINNT explicitly, OWLNExt will default to _WIN32_WINNT_WINXP and the application to _WIN32_WINNT_WIN10 (see sdkddkver.h from Windows SDK). Wouldn't it be better if wsysinc.h would use SDK headers to set the default. What do you think?

      Best regards
      Goran

       
  • Vidar Hasfjord

    Vidar Hasfjord - 2023-01-05

    @gobrad wrote:

    [e.g. with the the latest SDK] OWLNext will default to _WIN32_WINNT_WINXP and the application to _WIN32_WINNT_WIN10

    That should not be the case for a properly written OWL application, which should let OWL include "windows.h", as per the unwritten rule. Both the OWLNext library and the application will use the Windows API version selected by OWL.

    Wouldn't it be better if "wsysinc.h" would use [...] the default. What do you think?

    OWLNext needs to set the Windows API version it wants to use, because we need consistent results across a wide range of compilers (especially for OWLNext versions prior to series 7).

    Anyway, the Windows API version cannot be changed on our release branches, since it is an API break, and hence will require a major version number bump (according to the sound semantic versioning we use). However, we may choose to select a later Windows API version (than Vista) on the trunk (OWLNext 8, in development).

    PS. OWLNext 7 dropped XP support and now uses the Vista version of the Windows API. See our release history.

     

    Related

    Wiki: OWLNext_Roadmap_and_Prereleases
    Wiki: OWLNext_Stable_Releases

Anonymous
Anonymous

Add attachments
Cancel