Subclassing a Editbox

Windows specific questions.
Post Reply
stephanbrunker
Posts: 62
Joined: Nov 02, 2013 14:57

Subclassing a Editbox

Post by stephanbrunker »

Hello,

in my Windows GUI program I want to include a file drag-and-drop functionality. I have solved the problem for the moment with DragAcceptFiles for the Editbox and a redirection for the WM_DROPFILES message. The msg.hwnd is the handle of the editbox, and i change this in the message loop to the handle of the main Window. I tried to solve this with subclassing the Editbox, but if I use SetWindowLongPTR to define a new Function for the Editbox, SetDlgItem text or anything else which changes the text of the Editbox doesn't work anymore, from the main WindowProc or the EditboxWindowProc. Probably because the WM_SETTEXT message is also rerouted. I want to try SetWindowSubclass, but I couldn't find the include *.bi for this function ...?
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Re: Subclassing a Editbox

Post by MichaelW »

The problem with your subclassed edit control may be that you failed to pass any messages that you do not process to the previous edit control window procedure, see CallWindowProc.

This is somewhat crude, and I’m guessing at what you are trying to do, but it appears to work OK under Windows XP.

Note that this has been edited multiple times since it was first posted.

Code: Select all

//
// The defines below are from winuser.bi, included here to
// support resource definitions that are coded manually.
//
#define WS_BORDER 0x800000
#define WS_CAPTION 0xc00000
#define WS_CHILD 0x40000000
#define WS_CHILDWINDOW 0x40000000
#define WS_CLIPCHILDREN 0x2000000
#define WS_CLIPSIBLINGS 0x4000000
#define WS_DISABLED 0x8000000
#define WS_DLGFRAME 0x400000
#define WS_GROUP 0x20000
#define WS_HSCROLL 0x100000
#define WS_ICONIC 0x20000000
#define WS_MAXIMIZE 0x1000000
#define WS_MAXIMIZEBOX 0x10000
#define WS_MINIMIZE 0x20000000
#define WS_MINIMIZEBOX 0x20000
#define WS_OVERLAPPED 0
#define WS_OVERLAPPEDWINDOW 0xcf0000
#define WS_POPUP 0x80000000
#define WS_POPUPWINDOW 0x80880000
#define WS_SIZEBOX 0x40000
#define WS_SYSMENU 0x80000
#define WS_TABSTOP 0x10000
#define WS_THICKFRAME 0x40000
#define WS_TILED 0
#define WS_TILEDWINDOW 0xcf0000
#define WS_VISIBLE 0x10000000
#define WS_VSCROLL 0x200000
#define MDIS_ALLCHILDSTYLES 1
#define BS_3STATE 5
#define BS_AUTO3STATE 6
#define BS_AUTOCHECKBOX 3
#define BS_AUTORADIOBUTTON 9
#define BS_BITMAP 128
#define BS_BOTTOM 0x800
#define BS_CENTER 0x300
#define BS_CHECKBOX 2
#define BS_DEFPUSHBUTTON 1
#define BS_GROUPBOX 7
#define BS_ICON 64
#define BS_LEFT 256
#define BS_LEFTTEXT 32
#define BS_MULTILINE 0x2000
#define BS_NOTIFY 0x4000
#define BS_OWNERDRAW 0xb
#define BS_PUSHBUTTON 0
#define BS_PUSHLIKE 4096
#define BS_RADIOBUTTON 4
#define BS_RIGHT 512
#define BS_RIGHTBUTTON 32
#define BS_TEXT 0
#define BS_TOP 0x400
#define BS_USERBUTTON 8
#define BS_VCENTER 0xc00
#define BS_FLAT 0x8000
#define CBS_AUTOHSCROLL 64
#define CBS_DISABLENOSCROLL 0x800
#define CBS_DROPDOWN 2
#define CBS_DROPDOWNLIST 3
#define CBS_HASSTRINGS 512
#define CBS_LOWERCASE 0x4000
#define CBS_NOINTEGRALHEIGHT 0x400
#define CBS_OEMCONVERT 128
#define CBS_OWNERDRAWFIXED 16
#define CBS_OWNERDRAWVARIABLE 32
#define CBS_SIMPLE 1
#define CBS_SORT 256
#define CBS_UPPERCASE 0x2000
#define ES_AUTOHSCROLL 128
#define ES_AUTOVSCROLL 64
#define ES_CENTER 1
#define ES_LEFT 0
#define ES_LOWERCASE 16
#define ES_MULTILINE 4
#define ES_NOHIDESEL 256
#define ES_NUMBER 0x2000
#define ES_OEMCONVERT 0x400
#define ES_PASSWORD 32
#define ES_READONLY 0x800
#define ES_RIGHT 2
#define ES_UPPERCASE 8
#define ES_WANTRETURN 4096
#define LBS_DISABLENOSCROLL 4096
#define LBS_EXTENDEDSEL 0x800
#define LBS_HASSTRINGS 64
#define LBS_MULTICOLUMN 512
#define LBS_MULTIPLESEL 8
#define LBS_NODATA 0x2000
#define LBS_NOINTEGRALHEIGHT 256
#define LBS_NOREDRAW 4
#define LBS_NOSEL 0x4000
#define LBS_NOTIFY 1
#define LBS_OWNERDRAWFIXED 16
#define LBS_OWNERDRAWVARIABLE 32
#define LBS_SORT 2
#define LBS_STANDARD 0xa00003
#define LBS_USETABSTOPS 128
#define LBS_WANTKEYBOARDINPUT 0x400
#define SBS_BOTTOMALIGN 4
#define SBS_HORZ 0
#define SBS_LEFTALIGN 2
#define SBS_RIGHTALIGN 4
#define SBS_SIZEBOX 8
#define SBS_SIZEBOXBOTTOMRIGHTALIGN 4
#define SBS_SIZEBOXTOPLEFTALIGN 2
#define SBS_SIZEGRIP 16
#define SBS_TOPALIGN 2
#define SBS_VERT 1
#define SS_BITMAP 14
#define SS_BLACKFRAME 7
#define SS_BLACKRECT 4
#define SS_CENTER 1
#define SS_CENTERIMAGE 512
#define SS_ENHMETAFILE 15
#define SS_ETCHEDFRAME 18
#define SS_ETCHEDHORZ 16
#define SS_ETCHEDVERT 17
#define SS_GRAYFRAME 8
#define SS_GRAYRECT 5
#define SS_ICON 3
#define SS_LEFT 0
#define SS_LEFTNOWORDWRAP 0xc
#define SS_NOPREFIX 128
#define SS_NOTIFY 256
#define SS_OWNERDRAW 0xd
#define SS_REALSIZEIMAGE 0x800
#define SS_RIGHT 2
#define SS_RIGHTJUST 0x400
#define SS_SIMPLE 11
#define SS_SUNKEN 4096
#define SS_WHITEFRAME 9
#define SS_WHITERECT 6
#define SS_USERITEM 10
#define SS_TYPEMASK 0x0000001FL
#define SS_ENDELLIPSIS 0x00004000L
#define SS_PATHELLIPSIS 0x00008000L
#define SS_WORDELLIPSIS 0x0000C000L
#define SS_ELLIPSISMASK 0x0000C000L
#define DS_3DLOOK 4
#define DS_ABSALIGN 1
#define DS_CENTER 0x800
#define DS_CENTERMOUSE 4096
#define DS_CONTEXTHELP 0x2000
#define DS_CONTROL 0x400
#define DS_FIXEDSYS 8
#define DS_LOCALEDIT 32
#define DS_MODALFRAME 128
#define DS_NOFAILCREATE 16
#define DS_NOIDLEMSG 256
#define DS_SETFONT 64
#define DS_SETFOREGROUND 512
#define DS_SYSMODAL 2
#define DS_SHELLFONT (64 or 8)
#define WS_EX_ACCEPTFILES 16
#define WS_EX_APPWINDOW 0x40000
#define WS_EX_CLIENTEDGE 512
#define WS_EX_COMPOSITED 0x2000000
#define WS_EX_CONTEXTHELP 0x400
#define WS_EX_CONTROLPARENT 0x10000
#define WS_EX_DLGMODALFRAME 1
#define WS_EX_LAYERED 0x80000
#define WS_EX_LAYOUTRTL 0x400000
#define WS_EX_LEFT 0
#define WS_EX_LEFTSCROLLBAR 0x4000
#define WS_EX_LTRREADING 0
#define WS_EX_MDICHILD 64
#define WS_EX_NOACTIVATE 0x8000000
#define WS_EX_NOINHERITLAYOUT 0x100000
#define WS_EX_NOPARENTNOTIFY 4
#define WS_EX_OVERLAPPEDWINDOW 0x300
#define WS_EX_PALETTEWINDOW 0x188
#define WS_EX_RIGHT 0x1000
#define WS_EX_RIGHTSCROLLBAR 0
#define WS_EX_RTLREADING 0x2000
#define WS_EX_STATICEDGE 0x20000
#define WS_EX_TOOLWINDOW 128
#define WS_EX_TOPMOST 8
#define WS_EX_TRANSPARENT 32
#define WS_EX_WINDOWEDGE 256

