October 09, 2024, 05:16:17 PM

News:

IWBasic runs in Windows 11!


Need help with browse for folder dialog

Started by StormAce, February 29, 2016, 07:04:34 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

StormAce

Hi,
I'm trying to translate this piece of c or c++ code to aurora to set the initial select folder in the dialog



#include <windows.h>
#include <shlobj.h>

int CALLBACK BrowseForFolderCallback(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pData)
{
char szPath[MAX_PATH];

switch(uMsg)
{
case BFFM_INITIALIZED:
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, pData);
break;

case BFFM_SELCHANGED:
if (SHGetPathFromIDList((LPITEMIDLIST) lp ,szPath))
{
SendMessage(hwnd, BFFM_SETSTATUSTEXT,0,(LPARAM)szPath);

}
break;
}

return 0;
}

BOOL BrowseFolders(HWND hwnd, LPSTR lpszFolder, LPSTR lpszTitle)
{
BROWSEINFO bi;
char szPath[MAX_PATH + 1];
LPITEMIDLIST pidl;
BOOL bResult = FALSE;

LPMALLOC pMalloc;

    if (SUCCEEDED(SHGetMalloc(&pMalloc)))
{
bi.hwndOwner = hwnd;
bi.pidlRoot = NULL;
bi.pszDisplayName = NULL;
bi.lpszTitle = lpszTitle;
bi.ulFlags = BIF_STATUSTEXT; //BIF_EDITBOX
bi.lpfn = BrowseForFolderCallback;
bi.lParam = (LPARAM)lpszFolder;

pidl = SHBrowseForFolder(&bi);
if (pidl)
{
if (SHGetPathFromIDList(pidl,szPath))
{
bResult = TRUE;
strcpy(lpszFolder, szPath);
}

   pMalloc->Free(pidl);
   pMalloc->Release();
           
}
}

return bResult;

}




Here is mine that crash when i use the callback...


//extern string BrowseFolders(CWindow *win, STRING title, opt Pointer lpszFolder, opt INT newfolder , opt STRING root);
#include "windows.inc"
#include "shlwapi.inc"
#include "shlobj.inc"

#DEFINE BIF_RETURNONLYFSDIRS 0x0001
#DEFINE BIF_DONTGOBELOWDOMAIN 0x0002
#DEFINE BIF_STATUSTEXT     0x0004
#DEFINE BIF_RETURNFSANCESTORS 0x0008
#DEFINE BIF_EDITBOX     0x0010
#DEFINE BIF_VALIDATE 0x0020
#DEFINE BIF_NEWDIALOGSTYLE     0x0040
#DEFINE BIF_USENEWUI (BIF_NEWDIALOGSTYLE | BIF_EDITBOX)
#DEFINE BIF_BROWSEINCLUDEURLS 0x0080
#DEFINE BIF_UAHINT              0x0100
#DEFINE BIF_NONEWFOLDERBUTTON 0x0200
#DEFINE BIF_NOTRANSLATETARGETS  0x0400
#DEFINE BIF_BROWSEFORCOMPUTER 0x1000
#DEFINE BIF_BROWSEFORPRINTER 0x2000
#DEFINE BIF_BROWSEINCLUDEFILES  0x4000
#DEFINE BIF_SHAREABLE           0x8000

#DEFINE BFFM_INITIALIZED 1
#DEFINE BFFM_SELCHANGED     2
#DEFINE BFFM_VALIDATEFAILEDA 3
#DEFINE BFFM_VALIDATEFAILEDW 4
#DEFINE BFFM_IUNKNOWN 5

//Open Browse for folder dialog, return folder path
GLOBAL SUB BrowseFolders(CWindow *win, STRING title, opt Pointer lpszFolder, opt INT newfolder , opt STRING root),STRING
{
    DSTRING path[260];
    BROWSEINFO bi;
WORD szPath[MAX_PATH + 1];
    pointer lpIDList;
POINTER pidl;
    STRING Buffer;
    //Make sure COM is active
    CoInitialize(NULL);
//initialize variables
path[0] = 0;
RtlZeroMemory(bi,LEN(bi));

if root <> ""
{
bi.pidlRoot = GetPIDLfromPATH(win, s2w(root));
}
else
{
bi.pidlRoot = null;
}


bi.hwndOwner = win->GetHandle();
bi.pszDisplayName = Buffer;
bi.lpszTitle = Title;

bi.lpfn = /**(BFFCALLBACK)*/&BrowseForFolderCallback;
bi.lParam = *(LPARAM)lpszFolder;

    IF newfolder = true
{
        bi.ulFlags = 0x00000001|BIF_NEWDIALOGSTYLE;
}
    ELSE
{
        if newfolder = false | newfolder = null
    {
          bi.ulFlags = 0x00000001;
        }
    }
//messagebox(0, "", "");

lpIDList = SHBrowseForFolder(&bi);

if (lpIDList)
{
pszPath = SHGetPathFromIDList(lpIDList,path);
if (pszPath)
{
strcpy(*(string)lpszFolder, path);
}
    CoTaskMemFree(lpIDList);
        CoUninitialize();
}

RETURN path;
}

