Archive: GetSaveFileName in comdlg32 trouble


GetSaveFileName in comdlg32 trouble
I've read the System Docs and I've researched the Wiki and internet as much as I can with no result.

What I want to do: Within a section during instfiles I want to use the System Plugin to call a "Save File As..." Dialog. After much research I've found that the WinAPI comdlg32.dll function, GetSaveFileName, does just that. The tricky part is the setup for the call :( . Here's what I have so far... Any help would be nice :D

SAM

Sources:
http://msdn2.microsoft.com/en-us/library/ms646839.aspx
http://www.swfkit.com/swfkit/doc/manual/node37.html
http://nsis.sourceforge.net/Calling_...tem.dll_plugin
http://nsis.sourceforge.net/System_p...adme#newstruct
http://source.winehq.org/source/prog...mdlgtst.c#L212

  SetPluginUnload alwaysoff
; Setup a structure for allowed file types
; Build Struc #1
System::Call '*(t "Text Files (*.txt), *.txt;", t "All Files (*.*), *.*;") *i .r0'

; Setup the OPENFILENAME structure and store it into $2 (returns path and filename into structure $1)
; Build Struc #2
System::Call '*(i 23, t $HWNDPARENT, v, *i r0, i 0, i 0, i 0, *i r1, i 260, v, v, i 0, t "Open File", i 0x80000, 0, 0, "*", v, v, v, v, v, v, v) *i .r2'
; Open Save file dialog and return a bool into $3
System::Call 'comdlg32::GetSaveFileName(*i r2)'
Pop $3

; Now get the path and file name and put them together
System::Call '*$1( t r4, t r5)'
IntOp $6 $4 & $5
; MessageBox to the user the path/file that they chose
MessageBox MB_OK "Save File As Info...$\n\
Path: $4$\n\
FileName: $5$\n\
Whole String: $6"

; Unload the plugin from memory
SetPluginUnload manual
System::Free $0
System::Free $1
System::Free $2
System::Free 0

I am still having progress but all of my values aren't quite accounted for because it is still not working. However I found this nice article that shows structures being made with nsis script.

http://publicsvn.songbirdnest.com/br...?rev=5425#L143

SAM


;typedef struct tagOFN {
; DWORD lStructSize;
; HWND hwndOwner;
; HINSTANCE hInstance;
; LPCTSTR lpstrFilter;
; LPTSTR lpstrCustomFilter;
; DWORD nMaxCustFilter;
; DWORD nFilterIndex;
; LPTSTR lpstrFile;
; DWORD nMaxFile;
; LPTSTR lpstrFileTitle;
; DWORD nMaxFileTitle;
; LPCTSTR lpstrInitialDir;
; LPCTSTR lpstrTitle;
; DWORD Flags;
; WORD nFileOffset;
; WORD nFileExtension;
; LPCTSTR lpstrDefExt;
; LPARAM lCustData;
; LPOFNHOOKPROC lpfnHook;
; LPCTSTR lpTemplateName;
;#if (_WIN32_WINNT >= 0x0500)
; void * pvReserved;
; DWORD dwReserved;
; DWORD FlagsEx;
;#endif // (_WIN32_WINNT >= 0x0500)
;} OPENFILENAME, *LPOPENFILENAME;
SetPluginUnload alwaysoff
;System::Call '*(i 23, i, *i, *i "All Files (*.*), *.*", *i, i, i 0, *i "returned path/name", &t260, i, i, i, i, i 0x80000, i, i, i, i, i, i, i, i, i, i) i .r0'
; Setup a structure for allowed file types
; Build Struc #1
!define FILE_FILTER '(t "Text Files (*.txt), *.txt", t "All Files (*.*), *.*") i'
; Setup the OPENFILENAME structure and store it into $2 (returns path and filename into structure $1)
; Build Struc #2
System::Call '*(&l, i $HWNDPARENT, i, i $FILE_FILTER, i, i, i, i r1, &t260, i, i, i, t "Open File", i 0x80000, i, i, i, i, i, i, i, i, i, i) i .r2'
; Open Save file dialog and return a bool into $3
System::Call 'comdlg32::GetSaveFileName(*i r2)'
Pop $3
MessageBox MB_OK $3
; Now get the path and file name and put them together
System::Call '*$1( t r4, t r5)'
IntOp $6 $4 & $5
; MessageBox to the user the path/file that they chose
MessageBox MB_OK "Save File As Info...$\n\
Path: $4$\n\
FileName: $5$\n\
Whole String: $6"

