What happened to the Messenger Plus! forums on msghelp.net?
Shoutbox » MsgHelp Archive » Messenger Plus! for Live Messenger » Scripting » Browse For File

Pages: (2): « First [ 1 ] 2 » Last »
Browse For File
Author: Message:
Volv
Skinning Contest Winner
*****

Avatar

Posts: 1233
Reputation: 31
35 / Male / Flag
Joined: Oct 2004
O.P. Browse For File
Heya,
How would I go about making a 'Browse for file' dialog which allows the user to navigate to a file of their choice on their PC and returns the path to the file.

Thanks in advance
- Volv
06-24-2006 02:04 AM
Profile PM Find Quote Report
matty
Scripting Guru
*****


Posts: 8336
Reputation: 109
39 / Male / Flag
Joined: Dec 2002
Status: Away
RE: Browse For File
This is taken from Choli's Translator, give all credits too him.

js code:
    var OFN_ENABLESIZING = 0x800000;
    var OFN_EXPLORER = 0x80000;
    var OFN_FILEMUSTEXIST = 0x1000;
    var OFN_HIDEREADONLY = 0x4;
    var OFN_LONGNAMES = 0x200000;
    var OFN_PATHMUSTEXIST = 0x800;
   
function OnEvent_Initialize(MessengerStart)
{
    var OpenFileName = Interop.Allocate(88);
    var filter;
    var s_filter;
    var file;
    var s_file;
    var title;
    var s_title;
    var ret;
    var tdata;

     with (OpenFileName) {
        WriteDWORD(0, Size); // lStructSize
        WriteDWORD(4, 0); // hwndOwner
        WriteDWORD(8, 0); // hInstance
        filter = "Translation files (Lng_*.ini)|Lng_*.ini|All files (*.*)|*.*||";
        s_filter = Interop.Allocate(2 * (filter.length + 1));
        WriteMultiStringW(s_filter, filter);
        WriteDWORD(12, s_filter.DataPtr); // lpstrFilter
        WriteDWORD(16, 0); // lpstrCustomFilter
        WriteDWORD(20, 0); // nMaxCustomFilter
        WriteDWORD(24, 1); // nFilterIndex
        file = "Lng_*.ini" + Space(256);
        s_file = Interop.Allocate(2 * (file.length + 1));
        s_file.WriteString(0, file);
        WriteDWORD(28, s_file.DataPtr); // lpstrFile
        WriteDWORD(32, file.length); // nMaxFile
        WriteDWORD(36, 0); // lpstrFileTitle
        WriteDWORD(40, 0); // nMaxFileTitle
        WriteDWORD(44, 0); // lpstrInitialDir
        title = "Select original file";
        s_title = Interop.Allocate(2 * (title.length + 1));
        s_title.WriteString(0, title);
        WriteDWORD(48, s_title.DataPtr); // lpstrTitle
        WriteDWORD(52, OFN_ENABLESIZING | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST); // flags
        WriteWORD(56, 0); // nFileOffset
        WriteWORD(58, 0); // nFileExtension
        WriteDWORD(60, 0); // lpstrDefExt
        WriteDWORD(64, 0); // lCustData
        WriteDWORD(68, 0); // lpfnHook
        WriteDWORD(72, 0); // lpTemplateName
        WriteDWORD(76, 0); // pvReserved
        WriteDWORD(80, 0); // dwReserved
        WriteDWORD(84, 0); // FlagsEx
    } with OpenFileName
    ret = Interop.Call("comdlg32.dll", "GetOpenFileNameW", OpenFileName);
    if (ret == 0) {
        return;
    }
}
function WriteMultiStringW (datablock, string) {
    var pos = 0;
    datablock.WriteString(0, string);
    pos = string.indexOf("|", pos);
    while (pos != -1) {
        datablock.WriteWORD(2 * pos, 0);
        pos = string.indexOf("|", pos + 1);
    }
}
function Space (number) {
    var i;
    var s = "";
    for (i = 0; i < number; i++) {
        s += " ";
    }
    return s;
}