SUB BrowseForFolderCallback(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pData), LPARAM
{
WORD szPath[MAX_PATH];

switch(uMsg)
{
case BFFM_INITIALIZED:
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, pData);
return;

case BFFM_SELCHANGED:
if (SHGetPathFromIDList( lp ,szPath))
{
SendMessage(hwnd, BFFM_SETSTATUSTEXT,0,szPath);
}
return;
}

return 0;
}

// Use by BrowseForFolder
Global SUB GetPIDLfromPATH(Cwindow *win, POINTER *path),ITEMIDLIST*
{
return SHSimpleIDListFromPath(*(wstring)Path);
}




Anyone can see the problem?



LarryMc

does it compile?
If not, what error messages do you get?
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

StormAce

Yes it compile but the app crash when calling it...

Egil

I have never used Aurora myself, but in my version there is an example called folder_browser.src that compiles just fine and does not crash. The source also contains suggestions for modifications.
What is wrong with it?

Support Amateur Radio  -  Have a ham  for dinner!

StormAce

March 02, 2016, 01:20:56 AM #4 Last Edit: March 02, 2016, 01:27:35 AM by StormAce
Hi,
I don’t have check in my examples folder before seeing your post, but what i want to do is use the UINT lpfn; in BrowseInfo
This parameter is used to select the folder specified... and it use CallBack...
That’s where it crashes... you can isolate the call with the messagebox commented to see
I have done a lot of pirouettes with the code, can't find a way.

to run, just put the extern call at the top in an src file in a project and add the src file to your project

Egil

As I mentioned before, I do not have any experience with Aurora.

To use the value of lpfn you have to  return it in some way for use outside the particular suboutine where  it is retreived.
But when inspecting your code with my IWB eyes, the only value returned from your code is path, defined as string.

The folder_browser.src also use the BrowseInfo structure, so suggest you inspect that code closely to see what element(s) that differs from your code,
and see to that a variable holding the value of lpfn is returned.

Sorry for not being able to give you a more more specific answer. But hopefully some of the other forum users, with Aurora experience, will see this and guide you into the right path...


Good Luck!

Support Amateur Radio  -  Have a ham  for dinner!

StormAce

QuoteTo use the value of lpfn you have to  return it in some way for use outside the particular suboutine where  it is retreived.
But when inspecting your code with my IWB eyes, the only value returned from your code is path, defined as string.

yep browse for folder dialog return a path  ;)
the callback is retreived by sub "BrowseForFolderCallback" which must return 0 as the example in c or c++ above

QuoteThe folder_browser.src also use the BrowseInfo structure, so suggest you inspect that code closely to see what element(s) that differs from your code,
and see to that a variable holding the value of lpfn is returned.

BrowseInfo structure is declare in the one of the #define .inc file at the top of the code...  :)

Thanks anyway sir

Brian

Well, this was written by the man who wrote the Aurora compiler, so give it a go:

SUB main()
{
   string folder = GetFolder(null,"Select a folder");
   print(folder);
   While GetKey() == "";
}

/* subroutine to open the system folder browser */
struct BROWSEINFO
{
     UINT hOwner;
     UINT pidlRoot;
     POINTER pszDisplayName;
     POINTER lpszTitle;
     UINT ulFlags;
     UINT lpfn;
     INT lParam;
     INT iImage;
}

DECLARE IMPORT,SHGetPathFromIDList(int pidl,string pszPath),INT;
DECLARE IMPORT,SHBrowseForFolder(BROWSEINFO lpbi),INT;
DECLARE IMPORT,CoTaskMemFree(int pidl);   
DECLARE IMPORT,ZeroMemory ALIAS RtlZeroMemory(pData as POINTER,length as INT);
declare import,SHParseDisplayName(WSTRING pszName,UINT *pbc,UINT *ppidl,UINT sfgaoIn,UINT *psfgaoOut),INT;
declare import,SHGetSpecialFolderLocation(UINT hwnd,int nFoloder,UINT *ppidl),int;
SUB GetFolder(UINT hwnd,string title),STRING
{
DSTRING path[260];
BROWSEINFO bi;
UINT lpIDList;
UINT pRoot,pTemp;
   //initialize variables
   path[0] = 0;
   ZeroMemory(bi,LEN(bi));
   //for Windows XP and above we can use SHParseDisplayName which can take a file path
   //such as "c:\\progs" or a CLSID. The CLSID being used is for the My Computer folder.
   SHParseDisplayName(s2w("::{20d04fe0-3aea-1069-a2d8-08002b30309d}"),NULL,pRoot,0,pTemp);
   //for all Windows version this gets the pidl of the My Computer folder.
   //So if you are targeting Windows 9x and above use this instead
   //SHGetSpecialFolderLocation(null,0x0011,pRoot);
   bi.pidlRoot = pRoot;
   bi.hOwner = hwnd;
   bi.pszDisplayName = path;
   bi.lpszTitle = title;
   bi.ulFlags = 0x00000041;
   lpIDList = SHBrowseForFolder(bi);
   if lpIDList <> 0
   {
      SHGetPathFromIDList(lpIDList,path);
      CoTaskMemFree(lpIDList);
   }   
   RETURN path;
}