; Unload the plugin from memory
SetPluginUnload manual
;System::Free $0
;System::Free $1
;System::Free $2
System::Free 0

The easy way: http://nsis.sourceforge.net/Dialogs_plug-in

---

HINSTANCE should be just i
strings should be t and not *i
for a define, you use ${define} and not $define


what exactly are pointers?

I want to do it this way because the parameters for creating this save file dialog are much in depth.
SAM


Okay,
Now I think I'm making good progress. I have all of my parameters set (i think) but an essential one. I don't know what wParams should be when I do the SendMessage command to initiate the dialog.
SAM

  SetPluginUnload alwaysoff
; Allowed file types
!define FILE_FILTER "HTML Files (*.htm*)|*.htm*|Images (*.gif;*.jp*;*.png)|*.gif;*.jp*;*.png|All Files (*.*)|*.*|"
IntOp $0 "OFN_ENABLEHOOK" || 1
MessageBox MB_OK $0

; Setup the OPENFILENAME structure and store it into LPARAM (returns path and filename into structure $1)
!define LPARAM '*(&l, i $HWNDPARENT, i, t ${FILE_FILTER}, i, i, i, i r1, &t260, i, i, i, t "Open File", i r0, &t260, &t14, t "exe", t , i, i, i, i, i, i) i'

; Open Save file dialog and return a bool into $3
SendMessage $HWNDPARENT ${WM_INITDIALOG} wParam=IDontKnow ${LPARAM} $3
MessageBox MB_OK $3
; Now get the path and file name and put them together
System::Call '*$1( t r4, t r5)'
IntOp $6 $4 & $5
; MessageBox to the user the path/file that they chose
MessageBox MB_OK "Save File As Info...$\n\
Path: $4$\n\
FileName: $5$\n\
Whole String: $6"

; Unload the plugin from memory
SetPluginUnload manual
System::Free 0

there should not be a sendmessage, you need to call the GetSaveFileName function with the address of your struct.

Why not just use the plugin I linked to?


Yes... the plugin would be easier, check the samples.


Link: http://msdn2.microsoft.com/en-us/library/ms646839.aspx
Look at the Flags section of the OPENFILENAME structure. I want to access several of those properties that the plugin you linked to doesn't access as far as I can tell. Thanks for the link though. When I'm working on a simpler project that would be perfect! Although your reference to that plugin helped me resolve a couple of my issues :)

I just haven't found the right combination yet to get this thing going. Anders you're right. I found SendMessage doesn't work just after posting it. It only handles 4-6 args and I was passing it 43 with that combination. I went back to my old method but a little tweaked.

  SetPluginUnload alwaysoff
; Setup a structure for allowed file types
; Build Struc #1
!define FILE_FILTER "HTML Files (*.htm*)|*.htm*|Images (*.gif;*.jp*;*.png)|*.gif;*.jp*;*.png|All Files (*.*)|*.*|"
IntOp $0 "OFN_EXPLORER" || 1
;IntOp $0 $0 && "OFN_OVERWRITEPROMPT"
;IntOp $0 $0 || 1
MessageBox MB_OK $0

; Setup the OPENFILENAME structure and store it into $2 (returns path and filename into string $1)
; Build Struc #2
!define LPARAM '*(&l, i $HWNDPARENT, i, t ${FILE_FILTER}, i, i, i, t .r1, &t260, i, i, i, t "Open File", i r0, &t260, &t14, t "exe", i, i, i, i, i 0, i 0) i' ;x23
; Open Save file dialog and return a bool into $3
System::Call 'comdlg32::GetSaveFileName(i ${LPARAM}) b .r3'
MessageBox MB_OK "$1$\n$3"

; Unload the plugin from memory
SetPluginUnload manual
System::Free 0


With GetSaveFileName(i ${LPARAM}), what data type is the structure LPARAM? Because it holds several different data types but I guess I'm going to keep it i unless anyone says otherwise. I simply don't see what I'm missing. I've got 23 slots in the OPENFILENAME. I must be labeling data types wrong.
Any suggestions other than saying to move on?
SAM