This post was edited on 04-11-2011 at 12:28 PM by matty.
06-24-2006 02:19 AM
Profile E-Mail PM Find Quote Report
Volv
Skinning Contest Winner
*****

Avatar

Posts: 1233
Reputation: 31
35 / Male / Flag
Joined: Oct 2004
O.P. RE: Browse For File
Thanks a lot Matty & Choli, works great :D
06-24-2006 05:16 AM
Profile PM Find Quote Report
Choli
Elite Member
*****

Avatar
Choli

Posts: 4714
Reputation: 42
43 / Male / Flag
Joined: Jan 2003
RE: Browse For File
yeah, that's it :P

you can change the filter and put, for example:

"Text files|*.txt|All files|*.*||"

For more help, read this: GetOpenFileName and OPENFILENAME.
Messenger Plus! en español:
<< http://www.msgpluslive.es/ >>
<< http://foro.msgpluslive.es/ >>
:plus4:
06-24-2006 09:32 AM
Profile PM Find Quote Report
CookieRevised
Elite Member
*****

Avatar

Posts: 15517
Reputation: 173
– / Male / Flag
Joined: Jul 2003
Status: Away
RE: Browse For File
Since this recently came up again, I'll post my own variation/fix of the above code here.
Note the word 'variation', as this kind of code is very very generic and can be found all over the net already in various forms. It is simply a matter of filling a structure with the needed stuff and calling the proper Windows API, thus not realy something special.

Some small notes first:
  • Choli's variation has some things in it which are specific for his Translator tool and should not be copied as-is. Nevertheless I have seen his code cropping up in various places and scripts without those needed changes. Aka, those copies will not always function properly.

  • Matti has made a better variation in the post: Matti's reply to CommonDialog help
    However, that too still contains bugs and one or two things which could be done better.

So here is my variation, based upon Matti's variation of Choli's snippet. It is of course different, but not that different to be completely 'new'. As such, I've also tried to keep most of the structure and variable names used in the previous variations. However, due to the change of order of the parameters and the change in format of the returned value of the function, you can't simply replace your old BrowseForFile function with this one though. You will need to change the order of the parameters in your code and change how you handle the return value in your code.