/* other common CLSID's for use with SHParseDisplayName:
My Documents ::{450d8fba-ad25-11d0-98a8-0800361b1103}
Programs Folder ::{7be9d83c-a729-4d97-b5a7-1b7313c39e0a}
Start Menu Folder ::{48e7caab-b918-4e58-a94d-505519c795dc}
Temporary Internet Files ::{7bd29e00-76c1-11cf-9dd0-00a0c9034933}
My Network Places ::{208d2c60-3aea-1069-a2d7-08002b30309d}
*/

Brian

Egil

That is the same example I tried to direct his attention to, Brian.
It is found in the examples folder that came with Aurora, and the filename is folder_browser.src .
He stated himself he had not checked it before starting converting from C.

Bought Aurora just to support the site, and never used it. Had to install it last night to find that example.

The problem is that the subroutines he calls only return string values, and he want to use a structure member called lpfn which is defined as an UINT.
So  whatever programming language he uses, he somehow has to retreive and return the value of lpfn before he can use it.
Support Amateur Radio  -  Have a ham  for dinner!

Brian

March 02, 2016, 08:32:37 AM #9 Last Edit: March 02, 2016, 09:31:52 AM by Brian Pugh
Egil,

Well, I'm not doing anything much at the moment, so I might load my Aurora up and have a go!

Brian

Update: Well, loaded up Aurora. Didn't bother with the extra headers, and compiled

Got it - compile as a Console exe and not a Windows exe

It then displays the folder selected. "print (folder);" is the line that does that

Egil

Thats good Brian!
You have way more experience with Windows than me, so let's hope you are able to help him.

Support Amateur Radio  -  Have a ham  for dinner!

Brian

Compile folder_browser.src as a Console exe, not a Windows exe

Brian

Egil

Here it does not matter if I choose Console.exe or windows.exe. Both compile fine.
Guess the browse for folder dialog is the same for both methods.
Support Amateur Radio  -  Have a ham  for dinner!

aurelCB

well
interesting ...when i try to compile Ebasic(old freeware v1.62) program i receive
message from win7 that i don't have permission to do that and linker cannot link exe to
program files , but when i move eba example to folder  C\user then
compile file and exe work. :)
also i dont get it why he need to return
longPointer...
   lpIDList = SHBrowseForFolder(bi)

StormAce

Hi guys,
Like i said the folder_browser.src example work well as my code if you remove the bi.lpfn call, just comment it and it works...
My problem is the Callback that don't work... i need it so the last selected folder should be selected in the dialog when you open it ;)

fasecero

Hi. You are using lpszFolder as optional inside your BrowseFolders function, I think you should check its value before using it as a pointer



if lpszFolder { bi.lParam = *(LPARAM)lpszFolder; }

.....

if lpszFolder { strcpy(*(string)lpszFolder, path); }



And the SHGetPathFromIDList method from your BrowseForFolderCallback function should require a structure, not a pointer, in the first parameter


if (SHGetPathFromIDList( *(ITEMIDLIST) lp , szPath) )

StormAce

Thanks fasecero,
I have done lots of pirouettes with the code and i think i had tryed that... but i will give it another go with your suggestions later today
thank you :)

aurelCB

look this Ebasic program work properly and return last open folder:
/*
EBASIC example program
Shows how to use the system directory browser
Compile as a WINDOWS target
*/

DEF mywin:WINDOW
DEF result:INT
DEF Buffer:STRING
DEF sf:INT

OPENWINDOW mywin,0,0,400,300,@MINBOX,0,"Browse for Folder",&mysub

print mywin,GetFolder()

run = 1
WAITUNTIL run = 0
CLOSEWINDOW mywin
END

SUB mysub
SELECT @CLASS
CASE @IDCLOSEWINDOW
run = 0
CASE @IDCREATE
CENTERWINDOW mywin
ENDSELECT
RETURN
ENDSUB

