Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

comdlg32.dll - unable to resize

Status
Not open for further replies.

AlastairP

Technical User
Feb 8, 2011
286
AU
I am using comdlg32.dll to select multiple files, when the dialog box appears, I am unable to resize the window.
I am sure I have seen the exact same dialog box used in Windows and other programs and it has the resize grab point in the lower right corner.

I can't remember where I got the code from, so thanks to the original developer, and as the class was publicly available, posting code here should be ok??

Load Dlls:
Code:
DECLARE INTEGER GetSaveFileNameA IN comdlg32.dll AS _FFC_SAVEFILENAME STRING @
DECLARE INTEGER GetOpenFileNameA IN comdlg32.dll AS _FFC_GETFILENAME STRING @
DECLARE INTEGER malloc IN msvcrt.dll AS _FFC_MALLOC INTEGER
DECLARE free in msvcrt.dll AS _FFC_FREEMEM INTEGER

DialogHandler:
Code:
#DEFINE OFN_READONLY                 0x00000001
#DEFINE OFN_OVERWRITEPROMPT          0x00000002
#DEFINE OFN_HIDEREADONLY             0x00000004
#DEFINE OFN_NOCHANGEDIR              0x00000008
#DEFINE OFN_SHOWHELP                 0x00000010
#DEFINE OFN_ENABLEHOOK               0x00000020
#DEFINE OFN_ENABLETEMPLATE           0x00000040
#DEFINE OFN_ENABLETEMPLATEHANDLE     0x00000080
#DEFINE OFN_NOVALIDATE               0x00000100
#DEFINE OFN_ALLOWMULTISELECT         0x00000200
#DEFINE OFN_EXTENSIONDIFFERENT       0x00000400
#DEFINE OFN_PATHMUSTEXIST            0x00000800
#DEFINE OFN_FILEMUSTEXIST            0x00001000
#DEFINE OFN_CREATEPROMPT             0x00002000
#DEFINE OFN_SHAREAWARE               0x00004000
#DEFINE OFN_NOREADONLYRETURN         0x00008000
#DEFINE OFN_NOTESTFILECREATE         0x00010000
#DEFINE OFN_NONETWORKBUTTON          0x00020000		&& for old style dialog
#DEFINE OFN_NOLONGNAMES              0x00040000     && force no long names for 4.x modules
#DEFINE OFN_EXPLORER                 0x00080000     && new look commdlg
#DEFINE OFN_NODEREFERENCELINKS       0x00100000
#DEFINE OFN_LONGNAMES                0x00200000     && force long names for 3.x modules
#DEFINE OFN_ENABLEINCLUDENOTIFY      0x00400000     && send include message to callback
#DEFINE OFN_ENABLESIZING             0x00800000
#DEFINE OFN_EX_NOPLACESBAR         	 0x00000001		&& used for newer OS only (uses FlagsEx)


LOCAL lStructSize, hwndOwner, hInstance,;
cMyFilter, i, lpMyFilter,;
nMaxCustomFilter, cUserCustomFilter, lpUserCustomFilter,;
nMaxFileName, cFileName, lpFileName,;
nMaxFileTitle, cFileTitle, lpFileTitle
LOCAL lpInitialDirectory, lpTitlebarText, nFlags, nFlagsEx, ;
nFileOffset, nFileExtension, cDefExt,;
lpDefExt, lCustomData, lpHook, lpTemplateName,;
cMyStruct, nReturnVal, nFileNameOffset 
LOCAL lcFileName, lcUserCustomFilter, lcFileTitle,;
nCurrentFilePos, nArraySize, nNextFilePos 

hwndOwner = THIS.nHwndOwner
hInstance = THIS.nInstance
nFlags = 0
nFlags = OFN_ENABLEHOOK        
nFlagsEx = 0

*  Build the filter string.
cMyFilter = ""
FOR i = 1 TO ALEN(THIS.aFilterList, 1)
	cMyFilter = cMyFilter + THIS.aFilterList[m.i,1] + CHR(0) + THIS.aFilterList[m.i,2] + CHR(0)
ENDFOR
cMyFilter = cMyFilter + + REPL(CHR(0),2)
lpMyFilter = _FFC_MALLOC(LEN(cMyFilter))
IF lpMyFilter = 0
	* Couldn't allocate memory
	RETURN ""