The code itself:
js code:
/* FUNCION: BrowseForFile (CookieRevised variation)

    Original code by Choli from the Messenger Plus! Translator script
    Modified by Matti to be more versatile (eg: including "Save as" dialog function)
    Modified by CookieRevised to optimize it a bit and fix some bugs

    Description:
        Opens a "Browse for file" dialog with the given settings

    Parameters (all parameters are now optional):
        save        (boolean) / true is "Save as" dialog, false is "Open" dialog
        title       (string)  / Dialog title
        dir         (string)  / Start directory
        file        (string)  / File to display as default
        filter      (string)  / Filter message. Example: "JavaScript Files (*.JS)|*.JS|Text Files (*.TXT;*.LOG)|*.TXT;*.LOG|"
        ext         (string)  / Default extension to append to the filename when no extension is specified
        multiselect (boolean) / true to allow multiple file selection in an "Open" dialog
        hwnd_owner  (number)  / Handle of the owner window, used to copy window icon etc.



    Return value:
        >>>A (string) array of full filepath names.<<<
        Note that this will also be an array if just 1 file was selected (in that case an array with just 1 element).
        If no files were selected or the dialog was canceled, the array will be empty.
        I think this is more consistant and versatile across multiple implementations of the function.
*/
>>>function BrowseForFile(save, title, dir, file, filter, ext, multiselect, hwnd_owner) {<<<
    // see http://msdn.microsoft.com/en-us/library/ms646839(v=vs.85).aspx
    var OFN_OVERWRITEPROMPT =    0x2;
    var OFN_HIDEREADONLY =        0x4;
    >>>var OFN_NOCHANGEDIR =        0x8;<<<
    var OFN_ALLOWMULTISELECT =    0x200;
    var OFN_PATHMUSTEXIST =        0x800;
    var OFN_FILEMUSTEXIST =        0x1000;
    >>>var OFN_NONETWORKBUTTON =    0x20000;<<<
    var OFN_EXPLORER =            0x80000;
    var OFN_LONGNAMES =            0x200000;
    var OFN_ENABLESIZING =        0x800000;
    >>>var OFN_DONTADDTORECENT =    0x2000000;<<<

    var OpenFileName = Interop.Allocate(88);
    with (OpenFileName) {

        save = save == true;
        >>>multiselect = (multiselect == true) && !save;<<<
        >>>hwnd_owner = hwnd_owner ? 1 * hwnd_owner : 0;<<<
       
        >>>if (typeof filter !== "string") filter = "All Files (*.*)|*.*|";<<<
        >>>var s_filter = Interop.Allocate(2 * (filter.length + 1) + 4);<<<
        >>>s_filter.WriteBSTR(0, filter.replace(/\|/g, '\0'));<<<

        if (typeof file !== "string") file = "";
        >>>var s_file = Interop.Allocate(2 * (Math.max(file.length, 16383) + 1));<<<
        s_file.WriteString(0, file);

        if (typeof dir === "string") {
            var s_dir = Interop.Allocate(2 * (dir.length + 1));
            s_dir.WriteString(0, dir);
            WriteDWORD(44, s_dir.DataPtr);    // lpstrInitialDir
        }
        if (typeof title === "string") {
            var s_title = Interop.Allocate(2 * (title.length + 1));
            s_title.WriteString(0, title);
            WriteDWORD(48, s_title.DataPtr);    // lpstrTitle
        }
        if (typeof ext === "string") {
            var s_ext = Interop.Allocate(2 * (ext.length + 1));
            s_ext.WriteString(0, ext);
            WriteDWORD(60, s_ext.DataPtr);    // lpstrDefExt
        }
        WriteDWORD(0, Size);                // lStructSize
        WriteDWORD(4, hwnd_owner);            // hwndOwner
        >>>WriteDWORD(12, s_filter.DataPtr + 4);    // lpstrFilter<<<
        WriteDWORD(24, 1);                    // nFilterIndex
        WriteDWORD(28, s_file.DataPtr);        // lpstrFile
        >>>WriteDWORD(32, s_file.Size / 2);        // nMaxFile<<<
        >>>WriteDWORD(52, OFN_DONTADDTORECENT | OFN_ENABLESIZING | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST | (save ? OFN_OVERWRITEPROMPT : OFN_FILEMUSTEXIST) | (multiselect ? OFN_ALLOWMULTISELECT : 0)); // Flags<<<
    }

    var result = Interop.Call("COMDLG32.DLL", (save ? "GetSaveFileNameW" : "GetOpenFileNameW"), OpenFileName);
    if (result) {
        var p_path = s_file.ReadString(0);
        var p_files = new Array();
        if (multiselect) {
            var p_file = "";
            var pos = 2 * (p_path.length + 1);
            while (true) {
                p_file = s_file.ReadString(pos);
                if (p_file.length > 0) {
                    >>>p_files.push(p_path + (p_path.slice(-1) !== "\\" ? "\\" : "") + p_file);<<<
                    pos += 2 * (p_file.length + 1);
                } else break;
            }
        }
        >>>return p_files.length ? p_files : [p_path];<<<
    } else {
        >>>return [];<<<
    }
}
Major changes highlighted


Examples:
js code:
// Save File Dialog
// Opens up in C:\, file filters are 'Text Files' (*.txt and *.log) and 'All Files' (*.*).
var aFile = BrowseForFile(true, "Save file as", "C:\\", "test.txt", "Text Files (*.TXT;*.LOG)|*.TXT;*.LOG|All Files (*.*)|*.*|");
if (aFile.length) {
    // User specified a valid save location. Do something useful here.
    Debug.Trace(aFile[0])
} else {
    // User didn't specify a file to save to or pressed Cancel.
}
js code:
// Open File Dialog
// Opens up in C:\Windows, multiple selections are allowed, file filters are 'PNG Files' (*.png) and 'All Files' (*.*).
var aFile = BrowseForFile(false, "Select PNG", "C:\\Windows", null, "PNG Files (*.PNG)|*.PNG|All Files (*.*)|*.*|", null, true);
if (aFile.length) {
    // User selected some files. Do something useful here.
    for (var i in aFile) {
        Debug.Trace(aFile[i])
    }
} else {
    // User didn't selected any files to open or pressed Cancel.
}