Code: Select all

#define IDD_DLG  100
#define IDC_EDIT 200
#define EDIT_STYLE1 WS_CHILD | WS_VISIBLE | ES_WANTRETURN | ES_MULTILINE
#define EDIT_STYLE2 WS_VSCROLL | WS_HSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL
IDD_DLG DIALOGEX 0,0,200,150
CAPTION "Test"
FONT 10,"Microsoft Sans Serif",600,0,0
STYLE WS_OVERLAPPEDWINDOW | DS_CENTER
BEGIN
  EDITTEXT IDC_EDIT,0,0,0,0,EDIT_STYLE1 | EDIT_STYLE2
END

Code: Select all

''=============================================================================
#include "windows.bi"
#include "win\commctrl.bi"
#include "win\shellapi.bi"
#define IDD_DLG   100
#define IDC_EDIT  200

dim shared as HWND g_hwndEdit

''=============================================================================

''----------------------------------------------------------------------------
'' At least for version 0.90.1, while these declarations are missing from the
'' CommCtrl.bi header file, the corresponding function exports are present in
'' the ComCtl32 import library.
''----------------------------------------------------------------------------

type SUBCLASSPROC as function( byval as HWND, byval as UINT, byval as WPARAM, _
                               byval as LPARAM, byval as UINT_PTR, _
                               byval as DWORD_PTR) as LRESULT

