Дельфи, SysListView32. баг

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by raven1992, 24 Nov 2012.

  1. raven1992

    raven1992 New Member

    Joined:
    6 Oct 2011
    Messages:
    56
    Likes Received:
    3
    Reputations:
    0
    Суть бага: API ф-ция CreateWindowEx (в дельфи 6, проверил на 2 компах) отказывается создавать окна класса 'SysListView32', если к проекту не подключёна одна из следующих либ: forms\menus\controls\ImgList. (а если точнее - любая, цепляющая за собой controls.pas)

    Пример (30 строк), в котором убирая комменты можно создать окно любого класса, кроме 'SysListView32'.
    'SysListView32' удастся создать в случае, если раскомментировать последнюю строку.
    (при этом параметр, передаваемый ф-ции ImageList_DragLeave значения не имеет. Ведь она никогда не выполнится. Рекурсия. Брейнфак.)
    Code:
    program test_unit;
    
    uses
       windows, Types, commctrl, classes, sysutils;
    
    {$APPTYPE CONSOLE}
    
    var
       handle_of_this_program:HWND;
       msg:TMSG;
       i:integer;
    
    begin
          handle_of_this_program := GetModuleHandle(nil);
    
          CreateWindowEx(0,'SysListView32', Pchar('title'),WS_VISIBLE or LVS_REPORT or LVS_EDITLABELS or LVS_SHOWSELALWAYS or LVS_SHAREIMAGELISTS or LVS_OWNERDATA,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
      //  CreateWindowEx(0,'SysListView32', Pchar('title'),WS_VISIBLE  ,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
      //  CreateWindowEx(0,'BUTTON', Pchar('title'),WS_VISIBLE  ,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
      //  CreateWindowEx(0,'EDIT', Pchar('title'),WS_VISIBLE  ,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
      //  CreateWindowEx(0,'COMBOBOX', Pchar('title'),WS_VISIBLE  ,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
      //  CreateWindowEx(0,'LISTBOX', Pchar('title'),WS_VISIBLE  ,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
      //  CreateWindowEx(0,'STATIC', Pchar('title'),WS_VISIBLE  ,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
      //  CreateWindowEx(0,'SCROLLBAR', Pchar('title'),WS_VISIBLE  ,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
    
       while GetMessage(Msg, 0, 0, 0) do
       begin
          TranslateMessage(Msg);
          DispatchMessage(Msg);
       end;
     //  if i=strtoint('это условие выполнится, когда рак на горе свистнет') then ImageList_DragLeave(i);
    end.
    Дли чистоты эксперимента, то же самое, написанное без использования библиотек:
    Code:
    program test_unit;
    {$APPTYPE CONSOLE}
    
    type
    
       HWND = type LongWord;
       UINT = type LongWord;
       WPARAM = Longint;
       LPARAM = Longint;
       DWORD = LongWord;
       HMENU = type LongWord;
       BOOL = LongBool;
       LRESULT = Longint;
    
       TPoint = packed record
         X: Longint;
         Y: Longint;
       end;
    
       tMSG = packed record
        hwnd: HWND;
        message: UINT;
        wParam: WPARAM;
        lParam: LPARAM;
        time: DWORD;
        pt: TPoint;
       end;
    
    const
       WS_VISIBLE = $10000000;
    
    var
       handle_of_this_program:HWND;
       msg:TMSG;              //сообщение ф-ции GetMessage
       hlv:HWND;              //хэндл листвью
       i:integer;
    
       //API ф-ции, чтобы не подключать никаких либ
       function CreateWindowEx(dwExStyle: DWORD; lpClassName: PChar;
       lpWindowName: PChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
       hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND; stdcall; external 'user32.dll' name 'CreateWindowExA';
       function GetModuleHandle(lpModuleName: PChar): HMODULE; stdcall; external 'kernel32.dll' name 'GetModuleHandleA';
       function GetMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT): BOOL; stdcall; external 'user32.dll' name 'GetMessageA';
       function TranslateMessage(const lpMsg: TMsg): BOOL; stdcall; external 'user32.dll' name 'TranslateMessage';
       function DispatchMessage(const lpMsg: TMsg): Longint; stdcall; external 'user32.dll'  name 'DispatchMessageA';
       function SendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; external 'user32.dll' name 'SendMessageA';
    
       function ImageList_DragLeave(LockWnd: HWnd): Bool; stdcall; external 'comctl32.dll' name 'ImageList_DragLeave';
    
    begin
       handle_of_this_program := GetModuleHandle(nil);   //получаем хендл программы чтобы создать окно
    
       //листвью "по-минимуму". пока последня строка раскомментирована - создается.
       hlv:=CreateWindowEx(0,'SysListView32', Pchar('title'),WS_VISIBLE  ,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
    
       //пример с обычной кнопкой. Создается как положено
       //hlv:=CreateWindowEx(0,'button', Pchar('title'),WS_VISIBLE,0, 0, 600, 400, 0, 0,handle_of_this_program,nil);
    
       while GetMessage(Msg, 0, 0, 0) do
       begin
          TranslateMessage(Msg);
          DispatchMessage(Msg);
       end;
    
       if (i-1)=(i+1) then ImageList_DragLeave(i);
    end.
    
    ps.
    Столкнулся случайно, когда писал сурс-образец, иллюстрирующий другую ошибку 'SysListView32', с сообщениями LVM_SETCOLUMNORDERARRAY\LVM_GETCOLUMNORDERARRAY. Убил 6 часов на поиски причины. Может кому пригодится:)
     
    #1 raven1992, 24 Nov 2012
    Last edited: 24 Nov 2012
  2. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Нет, на баг не похоже. Не понимаю, в чем причина такого долгого поиска решения проблемы. Если считаешь, что каким то боком тут виновен controls.pas, то просто посмотри что происходит в этом модуле в секциях initialization/finalization. Если окно не создается, то нужно посмотреть код ошибки, многое может сказать. Еще стоит взглянуть в модуль ComCtrls, вроде как этот SysListView32 как раз там используется, ну и вообщем как это все в VCL делается.

    Да и кстати
    Code:
     handle_of_this_program:HWND;
    Ух, можно было конкретизировать
    Code:
    handle_of_this_program_running_on_the_current_computer : HWND;
     
    #2 Jingo Bo, 25 Nov 2012
    Last edited: 25 Nov 2012
  3. raven1992

    raven1992 New Member

    Joined:
    6 Oct 2011
    Messages:
    56
    Likes Received:
    3
    Reputations:
    0
    Jingo
    контролс.пас виновен только тем, что в одной из функций, имеющей глубокую вложенность и принадлежность к формам, может быть вызвана Api ф-ция, к-рую я нашел. (но если не использовать дельфиевский дрэглист, то выполнена она не будет)

    К тестовому сурсу что я написал можно добавить свой "тестовый_сурс_2", и в нём написать ф-цию, которая будет вызывать ImageList_DragLeave(123);
    В первом тестовом сурсе эту функцию ты вызывать не будешь, но листвью начнет появляться.
    Точно такая же стиуация с контролс.пас. Я удалил его инициализацию, а затем - все до одного типы и функции (сначала проделав то же самое с формс.пас), пока не обнаружил то, что заставляло листвью появляться.

    А ошибка при неудачном создании листвью такая:
    Error 1407: Cannot find window class
     
    #3 raven1992, 25 Nov 2012
    Last edited: 25 Nov 2012
  4. alexey-m

    alexey-m Elder - Старейшина

    Joined:
    15 Jul 2009
    Messages:
    518
    Likes Received:
    100
    Reputations:
    37
    Code:
    program test;
    
    uses
       Windows, SysUtils, Messages, commctrl;
    
    
    {$EXTERNALSYM InitCommonControls}
    procedure InitCommonControls; external comctl32 name 'InitCommonControls';
    
    var
      Instance: HWnd;
      wClass: TWndClass;
      Handle, hListView: HWnd;
      msg: TMsg;
      col: LV_COLUMN;
      pItem: TLVItem;
      dwStyle, i: DWORD;
    
    //Процедура выхода из программы
    procedure DoExit;
    begin
      Halt;
    end;
    
    function WindowProc (Hwn,msg,wpr,lpr: longint): longint; stdcall;
    begin
      result:= defwindowproc(hwn,msg,wpr,lpr);
      if msg = WM_DESTROY then DoExit;
      if msg = WM_KEYDOWN then
        if wpr = VK_ESCAPE then DoExit;
    end;
    
    begin
      InitCommonControls;
    
      Instance:= GetModuleHandle(nil);
      wClass.style:= CS_HREDRAW or CS_VREDRAW or CS_CLASSDC;
      wClass.Lpfnwndproc:= @windowproc;
      wClass.Hinstance:= Instance;
      wClass.HbrBackground:= COLOR_BACKGROUND;
      wClass.LpszClassName:= 'DX';
      wClass.Hcursor:= LoadCursor(0,IDC_ARROW);
      RegisterClass(wClass);
    
      Handle:= CreateWindowEx(WS_EX_WINDOWEDGE, 'DX', 'window', $14CF0000,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              400, 300, 0, 0, Instance, nil);
    
      ShowWindow(Handle, SW_SHOW);
      UpdateWindow(Handle);
    
      hListView:= CreateWindowEx(WS_EX_CLIENTEDGE, 'SysListView32', '',
      WS_CHILD or WS_VISIBLE or LVS_REPORT or LVS_EDITLABELS or LVS_SHOWSELALWAYS,
      0, 0, 400, 300, Handle, 0, Instance, nil);
    
      col.mask:= LVCF_FMT or LVCF_WIDTH or LVCF_TEXT or LVCF_SUBITEM;
      col.fmt:= LVCFMT_LEFT;
      col.cx:= 150;
      col.iSubItem:= 0;
      col.pszText:= LPSTR('Column 0');
      ListView_InsertColumn(hListView, 0, col);
    
      dwStyle:= ListView_GetExtendedListViewStyle(hListView);
      dwStyle:= dwStyle or LVS_EX_GRIDLINES;
      ListView_SetExtendedListViewStyle(hListView, dwStyle);
    
      pItem.mask:= LVIF_TEXT;
      pItem.iSubItem:= 0;
    
      for i:= 0 to 5 do begin
        pItem.pszText:= LPSTR('Item ' + IntToStr(i));
        pItem.iItem:= i;
        ListView_InsertItem(hListView, pItem);
      end;
    
      while (GetMessage(msg, 0, 0, 0)) do begin
        translatemessage(msg);
        dispatchmessage(msg);
      end;
    end.
    
    [​IMG]
    Что я делаю не так?
     
    #4 alexey-m, 25 Nov 2012
    Last edited: 25 Nov 2012