[ListView] How to force "LV_GetText()" function to get text longer than 8191 characters? Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
just me
Posts: 9457
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 05:07

CommCtrl.h wrote:

Code: Select all

typedef struct tagLVITEMW
{
    UINT mask;
    int iItem;
    int iSubItem;
    UINT state;
    UINT stateMask;
    LPWSTR pszText;
    int cchTextMax;
    int iImage;
    LPARAM lParam;
    int iIndent;
#if (NTDDI_VERSION >= NTDDI_WINXP)
    int iGroupId;
    UINT cColumns; // tile view columns
    PUINT puColumns;
#endif
#if (NTDDI_VERSION >= NTDDI_VISTA)
    int* piColFmt;
    int iGroup; // readonly. only valid for owner data.
#endif
} LVITEMW, *LPLVITEMW;
The structure doesn't contain a 'size' member. So it's safe to allocate the maximum structure size in any case. It's 88 bytes on Vista+ 64-bit. (But you'll 'waste' some bytes on XP and 32-bit OSes.)
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 11:00

what just me said.

The problem with many development environments (mine included) is that you have only one PC to test on. If you are a responsible developer, you write your stuff so that it will work on other versions of AutoHotkey (ANSI, Unicode, x64) and other versions of Windows but testing it on all possible environments is tough, if not impossible.
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 11:28

jballi wrote:
25 Oct 2018, 11:00
And what about getting the max "byte" integer number from a listview "RowX/ColX" (through DllCall, of course) and store it in "MaxbyteFound" variable, then use "VarSetCapacity(var, MaxbyteFound, 0)", then get the string from listview "RowX/ColX" and store it in "Var" variable?

I think the above is exactly what "ControlGetText" does:
https://autohotkey.com/docs/commands/Co ... tm#Remarks
https://docs.microsoft.com/en-us/window ... eving-text
f the retrieved text appears to be truncated (incomplete), try using VarSetCapacity(OutputVar, 55) prior to ControlGetText [replace 55 with a size that is considerably longer than the truncated text]. This is necessary because some applications do not respond properly to the WM_GETTEXTLENGTH message, which causes AutoHotkey to make the output variable too small to fit all the text.
Last edited by User on 25 Oct 2018, 19:21, edited 1 time in total.
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 18:40

User wrote:
25 Oct 2018, 11:28
And what about getting the max "byte" integer number from a listview "RowX/ColX" (through DllCall, of course) and store it in "MaxbyteFound" variable, then use "VarSetCapacity(var, MaxbyteFound, 0)", then get the string from listview "RowX/ColX" and store it in "Var" variable?
I apologize. It is very unclear to me what you are asking here. I have some idea but I'm not sure. If you could restate the question/requirement, I'll try to help if I can.
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 19:06

jballi wrote:
25 Oct 2018, 18:40
- "LVM_GetItemLength(LvCtrl, 3, 2)" and store in "MaxbyteFound" var
- then, "VarSetCapacity(Text, MaxbyteFound, 0)"
- "LVM_GetItemText(LvCtrl, 3, 2)" and store in "Text" var

Sorry, but I can't be more clear than that! (just the same "ControlGetText" does!)
https://autohotkey.com/docs/commands/Co ... tm#Remarks
https://docs.microsoft.com/en-us/window ... eving-text

Code: Select all

gui, add, edit, w400 h300 +HScroll gGetLength +HwndEditCtrlId,  hhhhh

gui, add, text, w100 vTextCtrl +border,

gui, show

GetLength:	;______________ Get text Length _______________

SendMessage, 0xE,0,0,, % "ahk_id" EditCtrlId	;"0xE" = WM_GETTEXTLENGTH

TextLength := ErrorLevel

guicontrol, , TextCtrl, % TextLength

return

guiclose:	;______________ gui close ________________
exitapp
by the way, is the code below correct?

Code: Select all

if (((GV:=DllCall("GetVersion"))&0xFF . "." . GV>>8&0xFF)>=6.0)		;Vista+ (Vista and later versions)
sizeofLVITEM:=(A_PtrSize=8) ? 72:60
else
sizeofLVITEM:=(A_PtrSize=8) ? 64:52	;WinXp- (winxp and earlier versions?)
Last edited by User on 25 Oct 2018, 19:45, edited 1 time in total.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 19:35