ENDIF

*  Setup Flags
IF THIS.lNewExplorer
	nFlags = nFlags + OFN_EXPLORER
ENDIF
IF THIS.lHideReadOnly
	nFlags = nFlags + OFN_HIDEREADONLY 
ENDIF
IF THIS.lFileMustExist
	nFlags = nFlags + OFN_FILEMUSTEXIST 
ENDIF
IF THIS.lNoNetworkButton
	* Old style dialogs only
	nFlags = nFlags + OFN_NONETWORKBUTTON 
ENDIF
IF THIS.lNoChangeDir
	nFlags = nFlags + OFN_NOCHANGEDIR 
ENDIF
IF THIS.lNoValidate
	nFlags = nFlags + OFN_NOVALIDATE
ENDIF
IF THIS.lAllowMultiSelect AND !THIS.lSaveDialog
	nFlags = nFlags + OFN_ALLOWMULTISELECT 
ENDIF

*  Setup FlagsEx
IF THIS.lnoplacesbar
	nFlagsEx = nFlagsEx + OFN_EX_NOPLACESBAR
ENDIF

DECLARE INTEGER memcpy IN msvcrt.dll AS _FFC_MEMCPY  INTEGER , STRING @, INTEGER

_FFC_MEMCPY(lpMyFilter, @cMyFilter, len(cMyFilter))

*  Prepare the custom filter string
nMaxCustomFilter = 1024
cUserCustomFilter = REPL(CHR(0), nMaxCustomFilter)
lpUserCustomFilter = _FFC_MALLOC(nMaxCustomFilter)
IF lpUserCustomFilter = 0
	* Couldn't allocate memory
	_FFC_FREEMEM(lpMyFilter)
	RETURN ""
ENDIF
_FFC_MEMCPY(lpUserCustomFilter, @cUserCustomFilter, nMaxCustomFilter)

* Prepare the string for the selected filename(s)
nMaxFileName = 1024
cFileName = LEFT(THIS.cFileName, 1023) + REPL(CHR(0), nMaxFileName - MIN(LEN(THIS.cFileName),1023))
lpFileName = _FFC_MALLOC(nMaxFileName)
IF lpFileName = 0
	_FFC_FREEMEM(lpMyFilter)
	_FFC_FREEMEM(lpUserCustomFilter)
ENDIF
_FFC_MEMCPY(lpFileName, @cFileName, nMaxFileName)

*  File Title
nMaxFileTitle = 300
cFileTitle  = REPL(CHR(0), nMaxFileTitle)
lpFileTitle = _FFC_MALLOC(nMaxFileTitle)
IF lpFileTitle = 0
	_FFC_FREEMEM(lpMyFilter)
	_FFC_FREEMEM(lpUserCustomFilter)
	_FFC_FREEMEM(lpFileName)
ENDIF
_FFC_MEMCPY(lpFileTitle, @cFileTitle, nMaxFileTitle)

*  Initial Directory
lpInitialDirectory = _FFC_MALLOC(266)
IF lpInitialDirectory = 0
	_FFC_FREEMEM(lpMyFilter)
	_FFC_FREEMEM(lpUserCustomFilter)
	_FFC_FREEMEM(lpFileName)
	_FFC_FREEMEM(lpFileTitle)
ENDIF		
_FFC_MEMCPY(lpInitialDirectory, THIS.cInitialDirectory + CHR(0), MIN(LEN(THIS.cInitialDirectory) + 1, 260))

* Title bar text
lpTitlebarText = _FFC_MALLOC(Len(THIS.cTitlebartext) + 2)
_FFC_MEMCPY(lpTitlebarText, THIS.cTitlebartext + CHR(0) + CHR(0), LEN(THIS.cTitlebarText) + 2)

nFileOffset = 0
nFileExtension = 0

* Default Extension
cDefExt = "TXT" + CHR(0) + CHR(0)
lpDefExt = _FFC_MALLOC(LEN(THIS.cDefaultExtension) + 1)
_FFC_MEMCPY(lpDefExt, THIS.cDefaultExtension + CHR(0), LEN(THIS.cDefaultExtension) + 1)

lCustomData = 0	&& not used without a hook
lpHook = 0
lpTemplateName = 0

lStructSize = IIF(VAL(OS(3)) > 4, 22, 19) * 4