Stuff changed compared to Matti's and Choli's (also see highlights in the code):
  • Made order of parameters a bit more logical, also in regards to optional paramater usage.
  • Changed the format of the returned value of the function. Now it always return an array with the full filepath names of the selected file(s). I think this is much more versatile and consistant in regards to multiple file selections, looping, etc. It is also very easy now to check if a file is returned or not, no matter the amount of files (just check the length of the array being 0 or not).

    thus beware, you can't simply replace your old BrowseForFile function with this one. See examples above.

  • Fixed the behaviour of the parameters so that they are now all optional. They weren't before in the sense that the code would fail if the file or dir parameter were not specified (was a big bug in Matti's variation). This was solved by removing the Datablock.Size = 0 lines at the end of the function. They are not needed as datablocks will be removed automatically by the garbage collector of Plus! when the function ends anyways.
  • Made hwnd_owner parameter also behave properly when not specified (was a potential bug). All parameters are now truely optional; you don't need to specify them.
  • Added the optional multiselect parameter (boolean).
  • Added some extra constants.
  • Replaced Choli's WriteMultiStringW function with Datablock::WriteBSTR() function and thus highly optimized the parsing of the filter parameter.
  • Removed Choli's Space() function.
  • Removed all unneeded Datablock::WriteDWORD(x, 0); lines. They are not needed because a Datablock will always be zero-filled anyways when you allocate it.
  • Added proper initial filepath name length check before writing to the filepath buffer. (was a potential bug)
  • Increased the filepath buffer drastically. 256 like in Choli's code is way too small. Even 1024 characters is small-ish, especially considering multiple file selections. There is no reason not to have a massive buffer for this.
  • Fixed nMaxFile calculation (size should include the null-character).
  • Added OFN_DONTADDTORECENT flag. This prevents files from being added to Windows' Recent Documents list, which is otherwise highly annoying in many cases. If you explicitly have a reason to add the opened/saved files in that list, simply remove that flag again in the code (on the line starting with WriteDWORD(52, ...).
  • Maybe some other tid bits not worth to mention.


This post was edited on 04-11-2011 at 08:43 PM by CookieRevised.
.-= A 'frrrrrrrituurrr' for Wacky =-.
04-11-2011 07:46 AM
Profile PM Find Quote Report
Choli
Elite Member
*****

Avatar
Choli

Posts: 4714
Reputation: 42
43 / Male / Flag
Joined: Jan 2003
RE: Browse For File
:mipdodgy: Cookie... this thread is 5 years old...
quote:
Originally posted by CookieRevised
256 like in Choli's code is way too small.
Sure, but if I remember well, I think that old versions of Windows (maybe 98, or 2000) don't allow more than that, do they?. Or something like that :P
Messenger Plus! en español:
<< http://www.msgpluslive.es/ >>
<< http://foro.msgpluslive.es/ >>
:plus4:
04-11-2011 06:41 PM
Profile PM Find Quote Report
CookieRevised
Elite Member
*****

Avatar

Posts: 15517
Reputation: 173
– / Male / Flag
Joined: Jul 2003
Status: Away
RE: Browse For File
quote:
Originally posted by Choli
:mipdodgy: Cookie... this thread is 5 years old...
Yup, the '90s are coming back :D
Nah, but joking aside, as I said, the request to have a 'BrowseForFile' dialog pops up now and then and will continue to pop up in the future no doubt. And apparently people keep on referring to this thread and/or use code from this thread (which is main reason why I posted this here and not in that other thread). I found many links pointing to Matty's post here, even in very recent posts. So, nothing wrong with (finaly) updating it a bit in that case imho.