- Do you want a listview control with every Unicode character? Am I right?
- Is it possible to do word wrap with a listview control, to see each character?

Code: Select all

;[JEE_LVGetText/JEE_LVSetText functions]
;GUIs via DllCall: get/set internal/external control text - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=40514

q:: ;create listview with every Unicode character
Gui, New, +HwndhGui, MyWinTitle
Gui, Add, ListView, +HwndhLV r3, LVH 1|LVH 2|LVH 3
Loop, 3
	if (A_Index = 1)
		LV_Add("Select", "LV A" A_Index, "LV B" A_Index, "LV C" A_Index)
	else
		LV_Add("", "LV A" A_Index, "LV B" A_Index, "LV C" A_Index)
Gui, Show, w300 h300
vText := ""
;VarSetCapacity(vText, 10000000*2)
VarSetCapacity(vText, 2162687*2)
Loop, 1114111
	vText .= Chr(A_Index)
JEE_LVSetText(hLV, vText, 1, 1,,, "m10000000")
Gui, Show, w300 h300
vText := JEE_LVGetText(hLV, 1, 1,,, "m10000000")
MsgBox, % StrLen(vText) "`r`n" (StrLen(vText)/1114111)
return
- Btw I worked on a list of common structs with parameter info, here:
list of structs with parameters (sizes and types) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 74&t=30497
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 20:07

jeeswg wrote:
25 Oct 2018, 19:35
@jeeswg, what I want is already accomplished, get all the text from a listview item!

The 1114111 unicode characters were used just for testings!

This is nothing to do with "listview control to contain every Unicode characters"!
_________________

Now we are just trying to find the best way to define "Maxbyte" from @Jballi function!
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 20:37

User wrote:
25 Oct 2018, 19:06
jballi wrote:
25 Oct 2018, 18:40
- "LVM_GetItemLength(LvCtrl, 3, 2)" and store in "MaxbyteFound" var
- then, "VarSetCapacity(Text, MaxbyteFound, 0)"
- "LVM_GetItemText(LvCtrl, 3, 2)" and store in "Text" var

Sorry, but I can't be more clear than that! (just the same "ControlGetText" does!)
https://autohotkey.com/docs/commands/Co ... tm#Remarks
https://docs.microsoft.com/en-us/window ... eving-text

Code: Select all

gui, add, edit, w400 h300 +HScroll gGetLength +HwndEditCtrlId,  hhhhh

gui, add, text, w100 vTextCtrl +border,

gui, show

GetLength:	;______________ Get text Length _______________

SendMessage, 0xE,0,0,, % "ahk_id" EditCtrlId	;"0xE" = WM_GETTEXTLENGTH

TextLength := ErrorLevel

guicontrol, , TextCtrl, % TextLength

return

guiclose:	;______________ gui close ________________
exitapp
by the way, is the code below correct?

Code: Select all

if (((GV:=DllCall("GetVersion"))&0xFF . "." . GV>>8&0xFF)>=6.0)		;Vista+ (Vista and later versions)
sizeofLVITEM:=(A_PtrSize=8) ? 72:60
else
sizeofLVITEM:=(A_PtrSize=8) ? 64:52	;WinXp- (winxp and earlier versions?)
I'm sorry, there is no "GetTextLength" message or equivalent for the ListView item text. The actual length can discovered with a try and fail loop. PM and I will show you an example.

Also, per just me, there is no cbSize member in the structure, so it's safe to always set the size of the LVITEM structure to the maximum size which is 88. Ex: VarSetCapacity(LVITEM,88,0)
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

25 Oct 2018, 21:00

jballi wrote:
25 Oct 2018, 20:37
well, if there is no way to get the text length of a listview item, that's ok! (I will set default maxchar to autohotkey default #maxmem, which is 64Megabyte!)

@jballi thanks for your help and for your function! (You should create a new thread just for this function of yours, and keep it updated from there!)

[Last Thing]:
(Oops, my bad! Got it! I should use sizeofLVITEM:=88 inside the function instead the code below! Thanks! - You really need to create a new thread just for this function and keep it updated!)

