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

Browse For File
Author: Message:
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
« Next Oldest Return to Top Next Newest »

Messages In This Thread
Browse For File - by Volv on 06-24-2006 at 02:04 AM
RE: Browse For File - by matty on 06-24-2006 at 02:19 AM
RE: Browse For File - by Volv on 06-24-2006 at 05:16 AM
RE: Browse For File - by Choli on 06-24-2006 at 09:32 AM
RE: Browse For File - by CookieRevised on 04-11-2011 at 07:46 AM
RE: RE: Browse For File - by Amec on 04-14-2011 at 06:54 PM
RE: Browse For File - by Choli on 04-11-2011 at 06:41 PM
RE: Browse For File - by CookieRevised on 04-11-2011 at 08:13 PM
RE: Browse For File - by matty on 04-14-2011 at 07:06 PM
RE: Browse For File - by CookieRevised on 04-14-2011 at 07:40 PM
RE: RE: Browse For File - by Amec on 04-14-2011 at 07:53 PM
RE: Browse For File - by CookieRevised on 04-14-2011 at 07:58 PM


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