/* subroutine to open the system folder browser */
TYPE BROWSEINFO
     DEF hOwner:UINT
     DEF pidlRoot:UINT
     DEF pszDisplayName:POINTER
     DEF lpszTitle:POINTER
     DEF ulFlags:UINT
     DEF lpfn:UINT
     DEF lParam:INT
     DEF iImage:INT
ENDTYPE

DECLARE IMPORT,SHGetPathFromIDList(pidl:INT,pszPath:STRING),INT
DECLARE IMPORT,SHBrowseForFolder(lpbi:BROWSEINFO),INT
DECLARE IMPORT,CoTaskMemFree(pidl:int)   
DECLARE IMPORT,ZeroMemory ALIAS RtlZeroMemory(pData as POINTER,length as INT)

SUB GetFolder(),STRING
DEF path[260] as ISTRING
DEF bi:BROWSEINFO
DEF lpIDList:INT
'initialize variables
path[0] = 0
ZeroMemory(bi,LEN(bi))
bi.hOwner = mywin.hwnd
bi.pszDisplayName = Buffer
bi.lpszTitle = "Select Directory:"
bi.ulFlags = 0x00000001
lpIDList = SHBrowseForFolder(bi)
if lpIDList <> 0
pszPath = SHGetPathFromIDList(lpIDList,path)
CoTaskMemFree(lpIDList)
ENDIF
RETURN path
ENDSUB

StormAce

Thanks Aurel
but its the same as folder_browser.src that was shown in a post above... yes it return the path but dont select the last folder selected ;)

aurelCB

Quoteyes it return the path but dont select the last folder selected
::)
well i dont get it what you mean exactly but if you mean
that browser dialog is not visible after path is presented in output window
then you need different type of program with lot more code
or maybe you can force dialog to be always on top

StormAce

March 02, 2016, 04:09:00 PM #20 Last Edit: March 02, 2016, 04:10:38 PM by StormAce
hehe .. lol
Aurel what i want to do is when you open the browsefolder dialog, i want the last folder that the user use to be selected in the dialog...
So he can know the folder he selected before...

in vb.net its just a question of pass a string...
in wn32 you have to use a callback function ;)

Egil

March 02, 2016, 04:17:56 PM #21 Last Edit: March 02, 2016, 04:20:23 PM by Egil
Quote from: StormAce on March 02, 2016, 04:09:00 PM

... i want the last folder that the user use to be selected in the dialog...
So he can know the folder he selected before...

Hi StormAce,

Below is the code needed for MiniBASIC to do exactly that.
It contains all the defines you need for such a function, and should not be too difficult to convert to Aurora.

The included zip archive also contains an exe file made by compiling the code shown.
(The code is part of a program for bulk renaming mp3 files)


'
' folder_browse.mba
'
##NOCONSOLE

const BIF_RETURNONLYFSDIRS = 1
const BIF_NEWDIALOGSTYLE = 0x00000040
const BIF_UAHINT = 0x00000100

@API SHBrowseForFolderA(lpbi As pointer),int
@API SHGetPathFromIDList(pidList As int,lpBuffer As String),int
@API CoTaskMemFree(hMem As int)

Type BrowseInfo
   int hWndOwner
   int pIDLRoot
   pointer pszDisplayName
   pointer lpszTitle
   int ulFlags
   int lpfnCallback
   int lParam
   int iImage
End Type

WINDOW win
win.Open(0,0,800,480,WS_SYSMENU|WS_VISIBLE|BUFFERED|CENTERED,0," Mp3Rename",NULL,&handler)

' Select directory to convert
BrowseInfo bi
int ret
string Path,name

bi.lpszTitle = "Choose Folder"
bi.ulFlags = BIF_RETURNONLYFSDIRS|BIF_NEWDIALOGSTYLE|BIF_UAHINT
bi.pszDisplayName = name
ret = SHBrowseForFolderA(bi)
SHGetPathFromIDList(ret,Path)
CoTaskMemFree(ret)
win.WriteText(10,20,"Selected Directory: "+Path)


DO:ProcessMessages():UNTIL win.GetHandle()=NULL:END



' Main loop
FUNC handler(WINDOW wnd),int

  SELECT wnd.Message
  CASE ID_CLOSE
wnd.Close()
  ENDSELECT

RETURN 0
ENDF


Support Amateur Radio  -  Have a ham  for dinner!

StormAce


aurelCB

Quotei want the last folder that the user use to be selected in the dialog...
So he can know the folder he selected before...

Well i get it now ,but you dont say that first time.
So you need something like listbox to store each selected folder name
but this require more coding....

StormAce

yes, well, its already coded ;)
I take it from registry in fact