I really need you to confirm if the below code is correct because I want to paste it inside your function, and I don't want to spread wrong infos!

Code: Select all

if (((GV:=DllCall("GetVersion"))&0xFF . "." . GV>>8&0xFF)>=6.0)		;Vista+ (Vista and later versions)
sizeofLVITEM:=(A_PtrSize=8) ? 72:60
else
sizeofLVITEM:=(A_PtrSize=8) ? 64:52	;WinXp- (winxp and earlier versions?)
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

26 Oct 2018, 10:42

jballi wrote:
25 Oct 2018, 20:37
@jballi, just to make things clear here:

sizeofLVITEM := 88, is supported by xp+ x32 and vista+ x64 (so xp x64 is not supported, correct?)

in the other hand:

the code below is supported by xp-/+ x32/x64 (correct?)

Code: Select all

if (((GV:=DllCall("GetVersion"))&0xFF . "." . GV>>8&0xFF)>=6.0)		;Vista+ x32/x64 (Vista and later versions?)
sizeofLVITEM:=(A_PtrSize=8) ? 72:60
else
sizeofLVITEM:=(A_PtrSize=8) ? 64:52	;WinXp- x32/x64 (winxp and earlier versions?)
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

26 Oct 2018, 13:49

To be clear again. 88 is the magic number and should work for all versions of AutoHotkey and all versions of Windows.

For example:

VarSetCapacity(LVITEM,88,0)

If you want to save a few bytes when running a 32-bit version of AutoHotkey, you can use the following:

VarSetCapacity(LVITEM,A_PtrSize=8 ? 88:60,0)
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

03 Nov 2018, 16:09

jballi wrote:
26 Oct 2018, 13:49
So I will use VarSetCapacity(LVITEM,A_PtrSize=8 ? 88:60,0) !

Thank you for helping!
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

22 Nov 2018, 00:34

jballi wrote:
23 Oct 2018, 19:21
@jballi, I modified this last version that you posted in this thread to my like\needs! (If there is something that I should\must change, please let me know!)

- LVM_GetSizeOfLVITEM() function is not necessary anymore (I removed it)
- The function now uses 1-based indexes
- l_Text variable does not consume all the bytes at function call (it consumes all the bytes only if necessary\needed)
- Default Max_bytes set to 64000100 bytes (64 Megabytes, the same default used by AHK for variables)

Code: Select all

	;The default "Max_bytes" the function can Get = 64 Megabytes (Can be changed, Example below)!
	;LVM_GetItemText(LVCtrlId, 3, 2, 256000100), the "Max_bytes" = 256 Megabytes


gui, wait: add, text, w300 h200, Please Wait!!!
gui, wait: show

gui, add, listview, w600 h300 Grid +HwndLVCtrlId, Id|A|B|C
gui, add, button, vGetFocused gGet, Get - Focused row / col2 - data
gui, add, button, x+5 gGet, Get - row3 / col2 - data

loop, 3
LV_ModifyCol(a_index, 100)

loop, 3
LV_ADD(,a_index, "A" a_index "	", "B" a_index "	", "C" a_index "	")

loop, 1114111
text .= chr(a_index)
text .= "`r`nEnd"

LV_Modify(3, "col2", text)

gui, wait: destroy

gui, show

return

Get:		;_____________ Get _________________

gui, wait: add, text, w300 h200, Please Wait!!!
gui, wait: show

gui, Get:Default

gui, destroy

gui, add, edit, w400 h300 +HScroll +HwndEditCtrlId,
gui, add, text, w400 vStrLen +border,

gui, 1:Default

if (a_guicontrol = "GetFocused")
StringFound := LVM_GetItemText(LVCtrlId, LV_GetNext(0, "F"), 2)		;Focused row, 2=col
else
StringFound := LVM_GetItemText(LVCtrlId, 3, 2)		;3=row, 2=col

gui, Get:Default

if (text == StringFound)	;"==" is case sensitive
Result := "Matched"
else
Result := "Un-Matched"

	;guicontrol, , % EditCtrlId, % StringFound
ControlSetText , , % StringFound, % "ahk_id" EditCtrlId

guicontrol, , StrLen, % "String Length = " StrLen(StringFound) "   /   'Text' and 'StringFound' vars " Result "!" 