quote:
Originally posted by Choli
quote:
Originally posted by CookieRevised
256 like in Choli's code is way too small.
Sure, but if I remember well, I think that old versions of Windows (maybe 98, or 2000) don't allow more than that, do they?. Or something like that :P
Not exactly; +-256 (or rather 260, which is MAX_PATH) is the absolute required minimum (eg: when you are sure you don't return multiple selections and with short file names). It's the minimum required size to hold exactly one complete path and file name (in short name notation.... because in long name notation the name itself can even be MAX_PATH characters). So, even back in the old days, the buffer could be (and better should be) much much bigger to hold both the returned path and filename.

For your Translator tool this was never a real problem. But since people copy that code without changing anything in it and using it as-is, problems do arise though.

This post was edited on 04-11-2011 at 08:51 PM by CookieRevised.
.-= A 'frrrrrrrituurrr' for Wacky =-.
04-11-2011 08:13 PM
Profile PM Find Quote Report
Amec
Junior Member
**


Posts: 19
32 / Male / Flag
Joined: Sep 2008
RE: RE: Browse For File
quote:
Originally posted by CookieRevised

js code:
with (OpenFileName) {

Every time you use a with statement, a teddy bear cries.

http://www.yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/
04-14-2011 06:54 PM
Profile E-Mail PM Find Quote Report
matty
Scripting Guru
*****


Posts: 8336
Reputation: 109
39 / Male / Flag
Joined: Dec 2002
Status: Away
RE: Browse For File
quote:
Originally posted by Amec
quote:
Originally posted by CookieRevised

js code:
with (OpenFileName) {

Every time you use a with statement, a teddy bear cries.

http://www.yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/
[Image: attachment.php?pid=1013025&update2]

.png File Attachment: like.png (1.23 KB)
This file has been downloaded 737 time(s).

This post was edited on 04-14-2011 at 07:14 PM by matty.
04-14-2011 07:06 PM
Profile E-Mail PM Find Quote Report
CookieRevised
Elite Member
*****

Avatar

Posts: 15517
Reputation: 173
– / Male / Flag
Joined: Jul 2003
Status: Away
RE: Browse For File
quote:
Originally posted by Amec
quote:
Originally posted by CookieRevised

js code:
with (OpenFileName) {

Every time you use a with statement, a teddy bear cries.

http://www.yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/
Yeah, well, you're 5 years to late....

Although I agree for 50%, it is mostly also a matter of schematics. There is essentially nothing wrong with using with() if you know what the code will do. In the code snippet it is also used for the exact purpose that with() statement was invented and there are enough readDWORD() statements to justify the use of with() imo.

May I also quote that very same page you linked to:
quote:
If you can’t read a program and be confident that you know what it is going to do, you can’t have confidence that it is going to work correctly. For this reason, the with statement should be avoided.
I can read the program and I am 100% confident in knowing what it is going to do and that it is going to work correctly.

Lastly, it is not my code:
quote:
Originally posted by CookieRevised
So here is my variation, based upon Matti's variation of Choli's snippet. It is of course different, but not that different to be completely 'new'. As such, I've also tried to keep most of the structure and variable names used in the previous variations.
Note that there are also other parts in the code which could be made shorter/smarter/cleaner/etc. But that was not the purpose of showing or updating that code.

This post was edited on 04-14-2011 at 07:53 PM by CookieRevised.
.-= A 'frrrrrrrituurrr' for Wacky =-.
04-14-2011 07:40 PM
Profile PM Find Quote Report
Pages: (2): « First [ 1 ] 2 » Last »
« Next Oldest Return to Top Next Newest »


Threaded Mode | Linear Mode
View a Printable Version
Send this Thread to a Friend
Subscribe | Add to Favorites
Rate This Thread:

Forum Jump:

Forum Rules:
You cannot post new threads
You cannot post replies
You cannot post attachments
You can edit your posts
HTML is Off
myCode is On
Smilies are On
[img] Code is On