declare function SetWindowSubclass alias "SetWindowSubclass" _
                                  ( byval as HWND, _
                                    byval as SUBCLASSPROC, _
                                    byval as UINT_PTR, _
                                    byval as DWORD_PTR ) as BOOL

declare function RemoveWindowSubclass alias "RemoveWindowSubclass" _
                                     ( byval as HWND, _
                                       byval as SUBCLASSPROC, _
                                       byval as UINT_PTR ) as BOOL

declare function DefSubclassProc alias "DefSubclassProc" _
                                ( byval as HWND, _
                                  byval as UINT, _
                                  byval as WPARAM, _
                                  byval as LPARAM ) as LRESULT

''-----------------------------------------------------
'' At least under XP, this one requires a manifest and
'' Comctl32.dll version 6. NOT TESTED!
''-----------------------------------------------------

declare function GetWindowSubclass alias "GetWindowSubclass" _
                                  ( byval as HWND, _
                                    byval as SUBCLASSPROC, _
                                    byval as UINT_PTR, _
                                    byval as DWORD_PTR ptr ) as BOOL

''=============================================================================

function SubclassProc( byval hwnd as HWND, byval uMsg as UINT, _
                       byval wParam as WPARAM, byval lParam as LPARAM, _
                       byval uIdSubclass as UINT_PTR, _
                       byval dwRefData as DWORD_PTR ) as LRESULT

    static as zstring * 300 filename
    static as ubyte ptr p

    select case uMsg

        case WM_DROPFILES

            ''--------------------------------------------------
            '' Assume that only one file was dropped (index 0).
            ''--------------------------------------------------

            DragQueryFile( cast(HDROP,wParam), 0, @filename, 300 )

            ''--------------------------------------------------
            '' Assume that the dropped file is a text file and
            '' copy the contents to the edit control. The +1
            '' ensures that the buffer has a null terminator.
            ''--------------------------------------------------

            open filename for binary as 1
            p = callocate(lof(1)+1)
            get #1,,p[0],lof(1)+1
            close

            SetWindowText( g_hwndEdit, p )

            deallocate(p)

            DragFinish( cast(HDROP,wParam) )

            return 0

        case else

            return DefSubclassProc( hwnd, uMsg, wParam, lParam)

    end select

end function

''=============================================================================

function DialogProc( byval hwndDlg as  HWND, _
                      byval uMsg as UINT, _
                      byval wParam as WPARAM, _
                      byval lParam as LPARAM ) as integer

    select case uMsg

        case WM_INITDIALOG

            g_hwndEdit = GetDlgItem( hwndDlg, IDC_EDIT )

            ''-----------------------------------------------------------
            '' Register the edit control window to accept dropped files.
            ''-----------------------------------------------------------

            DragAcceptFiles( g_hwndEdit, TRUE )

            ''--------------------------------------------------------
            '' Install the edit control subclass callback, specifying
            '' 1 for the subclass ID and 0 for the reference data.
            ''--------------------------------------------------------

            SetWindowSubclass( g_hwndEdit, @SubclassProc, 1, 0 )


        case WM_SIZE

            ''-------------------------------------------
            '' Size the edit control to the client area.
            ''-------------------------------------------

            MoveWindow( g_hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE )

        case WM_COMMAND

            select case wParam

                case IDCANCEL

                    ''------------------------------------------
                    '' This allows the user to close the dialog
                    '' window by pressing the Escape key.
                    ''------------------------------------------

                    RemoveWindowSubclass( g_hwndEdit, @SubclassProc, 1 )

                    EndDialog( hwndDlg, 0 )

            end select

        case WM_CLOSE

            RemoveWindowSubclass( g_hwndEdit, @SubclassProc, 1 )

            EndDialog( hwndDlg, 0 )

    end select

    return 0

end function

''=============================================================================

''----------------------------------------------------------
'' Create a modal dialog from the dialog template resource.
''
'' The DialogBoxParam function does not return until the
'' dialog box is destroyed.
''----------------------------------------------------------

DialogBoxParam( GetModuleHandle(null), _
                cast(zstring ptr,IDD_DLG), _
                0, _
                @DialogProc, _
                0 )
stephanbrunker
Posts: 62
Joined: Nov 02, 2013 14:57

Re: Subclassing a Editbox