gui, wait: destroy

gui, show

return

guiclose:	;_______________ gui close _________________
WaitGuiClose:
exitapp


LVM_GetItemText(hLV, p_Item, p_Column := 1, p_Max_bytes := 64000100)	;_______________ LVM_GetItemText(Function) - v1.1 "User" - by "jballi" Autohotkey forum user ____________________
{

	;by "jballi" Autohotkey forum user:
	;(AHK v1.1+ 64-bit now supported): https://autohotkey.com/boards/viewtopic.php?p=245633#p245633

	;p_Max_bytes := 64000100  (64 Megabytes is the default value, the same used by AHK)
	;https://autohotkey.com/docs/commands/_MaxMem.htm#Remarks


Static Dummy53843366
Static LVIF_TEXT:=0x1	;-- LVITEM flags

Static LVM_GETITEMTEXTA:=0x102D		;-- LVM_FIRST + 45 (Message)
Static LVM_GETITEMTEXTW:=0x1073		;-- LVM_FIRST + 115 (Message)

Static sizeofLVITEM := A_PtrSize = 8 ? 88 : 60
;"88" should work with all versions of AutoHotkey (32/64bit) and all versions of Windows (xp+ 32/64bit)
;"60" will be used in order to save few bytes when running 32-bit versions of AutoHotkey

p_Item--, p_Column--	;turns function into 1-based index (row\col 1 is 1, row\col 2 is 2, row\col 3 is 3, and so on ...!)


	;-- Create and populate LVITEM structure

	VarSetCapacity(l_Text, p_Max_bytes)
	;removed "0" from the third\last parameter (prevents "l_Text" variable to consume, for example, all the 64MB as soon as the function is called!)
	;by removing "0" from the third\last parameter, "l_Text" variable will consume the 64MB only if necessary\needed!

		;msgbox, wait - Open "Task manager" and check "Autohotkey.exe" ram usage!

	VarSetCapacity(LVITEM,sizeofLVITEM,0)

    NumPut(LVIF_TEXT,  LVITEM,0,"UInt")                 ;-- mask
    NumPut(p_Item,     LVITEM,4,"Int")                  ;-- iItem
    NumPut(p_Column,   LVITEM,8,"Int")                  ;-- iSubItem
    NumPut(&l_Text,    LVITEM,(A_PtrSize=8) ? 24:20,"Ptr")
        ;-- pszText
    NumPut(p_Max_bytes,LVITEM,(A_PtrSize=8) ? 32:24,"Int")
        ;-- cchTextMax.  Note: This contains the number of TCHARs in the buffer
        ;   pointed to by pszText.

    ;-- Collect text
    SendMessage
        ,% A_IsUnicode ? LVM_GETITEMTEXTW:LVM_GETITEMTEXTA
        ,p_Item
        ,&LVITEM
        ,,ahk_id %hLV%

    VarSetCapacity(l_Text,-1)
    Return l_Text
}
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

22 Nov 2018, 03:41

The function looks good but the 64MB limit is wasteful and could cause an "out of memory" script error on smaller systems. Removing 0 from the third\last parameter of the VarSetCapacity function is correct for the LVM_GETITEMTEXT message but it does not stop the l_Text variable from consuming the 64MB. It only stops AutoHotkey from initializing the space with 0 (null) values.

Here is an alternative version of the function. It uses a fail loop to ensure that all text is collected from the specified ListView row/column. In 99.9+% of the requests, only one loop iteration is required so there should be no performance degradation.

Disclaimer: I've tested the function but it hasn't gone through any system-level testing or performance testing.

Code: Select all