Here is a new version of the code...still doesn't work but I believe I continue to get closer to a solution.

  SetPluginUnload alwaysoff
; Setup a structure for allowed file types
; Build Struc #1
!define FILE_FILTER "HTML Files (*.htm*)|*.htm*|Images (*.gif;*.jp*;*.png)|*.gif;*.jp*;*.png|All Files (*.*)|*.*|"

; define and set the flags I want to use
Var /GLOBAL FLAG
IntOp $FLAG "OFN_EXPLORER" || 1
!define _OFN_EXPLORER $FLAG
IntOp $FLAG "OFN_OVERWRITEPROMPT" || 1
!define _OFN_OVERWRITEPROMPT $FLAG
IntOp $FLAG "OFN_DONTADDTORECENT" || 1
!define _OFN_DONTADDTORECENT $FLAG
IntOp $FLAG "OFN_NONETWORKBUTTON" || 1
!define _OFN_NONETWORKBUTTON $FLAG
!define SET_FLAGS '${_OFN_EXPLORER}|${_OFN_OVERWRITEPROMPT}|${_OFN_DONTADDTORECENT}|${_OFN_NONETWORKBUTTON}'
MessageBox MB_OK ${SET_FLAGS} ; debugging purposes

; Setup the OPENFILENAME structure and store it into $2 (returns path and filename into string $1)
; Build Struc #2
!define LPARAM '*(&l, i $HWNDPARENT, i, t ${FILE_FILTER}, i, i, i, t .r1, &t260, i, i, i, t "Open File", i ${SET_FLAGS}, &t3, &t14, t "exe", i, i, i, v, i 0, i 0) i' ;x23
; Open Save file dialog and return a bool into $3
System::Call 'comdlg32::GetSaveFileName(i ${LPARAM}) b .r3'
MessageBox MB_OK "$1$\n$3" ; debugging purposes

; Unload the plugin from memory
SetPluginUnload manual
System::Free 0


And here is a list of all the datatypes that I believe are in the define LPARAM.

;typedef struct tagOFN {
; &l DWORD lStructSize;
; i HWND hwndOwner;
; i HINSTANCE hInstance;
; t LPCTSTR lpstrFilter;
; t LPTSTR lpstrCustomFilter;
; i DWORD nMaxCustFilter;
; i DWORD nFilterIndex;
; t LPTSTR lpstrFile;
; i DWORD nMaxFile;
; t LPTSTR lpstrFileTitle;
; i DWORD nMaxFileTitle;
; t LPCTSTR lpstrInitialDir;
; t LPCTSTR lpstrTitle;
; i DWORD Flags;
; &t WORD nFileOffset;
; &t WORD nFileExtension;
; t LPCTSTR lpstrDefExt;
; ? LPARAM lCustData;
; ? LPOFNHOOKPROC lpfnHook;
; t LPCTSTR lpTemplateName;
;#if (_WIN32_WINNT >= 0x0500)
; v void * pvReserved;
; i DWORD dwReserved;
; i DWORD FlagsEx;
;#endif // (_WIN32_WINNT >= 0x0500)
;} OPENFILENAME, *LPOPENFILENAME;


System.dll data types are listed first, then general data types, and then parameter names. The only two parameters that I don't know what type of data is lCustData and lpfnHook.

I'm going to keep updating my progress on here every once in a while in case anyone has ideas to contribute or even just interested.

SAM

they are probaby i or *i, still not sure why you keep calling the struct LPARAM, its called OPENFILENAME, and I'm not even sure if you can pass a struct this way to a function with just a define.

and your flags code is way off, try googling for "define OFN_EXPLORER" and use the actual number (or install the platform sdk)


I'm calling the struct because thats what it says on MSDN.
http://msdn2.microsoft.com/en-us/library/ms646928.aspx

Or maybe I'm just not quite sure how to use the pointer i* correctly to refer it to OPENFILENAME. I did a small code change, maybe now it makes more sense (didn't fix the flags though.) I only did the flags that way with the IntOp because it worked on this other system call for setting "SND_ASYNC":
http://nsis.sourceforge.net/WinAPI:winmm:PlaySound

So I figured that it automatically converted it to that integer for me rather than having to look it up.

  SetPluginUnload alwaysoff