cMyStruct = THIS.IntegerToString(lStructSize,4) + ;
	THIS.IntegerToString(hwndOwner,4) + ;
	THIS.IntegerToString(hInstance,4) + ;
	THIS.IntegerToString(lpMyFilter,4) + ;
	THIS.IntegerToString(lpUserCustomFilter,4) + ;
	THIS.IntegerToString(nMaxCustomFilter,4) + ;
	THIS.IntegerToString(THIS.nFilterIndex,4) + ;
	THIS.IntegerToString(lpFileName,4) + ;
	THIS.IntegerToString(nMaxFileName,4) + ;
	THIS.IntegerToString(lpFileTitle,4) + ;
	THIS.IntegerToString(nMaxFileTitle,4) + ;
	THIS.IntegerToString(lpInitialDirectory,4) + ;
	THIS.IntegerToString(lpTitleBarText,4) + ;
	THIS.IntegerToString(nFlags,4) + ;
	THIS.IntegerToString(nFileOffset,2) + ;
	THIS.IntegerToString(nFileExtension,2) + ;
	THIS.IntegerToString(lpDefExt,4) + ;
	THIS.IntegerToString(lCustomData,4) + ;
	THIS.IntegerToString(lpHook,4) + ;
	THIS.IntegerToString(lpTemplateName,4)

IF VAL(OS(3)) > 4
	cMyStruct = cMyStruct + ;
	THIS.IntegerToString(0,4) + ;
	THIS.IntegerToString(0,4) + ;
	THIS.IntegerToString(nFlagsEx,4)
ENDIF

* Call the dialog now
IF THIS.lsavedialog
	nReturnVal = _FFC_SAVEFILENAME(@cMyStruct)
ELSE
	nReturnVal = _FFC_GETFILENAME(@cMyStruct)
ENDIF

IF nReturnVal = 1
	*  Now retrieve info from allocated strings		
	*  Retrieve Filename string
	DECLARE INTEGER memcpy in msvcrt.dll AS	_FFC_MEMCPY2 STRING @, INTEGER , INTEGER
	lcFileName = REPL(CHR(0),nMaxFileName)
	_FFC_MEMCPY2(@lcFileName, lpFileName, nMaxFileName)
	nFileNameOffset = THIS.StringToInteger(SUBSTR(cMyStruct, 14*4+1, 2), 2)

	*  Check if user selected multiple files.
	IF THIS.lAllowMultiSelect AND nFileNameOffset > 1 AND SUBSTR(lcFileName, nFileNameOffset, 1) = CHR(0)
		*Now parse out to get multiple file names
		nCurrentFilePos = AT(CHR(0), lcFileName) + 1
		THIS.cFilePath = LEFT(lcFileName, nCurrentFilePos - 2)
		nArraySize = 1
		DO WHILE .T.
			IF SUBSTR(lcFileName, nCurrentFilePos, 1) = CHR(0)
				* end of list.
				EXIT
			ENDIF
			DIMENSION THIS.aFileNames[nArraySize]
			nNextFilePos = AT(CHR(0), lcFileName, nArraySize + 1) + 1
			THIS.aFileNames[nArraySize] = SUBSTR(lcFileName, nCurrentFilePos, nNextFilePos - nCurrentFilePos - 1)
			nArraySize = nArraySize + 1
			nCurrentFilePos = nNextFilePos
		ENDDO
		THIS.nFileCount = nArraySize - 1
	ELSE
		*  Didn't multiselect, so there's just one filename.
		lcFileName = LEFT(lcFileName, AT(CHR(0), lcFileName) - 1)
		THIS.cFilePath = JUSTPATH(lcFileName)
		DIMENSION THIS.aFileNames[1]
		THIS.aFileNames[1] = JUSTFNAME(lcFileName)
		THIS.nFileCount = 1
	ENDIF
			
	THIS.cFileName = LEFT(lcFileName, AT(CHR(0), lcFileName) - 1)

	lcUserCustomFilter= repl(chr(0), nMaxCustomFilter)
	_FFC_MEMCPY2(@lcUserCustomFilter, lpUserCustomFilter, nMaxCustomFilter)
	THIS.cCustomFilter= LEFT(lcUserCustomFilter, AT(CHR(0), lcUserCustomFilter) - 1)

	lcFileTitle = repl(chr(0), nMaxFileTitle)
	_FFC_MEMCPY2(@lcFileTitle, lpFileTitle, nMaxFileTitle)
	THIS.cFileTitle = LEFT(lcFileTitle , AT(CHR(0), lcFileTitle ) - 1)
			
	THIS.nFilterIndex = THIS.StringToInteger(SUBSTR(cMyStruct, 6*4+1, 4), 4)