;------------------------------
;
; Function: LVM_GetText
;
; Description:
;
;   Gets the text of a ListView row/column.
;
; Parameters:
;
;   p_Row - 1-based row index.
;
;   p_Column - 1-based column index.
;
;   p_MaxTCHARs - See the *Maximum Characters* section for more information.
;
; Returns:
;
;   Text from the specified row/column.
;
; Maximum Characters:
;
;   The p_MaxTCHARs parameter contains the initial maximum number of characters
;   of text (including a trailing null character) to retrieve.  If the ListView
;   row/subitem contains more text than the limit defined by p_MaxTCHARs, the
;   capacity is doubled and the function will try again.  This "double the
;   capacity and try again" process will continue until all the text for the
;   ListView row/subitem is collected.
;
;   The current default for p_MaxTCHARs parameter is 16384 which is double the
;   current AutoHotkey limit for this request.  This default should allow the
;   function to collect all of the text for most ListView rows/subitems on the
;   first try.  The developer can set this parameter to any value but any large
;   deviation is not recommended.
;
;   If the p_MaxTCHARs parameter is set to a very large value or if the ListView
;   row/subitem contains a very large amount of text, is it possible (although
;   unlikely) for the function to generate a "Out of Memory" script error.
;   Thorough testing is recommended.
;
; Remarks:
;
;   ErrorLevel is set to the number of characters returned.
;
;-------------------------------------------------------------------------------
LVM_GetText(hLV,p_Row,p_Column:=1,p_MaxTCHARs:=16384)
    {
    Static Dummy45758032

          ;-- LVITEM flags
          ,LVIF_TEXT:=0x1

          ;-- Messages
          ,LVM_GETITEMTEXTA:=0x102D                     ;-- LVM_FIRST + 45
          ,LVM_GETITEMTEXTW:=0x1073                     ;-- LVM_FIRST + 115

    ;-- Create and populate LVITEM structure
    VarSetCapacity(LVITEM,A_PtrSize=8 ? 88:60,0)
    NumPut(LVIF_TEXT,LVITEM,0,"UInt")                   ;-- mask
    NumPut(p_Row-1,  LVITEM,4,"Int")                    ;-- iItem
    NumPut(p_Column-1,LVITEM,8,"Int")                   ;-- iSubItem

    ;-- Get text
    ;   Note: A fixed loop limit was added during development/testing to remove
    ;   the possibility of an infinite loop.  It's no longer needed but it
    ;   remains as a fail-safe.
    Loop 9
        {
        VarSetCapacity(l_Text,A_IsUnicode ? p_MaxTCHARs*2:p_MaxTCHARs)
        NumPut(&l_Text,LVITEM,A_PtrSize=8 ? 24:20,"Ptr")
            ;-- pszText
        NumPut(p_MaxTCHARs,LVITEM,A_PtrSize=8 ? 32:24,"Int")
            ;-- cchTextMax.  Note: This contains the number of TCHARs in the
            ;   buffer pointed to by pszText.

        ;-- Get text
        SendMessage
            ,% A_IsUnicode ? LVM_GETITEMTEXTW:LVM_GETITEMTEXTA
            ,p_Row-1
            ,&LVITEM
            ,,ahk_id %hLV%

        ;-- Does the number of characters retrieved equal the maximum possible
        ;   characters minus the trailing null char?  If not, we're done.
        if (ErrorLevel<>p_MaxTCHARs-1)
            Break

        ;-- Double the capacity and try again
        p_MaxTCHARs*=2
        }

    VarSetCapacity(l_Text,-1)
    Return l_Text
    }
I hope this is helpful.

20181122_1313 Edit: Function updated to include VarSetCapacity(l_Text,-1). I originally took this statement out because it wasn't needed in my initial tests but other tests revealed that it is required.
Last edited by jballi on 22 Nov 2018, 14:16, edited 1 time in total.
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

22 Nov 2018, 10:43

jballi wrote:
22 Nov 2018, 03:41
The function looks good but the 64MB limit is wasteful and could cause an "out of memory" script error on smaller systems.
That's right, it may cause an "out of memory" script error on smaller systems.

I tested it out in my system! I have 4gb of ram, while testing, 2.5gb was being used by my system and 1.5gb was free\available! I set Max_byte = 2100100100 = 2gb and as soon as the function was called, "out of memory" script error showed up! (even though "l_Text" variable consumed no byte at all!)

So it means that, if you don't have 64MB of ram free/available in your system, "out of memory" script error shows up as soon as the function is called!

I think the above is AutoHotKey fault though, and should be fixed! (the "out of memory" script error should show up only if the "AutoHotKey.exe" process itself, while consuming bytes, finds out that there is no more "bytes" to consume from ram or, for example, fixes a limit, while consuming bytes, if free ram available = 100 MB => "out of memory" script error shows up! )