; Setup a structure for allowed file types
; Build Struc #1
!define FILE_FILTER "HTML Files (*.htm*)|*.htm*|Images (*.gif;*.jp*;*.png)|*.gif;*.jp*;*.png|All Files (*.*)|*.*|"

; define and set the flags I want to use
Var /GLOBAL FLAG
IntOp $FLAG "OFN_EXPLORER" || 1
!define _OFN_EXPLORER $FLAG
IntOp $FLAG "OFN_OVERWRITEPROMPT" || 1
!define _OFN_OVERWRITEPROMPT $FLAG
IntOp $FLAG "OFN_DONTADDTORECENT" || 1
!define _OFN_DONTADDTORECENT $FLAG
IntOp $FLAG "OFN_NONETWORKBUTTON" || 1
!define _OFN_NONETWORKBUTTON $FLAG
!define SET_FLAGS '${_OFN_EXPLORER}|${_OFN_OVERWRITEPROMPT}|${_OFN_DONTADDTORECENT}|${_OFN_NONETWO
RKBUTTON}'
MessageBox MB_OK ${SET_FLAGS} ; debugging purposes

; Setup the OPENFILENAME structure and store it into $2 (returns path and filename into string $1)
; Build Struc #2
!define OPENFILENAME '*(&l, i $HWNDPARENT, i, t ${FILE_FILTER}, i, i, i, t .r1, &t260, i, i, i, t "Open File", i ${SET_FLAGS}, &t3, &t14, t "exe", i, i, i, v, i 0, i 0) i' ;x23
; Open Save file dialog and return a bool into $3
System::Call 'comdlg32::GetSaveFileName(i* ${OPENFILENAME}) b .r3'
MessageBox MB_OK "$1$\n$3" ; debugging purposes

; Unload the plugin from memory
SetPluginUnload manual
System::Free 0
I basically was calling OPENFILENAME I simply called it LPARAM instead.

SAM

I changed what you told me to and I also switched the OPENFILENAME define back to a System.dll call. Do you think it more logical for it to be a system call, define, or it doesn't matter?

SAM


SetPluginUnload alwaysoff
; Setup a structure for allowed file types
; Build Struc #1
!define FILE_FILTER "HTML Files (*.htm*)|*.htm*|Images (*.gif;*.jp*;*.png)|*.gif;*.jp*;*.png|All Files (*.*)|*.*|"

; define and set the flags I want to use
Var /GLOBAL FLAG
!define OFN_EXPLORER 0x00080000
!define OFN_OVERWRITEPROMPT 0x00000002
!define OFN_DONTADDTORECENT 0x02000000
!define OFN_NONETWORKBUTTON 0x00020000
!define SET_FLAGS '${OFN_EXPLORER}|${OFN_OVERWRITEPROMPT}|${OFN_DONTADDTORECENT}|${OFN_NONETWORKBUTTON}'
MessageBox MB_OK ${SET_FLAGS} ; debugging purposes

; Setup the OPENFILENAME structure and store it into $2 (returns path and filename into string $1)
; Build Struc #2
System::Call '*(&l, i $HWNDPARENT, i, t ${FILE_FILTER}, i, i, i, t .r1, &t260, i, i, i, t "Open File", i ${SET_FLAGS}, &t3, &t14, t "exe", i, i, i, v, i 0, i 0) i r2' ;x23
; Open Save file dialog and return a bool into $3
System::Call 'comdlg32::GetSaveFileName(i* r2) b .r3'
MessageBox MB_OK "$1$\n$3" ; debugging purposes

; Unload the plugin from memory
SetPluginUnload manual
System::Free 0

You might be able to use this function: http://nsis.sourceforge.net/FileRequ...on_or_function


eccles THANK YOU SOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO MUCH!!!!!


Thanks again eccles,
I have modified that, another function, and did a little magic of my own. I was able to achieve unlimited File Filters instead of just two.

TODO: Somehow read the output after multiple files are selected. It doesn't work but I'm not sure how to retrieve the returned stack for that.

P.S. I know how to break apart a return stack *$0(t .r1, i .r2, .......) and so on.

See attachment for the source of how I'm using it now.

Now files can be filtered like:
"file1|ext,file2|ext,..." with no spaces.