Post by stephanbrunker »

Thanks Michael for helping ... but it doesn't work out.

I generated a new project in FBEdit, and if I integrate your code in the standard template it looks like:

dropfiles.bi:

Code: Select all

#Define IDD_DIALOG			1000

#Define IDC_EDT1	1001
#Define IDC_BTN1 1002
#Define IDC_EDT2 1003

Dim Shared hInstance As HMODULE
Dim Shared CommandLine As ZString Ptr
Dim Shared hWnd As HWND
Dim Shared hWinX As HWND
Dim Shared hEDT1 As HWND
Dim Shared filenamex As ZString*500
Dim Shared SubID As UInteger = 1

Const ClassName="DLGCLASS"
Const AppName="Dialog as main"
The Dialog includes one Button and two Textboxes.

The dropfiles.bas:

Code: Select all

#Include once "windows.bi"
#Include Once "win/commctrl.bi"
#Include Once "win/commdlg.bi"
#Include Once "win/shellapi.bi"
#Include "dropfiles.bi"

Declare Function WndProc(ByVal hWin As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM) As Integer


''=============================================================================

''----------------------------------------------------------------------------
'' At least for version 0.90.1, while these declarations are missing from the
'' CommCtrl.bi header file, the corresponding function exports are present in
'' the ComCtl32 import library.
''----------------------------------------------------------------------------

type SUBCLASSPROC as function( byval as HWND, byval as UINT, byval as WPARAM, _
                               byval as LPARAM, byval as UINT_PTR, _
                               byval as DWORD_PTR) as LRESULT

declare function SetWindowSubclass alias "SetWindowSubclass" _
                                  ( byval as HWND, _
                                    byval as SUBCLASSPROC, _
                                    byval as UINT_PTR, _
                                    byval as DWORD_PTR ) as BOOL

declare function RemoveWindowSubclass alias "RemoveWindowSubclass" _
                                     ( byval as HWND, _
                                       byval as SUBCLASSPROC, _
                                       byval as UINT_PTR ) as BOOL

declare function DefSubclassProc alias "DefSubclassProc" _
                                ( byval as HWND, _
                                  byval as UINT, _
                                  byval as WPARAM, _
                                  byval as LPARAM ) as LRESULT

''-----------------------------------------------------
'' At least under XP, this one requires a manifest and
'' Comctl32.dll version 6. NOT TESTED!
''-----------------------------------------------------

declare function GetWindowSubclass alias "GetWindowSubclass" _
                                  ( byval as HWND, _
                                    byval as SUBCLASSPROC, _
                                    byval as UINT_PTR, _
                                    byval as DWORD_PTR ptr ) as BOOL

''=============================================================================



Function EDT1Proc(ByVal hWin2 As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM,ByVal uIdSubclass As UINT_PTR, dwRefData As DWORD_PTR) As Integer
Dim windowcontent As String
Dim files As HDROP
	Select Case uMsg
		Case WM_DROPFILES

			files=Cast(hdrop,wParam)
			DragQueryFile(files,0,@filenamex,500)
			windowcontent=filenamex
			DragQueryFile(files,1,@filenamex,500)
			windowcontent &= Chr(13,10) & filenamex
			Print filenamex
			SetWindowText(hEDT1,@filenamex)
			Return 0
		Case Else
			return DefSubclassProc( hwnd, uMsg, wParam, lParam)
	End Select
End Function

Function WndProc(ByVal hWin As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM) As Integer
Dim files As HDROP
Dim windowcontent As String

	Select Case uMsg
	
		Case WM_INITDIALOG

			SetWindowSubclass( hEDT1, @EDT1Proc, 1, 0 )
		Case WM_COMMAND
			Select Case HiWord(wParam)
				Case BN_CLICKED,1
					Select Case LoWord(wParam)
						Case IDC_BTN1
							Print "BTN1"
							SetDlgItemText(hWin,IDC_EDT1,@"test")
							SetDlgItemText(hWin,IDC_EDT2,@"test2")
						'
					End Select
					'
			End Select
		

		Case WM_SIZE
			'
		Case WM_CLOSE
			DestroyWindow(hWin)
			'
		Case WM_DESTROY
			RemoveWindowSubclass( hEDT1, @EDT1Proc, 1 )
			PostQuitMessage(NULL)
			'
		Case Else
			Return DefWindowProc(hWin,uMsg,wParam,lParam)
			'
	End Select
	Return 0

End Function