The fail loop method seems a great solution, will test it out and let you know later what I think about it!
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

22 Nov 2018, 14:18

Bug fix in the LVM_GetText function. See my previous post for the details.
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

22 Nov 2018, 20:45

jballi wrote:
22 Nov 2018, 03:41

Tested the fail loop method and it works!

I noticed one issue, if VarSetCapacity(l_Text,A_IsUnicode ? p_MaxTCHARs*2:p_MaxTCHARs) is changed to VarSetCapacity(l_Text,p_MaxTCHARs) the below problem occurs:

fail loop method.png
fail loop method.png (14.73 KiB) Viewed 4060 times

Do you know why? Any workaround? (I want to use "max_byte" parameter instead "max_character" )

Working test code:

Code: Select all

gui, wait: add, text, w300 h200, Please Wait!!!
gui, wait: show

gui, add, listview, w600 h300 Grid +HwndLVCtrlId, Id|A|B|C
gui, add, button, vGetFocused gGet, Get - Focused row / col2 - data
gui, add, button, x+5 gGet, Get - row3 / col2 - data

loop, 3
LV_ModifyCol(a_index, 100)

loop, 3
LV_ADD(,a_index, "A" a_index "	", "B" a_index "	", "C" a_index "	")

loop, 1114111
text .= chr(a_index)
text .= "`r`nEnd"

LV_Modify(3, "col2", text)

gui, wait: destroy

gui, show

return

Get:		;_____________ Get _________________

gui, wait: add, text, w300 h200, Please Wait!!!
gui, wait: show

gui, Get:Default

gui, destroy

gui, add, edit, w400 h300 +HScroll +HwndEditCtrlId,
gui, add, text, w400 vStrLen +border,

gui, 1:Default

if (a_guicontrol = "GetFocused")
StringFound := LVM_GetText(LVCtrlId, LV_GetNext(0, "F"), 2)		;Focused row, 2=col
else
StringFound := LVM_GetText(LVCtrlId, 3, 2)		;3=row, 2=col

gui, Get:Default

if (text == StringFound)	;"==" is case sensitive
Result := "Matched"
else
Result := "Un-Matched"

	;guicontrol, , % EditCtrlId, % StringFound
ControlSetText , , % StringFound, % "ahk_id" EditCtrlId

guicontrol, , StrLen, % "String Length = " StrLen(StringFound) "   /   'Text' and 'StringFound' vars " Result "!" 

gui, wait: destroy

gui, show

return

guiclose:	;_______________ gui close _________________
WaitGuiClose:
exitapp