ENDIF

_FFC_FREEMEM(lpMyFilter)
_FFC_FREEMEM(lpUserCustomFilter)
_FFC_FREEMEM(lpFileName)
_FFC_FREEMEM(lpFileTitle)
_FFC_FREEMEM(lpInitialDirectory)
_FFC_FREEMEM(lpTitleBarText)
_FFC_FREEMEM(lpDefExt)
		
RETURN nReturnVal
 
Hi Olaf,
So I can select multiple files. I can't see that GETFILE() does that.
 
Hi tbleken,
Actually that is the class I am using, and the dialog handler method I posted is from the _system.vcx class that is used for that dialog box.
But the question is: how can I make the dialog able to be resized. It is so small . . .

 
Do you simply want to make it bigger, or do you want your users to be able to resize it? For number one, you can simply change the form's width and height properties. For number two you may need to add some code.
 
Alastair,

You seem to be missing a flag. In your #DEFINE list, you need this setting:

[tt]#DEFINE OFN_ENABLESIZING 0xH800000[/tt]

Then, in the Setup Flags section of your code, add [tt]OFN_ENABLESIZING[/tt] to the cumulative value of [tt]nFlags[/tt].

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
It's in there as

[tt]#DEFINE OFN_ENABLESIZING 0x00800000[/tt]

Anyway, it's not used. You may want to initialize your nFlags via [tt]nFlags = OFN_ENABLEHOOK + OFN_ENABLESIZING[/tt], if you want this to always be a feature. It means to change the ffc class in home, unfortunately the code offers no hook to change nFlags from outside, from using the class. But it should be okay to make that change in _comdlg of _system.vcx

Bye, Olaf.
 
Subclassing is an option, if you can thereby hook into the class behaviour in a good way. In this case I see no real gain from that. The shown DialogHandler method is the one you'd need to extend, but you can't set nFlags = OFN_ENABLESIZING and then DODEFAULT(). One option would be to simply copy this code into your subclass and don't make a DODEFAULT() call anywhere, meaning to override the method. An override is a unelegant subclass change, but it's needed in this case, as this method defines nFlags LOCAL, intializes, changes and uses this variable in creating a struct for passing to the API call. You can't hook into this at any point. I see no way to elegantly only add in another property like THIS.lNewExplorer and all the other properties already defined to control nFlags, for example just add in THIS.lEnableResizing. You'd also need to put in another IF statement to make use of that new flag property and that's not possible in subclassing, there is no call DODEFAULT("but in line 5343 put in IF....ENDIF").

The only advantage of overriding a method is that the original class is kept as is and any other project using it still has the old behaviour. Bot overriding a method by copying it and extending it, means you copy code. You cut off inheritance. Only all other methods are inherited as is, but whenever _cmddlg.DialogHandler code changes, the same change has to be made in your subclass and that breaks the intention of inheritance.

A good class design would do a call like nFlags = THIS.AddFurtherFlags(nFlags) with an initial implementation of LPARAMETERS tnFlags / RETURN tnFlags. That could then be extended to add further flags not known at the implementation of the original class, eg to be able to add flags of future API function versions of future OSes. Or the code would more generically processs all flag properties starting with lFlag prefix or offer an initialFlags int property to name a few ideas for a better class design. But all these ideas address the already existing code and class level.

As we all know the FFC classes are not the best MS could have provided from their architecture and usability.

Bye, Olaf.
 
Personally, I would favour modifying the original class. I only suggested sub-classing as another possible approach. And I wouldn't bother to make resizing an option. I would prefer to do it such that the dialogue is always resizable - on the basis that there is no benefit to the user in not being able to resize it.

But before we go any further with this, perhaps we better hear back from Alastair to find out if my fix actually works. I haven't tested it myself.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi Mike,
yes I have tested that and it works.
I just modified the base class, as that is the kind of default behaviour expected from users
Thank you.


Alastair
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top