Function WinMain(ByVal hInst As HINSTANCE,ByVal hPrevInst As HINSTANCE,ByVal CmdLine As ZString ptr,ByVal CmdShow As Integer) As Integer
	Dim wc As WNDCLASSEX
	Dim msg As MSG

	' Setup and register class for dialog
	wc.cbSize=SizeOf(WNDCLASSEX)
	wc.style=CS_HREDRAW or CS_VREDRAW
	wc.lpfnWndProc=@WndProc
	wc.cbClsExtra=0
	wc.cbWndExtra=DLGWINDOWEXTRA
	wc.hInstance=hInst
	wc.hbrBackground=Cast(HBRUSH,COLOR_BTNFACE+1)
	wc.lpszClassName=@ClassName
	wc.hIcon=LoadIcon(NULL,IDI_APPLICATION)
	wc.hIconSm=wc.hIcon
	wc.hCursor=LoadCursor(NULL,IDC_ARROW)
	RegisterClassEx(@wc)
	
	' Create and show the dialog
	hWinX=CreateDialogParam(hInstance,Cast(ZString Ptr,IDD_DIALOG),NULL,@WndProc,NULL)
	hEDT1=GetDlgItem(hWinX,IDC_EDT1)
	DragAcceptFiles(hEDT1,TRUE)
	SetWindowSubclass( hEDT1, @EDT1Proc, 1, 0 )

	' Message loop
	Do While GetMessage(@msg,NULL,0,0)
		TranslateMessage(@msg)
		DispatchMessage(@msg)
	Loop
	Return msg.wParam

End Function

' Program start
hInstance=GetModuleHandle(NULL)
CommandLine=GetCommandLine
InitCommonControls
WinMain(hInstance,NULL,CommandLine,SW_SHOWDEFAULT)
ExitProcess(0)

End
Without the Case Else - Return DefWindowProc(hWin,uMsg,wParam,lParam) in the WndProc the Dialog doesn't even show.

And if i put the
DragAcceptFiles(hEDT1,TRUE)
SetWindowSubclass( hEDT1, @EDT1Proc, 1, 0 )

in the WndProc - WMINIT the Editbox doesn't accept drops and the subclassing doesn't work too. And if they're where they are, the drop occurs - I can see it printed in the console window - but the Editbox doesn't accept changing text. If I quote out the subclassing, the test message is printed in both editboxes, with the subclassing only in the normal one. ...?

And if I add a "Case WM_SETTEXT" in the EDT1Proc with a print in the console window, I can see the SETTEXT message is sent, but I don't know why the Textbox doesn't react. Perhaps because Windows thinks that my editbox is now a window, and the SETTEXT message tries to change the (invisible) caption of the window instead of the content of the editbox.
stephanbrunker
Posts: 62
Joined: Nov 02, 2013 14:57

Re: Subclassing a Editbox

Post by stephanbrunker »