;------------------------------
;
; Function: LVM_GetText
;
; Description:
;
;   Gets the text of a ListView row/column.
;
; Parameters:
;
;   p_Row - 1-based row index.
;
;   p_Column - 1-based column index.
;
;   p_MaxTCHARs - See the *Maximum Characters* section for more information.
;
; Returns:
;
;   Text from the specified row/column.
;
; Maximum Characters:
;
;   The p_MaxTCHARs parameter contains the initial maximum number of characters
;   of text (including a trailing null character) to retrieve.  If the ListView
;   row/subitem contains more text than the limit defined by p_MaxTCHARs, the
;   capacity is doubled and the function will try again.  This "double the
;   capacity and try again" process will continue until all the text for the
;   ListView row/subitem is collected.
;
;   The current default for p_MaxTCHARs parameter is 16384 which is double the
;   current AutoHotkey limit for this request.  This default should allow the
;   function to collect all of the text for most ListView rows/subitems on the
;   first try.  The developer can set this parameter to any value but any large
;   deviation is not recommended.
;
;   If the p_MaxTCHARs parameter is set to a very large value or if the ListView
;   row/subitem contains a very large amount of text, is it possible (although
;   unlikely) for the function to generate a "Out of Memory" script error.
;   Thorough testing is recommended.
;
; Remarks:
;
;   ErrorLevel is set to the number of characters returned.
;
;-------------------------------------------------------------------------------
LVM_GetText(hLV,p_Row,p_Column:=1,p_MaxTCHARs:=16384)
    {
    Static Dummy45758032

          ;-- LVITEM flags
          ,LVIF_TEXT:=0x1

          ;-- Messages
          ,LVM_GETITEMTEXTA:=0x102D                     ;-- LVM_FIRST + 45
          ,LVM_GETITEMTEXTW:=0x1073                     ;-- LVM_FIRST + 115

    ;-- Create and populate LVITEM structure
    VarSetCapacity(LVITEM,A_PtrSize=8 ? 88:60,0)
    NumPut(LVIF_TEXT,LVITEM,0,"UInt")                   ;-- mask
    NumPut(p_Row-1,  LVITEM,4,"Int")                    ;-- iItem
    NumPut(p_Column-1,LVITEM,8,"Int")                   ;-- iSubItem

    ;-- Get text
    ;   Note: A fixed loop limit was added during development/testing to remove
    ;   the possibility of an infinite loop.  It's no longer needed but it
    ;   remains as a fail-safe.
    Loop 9
        {
        VarSetCapacity(l_Text,A_IsUnicode ? p_MaxTCHARs*2:p_MaxTCHARs)
        NumPut(&l_Text,LVITEM,A_PtrSize=8 ? 24:20,"Ptr")
            ;-- pszText
        NumPut(p_MaxTCHARs,LVITEM,A_PtrSize=8 ? 32:24,"Int")
            ;-- cchTextMax.  Note: This contains the number of TCHARs in the
            ;   buffer pointed to by pszText.

        ;-- Get text
        SendMessage
            ,% A_IsUnicode ? LVM_GETITEMTEXTW:LVM_GETITEMTEXTA
            ,p_Row-1
            ,&LVITEM
            ,,ahk_id %hLV%

        ;-- Does the number of characters retrieved equal the maximum possible
        ;   characters minus the trailing null char?  If not, we're done.
        if (ErrorLevel<>p_MaxTCHARs-1)
            Break

        ;-- Double the capacity and try again
        p_MaxTCHARs*=2
        }

    VarSetCapacity(l_Text,-1)
    Return l_Text
    }
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

23 Nov 2018, 03:37

The LVM_GETITEMTEXT message uses the LVITEM structure to determine what's what. The pszText member contain the address to the space where the retrieved text is stored. The cchTextMax member must be set to the number of characters have been allocated. The amount of space necessary depends on whether you are using ANSI or Unicode. If using ANSI, every character uses 1 byte of space. If using Unicode (UTF-16), every character uses 2 bytes of space.

So... when you changed VarSetCapacity(l_Text,A_IsUnicode ? p_MaxTCHARs*2:p_MaxTCHARs) to VarSetCapacity(l_Text,p_MaxTCHARs), you told the LVM_GETITEMTEXT message that you had allocated twice as much space than you actually allocated. In other words, you allocated half as much space as you needed so when the message tried to access memory that wasn't there, you get an exception and AutoHotkey crashes.
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

23 Nov 2018, 10:43

jballi wrote:
23 Nov 2018, 03:37
Well, I will pretend that I understood what you post above (which I didn't), but anyway, I realized that with the fail loop method above, I don't need to worry about "p_maxChar" or "p_maxbyte", in fact, I will remove those parameters from the function! (I will set it internally inside the function to 1000100 characters, which is 1MB for ANSI? and 2MB for Unicode?)

By the way, do you have anything against removing "p_maxChar" or "p_maxbyte" parameters? Do you think it's unsafe or something?
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: [ListView] How to force "LV_GetText()" function to get text longer than 8191 characters?

23 Nov 2018, 16:35

User wrote:
23 Nov 2018, 10:43
do you have anything against removing "p_maxChar" or "p_maxbyte" parameters? Do you think it's unsafe or something?
The p_MaxTCHARs parameter has a default value that is meant to be ignored (read: unspecified) in most cases. For example:

Code: Select all

StringFound:=LVM_GetText(hLV,LV_GetNext(0,"F"),1)
However, if you know that you are getting text from a row/column that contains a lot of text, you can specify a larger value. For example,

Code: Select all

StringFound:=LVM_GetText(hLV,LV_GetNext(0,"F"),2,1048576)
Removing the p_MaxTCHARs parameter from the function takes away the option of specifying a larger default value when needed.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Chunjee, jaka1, LuckyJoe, Rohwedder and 335 guests