Okay, I found the error. Every time when I use something "out of the box", it doesn't work until I've completely understood it myself (and then I doesn't need a "out of the box" solution ...). In this case, the templates of FBEdit caused the problem. The Template "DialogApp" works like the one Michael has posted. And there, the subclassing works.

But the Template "Dialog as Main" is different, as you can see in the code I've posted. This is my first program and so I haven't seen the obvious difference: It comes with Window classes and a WinMain with message loop. And, what is understandable, Subclassing with SetWindowSubclass doesn't work when you work already with Window classes. So, either I simply register another Window Class or I stick to fishing the Messages from the message loop. What I've to have anyway out of other requirements.

I found out because FBEDit's own Tutorial step 1 doesn't work with this template: You can't set simply an Icon like it's written in the tutorial because in the wndclassex structure, the icon has to be in the right size and thus loaded with loadimage, otherwise the default icon is used ...

So, thanks anyway ..!
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Re: Subclassing a Editbox

Post by MichaelW »

I used a dialog as a convenience. I don’t use FBEdit (or any other IDE) so I can’t provide anything to help you with that, but I can provide a working version that is based on a normal application window.

Code: Select all

''=============================================================================
#include "windows.bi"
#include "win\commctrl.bi"
#include "win\shellapi.bi"
''=============================================================================

dim shared as HWND g_hwndEdit

''=============================================================================

''----------------------------------------------------------------------------
'' At least for version 0.90.1, while these declarations are missing from the
'' CommCtrl.bi header file, the corresponding function exports are present in
'' the ComCtrl32 import library.
''----------------------------------------------------------------------------

type SUBCLASSPROC as function( byval as HWND, byval as UINT, byval as WPARAM, _
                               byval as LPARAM, byval as UINT_PTR, _
                               byval as DWORD_PTR) as LRESULT

declare function SetWindowSubclass alias "SetWindowSubclass" _
                                  ( byval as HWND, _
                                    byval as SUBCLASSPROC, _
                                    byval as UINT_PTR, _
                                    byval as DWORD_PTR ) as BOOL

declare function RemoveWindowSubclass alias "RemoveWindowSubclass" _
                                     ( byval as HWND, _
                                       byval as SUBCLASSPROC, _
                                       byval as UINT_PTR ) as BOOL

declare function DefSubclassProc alias "DefSubclassProc" _
                                ( byval as HWND, _
                                  byval as UINT, _
                                  byval as WPARAM, _
                                  byval as LPARAM ) as LRESULT

''-----------------------------------------------------
'' At least under XP, this one requires a manifest and
'' Comctl32.dll version 6. NOT TESTED!
''-----------------------------------------------------

declare function GetWindowSubclass alias "GetWindowSubclass" _
                                  ( byval as HWND, _
                                    byval as SUBCLASSPROC, _
                                    byval as UINT_PTR, _
                                    byval as DWORD_PTR ptr ) as BOOL

''=============================================================================

''---------------------------------------------------------------------
'' This procedure sizes the specified window so the client area is the
'' specified width and height and optionally centers the window on the
'' the screen. Unlike AdjustWindowRect and AdjustWindowRectEx, this
'' procedure can handle a window with the WS_OVERLAPPED style.
''---------------------------------------------------------------------

sub SetClientSize( hwnd as HWND, pixelWidth as integer, _
                   pixelHeight as integer, bCenter as BOOL )

    dim as integer x, y, w, h
    dim as RECT rcc, rcw

    GetClientRect( hwnd, @rcc )
    GetWindowRect( hwnd, @rcw )

    w = (rcw.right - rcw.left) - (rcc.right - pixelWidth) - 1
    h = (rcw.bottom - rcw.top) - (rcc.bottom - pixelHeight) - 1

    if bCenter then
        x = (GetSystemMetrics( SM_CXSCREEN ) / 2) - w / 2
        y = (GetSystemMetrics( SM_CYSCREEN ) / 2) - h / 2
    else
        x = rcw.left
        y = rcw.top
    end if

    MoveWindow( hwnd, x, y, w, h, TRUE )

end sub

''=============================================================================

function SubclassProc( byval hwnd as HWND, byval uMsg as UINT, _
                       byval wParam as WPARAM, byval lParam as LPARAM, _
                       byval uIdSubclass as UINT_PTR, _
                       byval dwRefData as DWORD_PTR ) as LRESULT

    static as zstring * 300 filename
    static as ubyte ptr p

    select case uMsg

        case WM_DROPFILES

            ''--------------------------------------------------
            '' Assume that only one file was dropped (index 0).
            ''--------------------------------------------------

            DragQueryFile( cast(HDROP,wParam), 0, @filename, 300 )

            ''--------------------------------------------------
            '' Assume that the dropped file is a text file and
            '' copy the contents to the edit control. The +1
            '' ensures that the buffer has a null terminator.
            ''--------------------------------------------------

            open filename for binary as 1
            p = callocate(lof(1)+1)
            get #1,,p[0],lof(1)+1
            close

            SetWindowText( g_hwndEdit, p )

            deallocate(p)

            DragFinish( cast(HDROP,wParam) )

            return 0

        case else

            return DefSubclassProc( hwnd, uMsg, wParam, lParam)

    end select

end function

''=============================================================================

function WindowProc( byval hwnd as HWND, _
                     byval uMsg as UINT, _
                     byval wParam as WPARAM, _
                     byval lParam as LPARAM ) as integer

    select case uMsg

        case WM_CREATE

            SetClientSize( hwnd, 800, 600, TRUE )

            ''--------------------------------------------------------
            '' Create the edit control window. The coordinates, width
            '' and height can be set to 0 because the control window
            '' gets positioned and sized in the WM_SIZE handler.
            ''--------------------------------------------------------

            #define EDIT_STYLE WS_CHILD or WS_VISIBLE or ES_WANTRETURN or _
                               ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or _
                               ES_AUTOHSCROLL  or ES_AUTOVSCROLL

            g_hwndEdit = CreateWindowEx( 0, "Edit", NULL, EDIT_STYLE, _
                                         0, 0, 0, 0, hwnd, NULL, NULL, 0 )

            ''-----------------------------------------------------------
            '' Register the edit control window to accept dropped files.
            ''-----------------------------------------------------------

            DragAcceptFiles( g_hwndEdit, TRUE )

            ''--------------------------------------------------------------
            '' Install the edit control subclass callback, specifying 1 for
            '' the subclass ID and 0 for the reference data.
            ''--------------------------------------------------------------

            SetWindowSubclass( g_hwndEdit, @SubclassProc, 1, 0 )

        case WM_SIZE

            ''-------------------------------------------
            '' Size the edit control to the client area.
            ''-------------------------------------------

            MoveWindow(g_hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE)

        case WM_COMMAND

            select case wParam

                case IDCANCEL

                    ''------------------------------------------------------
                    '' This allows the user to close the window by pressing
                    '' the Escape key. Note that this behavior depends on
                    '' the message loop calling IsDialogMessage.
                    ''------------------------------------------------------

                    RemoveWindowSubclass( g_hwndEdit, @SubclassProc, 1 )
                    DestroyWindow( hwnd )

            end select

        case WM_CLOSE

            RemoveWindowSubclass( g_hwndEdit, @SubclassProc, 1 )
            DestroyWindow( hwnd )

        case WM_DESTROY

            PostQuitMessage( 0 )

        case else

            return DefWindowProc( hwnd, uMsg, wParam, lParam )

    end select

    return 0

end function

''=============================================================================

''-------------------------
'' Start of implicit main.
''-------------------------

dim as HWND hwnd
dim as MSG msg
dim as WNDCLASSEX wcx

wcx.cbSize = sizeof(WNDCLASSEX)
wcx.style = 0
wcx.lpfnWndProc = @WindowProc
wcx.cbClsExtra = 0
wcx.cbWndExtra = 0
wcx.hInstance = GetModuleHandle( NULL )
wcx.hIcon = LoadIcon( NULL, IDI_APPLICATION )
wcx.hCursor = LoadCursor( NULL, IDC_ARROW )
wcx.hbrBackground = GetStockObject( WHITE_BRUSH )
wcx.lpszMenuName = NULL
wcx.lpszClassName = strptr("test_class")
wcx.hIconSm = 0

RegisterClassEx( @wcx )

hwnd = CreateWindowEx( 0, "test_class", "Test", WS_OVERLAPPEDWINDOW, _
                       0, 0, 0, 0, NULL, NULL, NULL, NULL )

ShowWindow( hwnd, SW_SHOWNORMAL )
UpdateWindow( hwnd )

''-------------------------------------------------------------------------
'' The IsDialogMessage function provides dialog-style keyboard navigation
'' and selection. It provides most, but not all, of the functionality of
'' TranslateMessage and DispatchMessage. For this app TranslateMessage and
'' DispatchMessage are not actually necessary, and will never be called,
'' but are included as standard message loop components for apps where
'' they are necessary.
''-------------------------------------------------------------------------

do while GetMessage( @msg, NULL, 0, 0 ) > 0
    if IsDialogMessage(hwnd, @msg ) = 0 then
        TranslateMessage( @msg )
        DispatchMessage( @msg )
    end if
loop
stephanbrunker
Posts: 62
Joined: Nov 02, 2013 14:57

Re: Subclassing a Editbox

Post by stephanbrunker »

I tried several combinations, and as far I can say, createdialogparam and setwindowsubclass or setwindowlongptr simply wouldn't work together. But if there's no disadvantage, it works fine if you simply check the messages in the message loop and redirect them - and if you have multiple editboxes, include the ID of the editbox in the lparam, because it's not used by WM_DROPFILES :

Code: Select all

	Do While GetMessage(@winmsg,NULL,0,0)
		If winmsg.message = WM_DROPFILES And winmsg.hwnd = hEditbox then
			winmsg.hwnd=hMainWindow
			winmsg.lparam=Cast(lparam,IDC_EDITBOX)
		EndIf
		TranslateMessage(@winmsg)
		DispatchMessage(@winmsg)
	Loop
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Re: Subclassing a Editbox

Post by MichaelW »

You have not posted a complete set of source files, so I took the code you posted here, corrected some errors in it and combined it with a modified version of my template resource, and ended up with a working app (although it only displays the name of the dropped file, and not the contents). Note that in my example code I used the template to create a modal dialog, so there was no need for the WS_VISIBLE style:

Code: Select all

#define IDD_DLG  1000
#define IDC_EDT1 1001
#define EDIT_STYLE1 WS_CHILD | WS_VISIBLE | ES_WANTRETURN | ES_MULTILINE
#define EDIT_STYLE2 WS_VSCROLL | WS_HSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL
IDD_DLG DIALOGEX 0,0,200,150
CAPTION "Test"
FONT 10,"Microsoft Sans Serif",600,0,0
// For a modeless dialog the WS_VISIBLE style is required for the dialog
// to be visible (by default, IIRC it can be made visible by other means).
STYLE WS_OVERLAPPEDWINDOW | WS_VISIBLE | DS_CENTER
BEGIN
  EDITTEXT IDC_EDT1,0,0,0,0,EDIT_STYLE1 | EDIT_STYLE2
END

Code: Select all

#Include once "windows.bi"
#Include Once "win/commctrl.bi"
#Include Once "win/commdlg.bi"
#Include Once "win/shellapi.bi"
#Include "dropfiles.bi"

Declare Function WndProc(ByVal hWin As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM) As Integer

''=============================================================================

''----------------------------------------------------------------------------
'' At least for version 0.90.1, while these declarations are missing from the
'' CommCtrl.bi header file, the corresponding function exports are present in
'' the ComCtl32 import library.
''----------------------------------------------------------------------------

type SUBCLASSPROC as function( byval as HWND, byval as UINT, byval as WPARAM, _
                               byval as LPARAM, byval as UINT_PTR, _
                               byval as DWORD_PTR) as LRESULT

declare function SetWindowSubclass alias "SetWindowSubclass" _
                                  ( byval as HWND, _
                                    byval as SUBCLASSPROC, _
                                    byval as UINT_PTR, _
                                    byval as DWORD_PTR ) as BOOL

declare function RemoveWindowSubclass alias "RemoveWindowSubclass" _
                                     ( byval as HWND, _
                                       byval as SUBCLASSPROC, _
                                       byval as UINT_PTR ) as BOOL

declare function DefSubclassProc alias "DefSubclassProc" _
                                ( byval as HWND, _
                                  byval as UINT, _
                                  byval as WPARAM, _
                                  byval as LPARAM ) as LRESULT

''-----------------------------------------------------
'' At least under XP, this one requires a manifest and
'' Comctl32.dll version 6. NOT TESTED!
''-----------------------------------------------------

declare function GetWindowSubclass alias "GetWindowSubclass" _
                                  ( byval as HWND, _
                                    byval as SUBCLASSPROC, _
                                    byval as UINT_PTR, _
                                    byval as DWORD_PTR ptr ) as BOOL

''=============================================================================

Function EDT1Proc(ByVal hWin2 As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM,ByVal uIdSubclass As UINT_PTR, dwRefData As DWORD_PTR) As Integer
Dim windowcontent As String
Dim files As HDROP
    Select Case uMsg
        Case WM_DROPFILES
            files=Cast(hdrop,wParam)
            DragQueryFile(files,0,@filenamex,500)
            windowcontent=filenamex
            DragQueryFile(files,1,@filenamex,500)
            windowcontent &= Chr(13,10) & filenamex
            Print filenamex
            SetWindowText(hEDT1,@filenamex)
            Return 0
        Case Else
            '' THIS NEEDS TO USE THE WINDOW HANDLE PASSED TO THE SUBCLASS PROC:
            '' return DefSubclassProc( hwnd, uMsg, wParam, lParam)
            return DefSubclassProc( hWin2, uMsg, wParam, lParam)
    End Select
End Function

Function WndProc(ByVal hWin As HWND,ByVal uMsg As UINT,ByVal wParam As WPARAM,ByVal lParam As LPARAM) As Integer
Dim files As HDROP
Dim windowcontent As String

    Select Case uMsg

        Case WM_INITDIALOG

            hEDT1=GetDlgItem(hWin,IDC_EDT1)
            DragAcceptFiles(hEDT1,TRUE)
            SetWindowSubclass( hEDT1, @EDT1Proc, 1, 0 )

        Case WM_COMMAND
            Select Case HiWord(wParam)
                Case BN_CLICKED,1
                    Select Case LoWord(wParam)
                        Case IDC_BTN1
                            Print "BTN1"
                            SetDlgItemText(hWin,IDC_EDT1,@"test")
                            SetDlgItemText(hWin,IDC_EDT2,@"test2")
                        '
                    End Select
                    '
            End Select

        Case WM_SIZE
            '
            MoveWindow(hEDT1, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE)

        Case WM_CLOSE
            DestroyWindow(hWin)
            '
        Case WM_DESTROY
            RemoveWindowSubclass( hEDT1, @EDT1Proc, 1 )
            PostQuitMessage(NULL)
            '
        '' THIS NOT FOR A DIALOG:
        ''Case Else
            ''Return DefWindowProc(hWin,uMsg,wParam,lParam)
            ''
    End Select
    Return 0

End Function

Function WinMain(ByVal hInst As HINSTANCE,ByVal hPrevInst As HINSTANCE,ByVal CmdLine As ZString ptr,ByVal CmdShow As Integer) As Integer
    Dim wc As WNDCLASSEX
    Dim msg As MSG

    '' THIS CLASS NOT USED:
    ' Setup and register class for dialog
    wc.cbSize=SizeOf(WNDCLASSEX)
    wc.style=CS_HREDRAW or CS_VREDRAW
    wc.lpfnWndProc=@WndProc
    wc.cbClsExtra=0
    wc.cbWndExtra=DLGWINDOWEXTRA
    wc.hInstance=hInst
    wc.hbrBackground=Cast(HBRUSH,COLOR_BTNFACE+1)
    wc.lpszClassName=@ClassName
    wc.hIcon=LoadIcon(NULL,IDI_APPLICATION)
    wc.hIconSm=wc.hIcon
    wc.hCursor=LoadCursor(NULL,IDC_ARROW)
    RegisterClassEx(@wc)

    ' Create and show the dialog
    hWinX=CreateDialogParam(hInstance,Cast(ZString Ptr,IDD_DIALOG),NULL,@WndProc,NULL)

    '' THIS WILL NOT WORK HERE, NEEDS TO BE IN WM_INITDIALOG HANDLER
    ''hEDT1=GetDlgItem(hWinX,IDC_EDT1)
    ''DragAcceptFiles(hEDT1,TRUE)
    ''SetWindowSubclass( hEDT1, @EDT1Proc, 1, 0 )

    ' Message loop
    Do While GetMessage(@msg,NULL,0,0) > 0
        if IsDialogMessage( hWinX, @msg ) = 0 then
            TranslateMessage(@msg)
            DispatchMessage(@msg)
        end if
    Loop
    Return msg.wParam

End Function

' Program start
hInstance=GetModuleHandle(NULL)
CommandLine=GetCommandLine

InitCommonControls
WinMain(hInstance,NULL,CommandLine,SW_SHOWDEFAULT)
ExitProcess(0)

End
Post Reply