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 Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

VFP6... GETDIR() and hidden folders

Status
Not open for further replies.

GriffMG

Programmer
Mar 4, 2002
6,308
FR
Just been dealing with a little support issue on a, very, remote system - nearer to MikeL than me - an old app
written in VFP6 (Started in 2002!).

User retiring, new user, new laptop, WFH, Windows 11... trying to locate the Sage Accounts data so that customers can
be read directly from there.

The app has a browse button to locate the folder (C:\ProgramData\sage\accounts\2022\company.000\accdata) but there is a
problem. C:\ProgramData is a hidden folder and VFP6 can't 'see' it via GETDIR().

In VFP7 and above you can specify three parameters and then VFP uses the Windows api to select a folder, but not back in
VFP6.

Anyone got a good workaround for this - obviously I *could* upgrade the app to VFP9, but I would rather not... well not
right now.

I have solved the instance in question by starting the search further down the tree so to speak - past ProgramData


Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
An interesting problem, Griff. A couple of points come to mind:

First, in VFP 7 and above, GETDIR() can see hidden folders even using the native dialogue (that is, without setting the flag to 64). That's of no help here, but I thought I would mention it for completeness.

But even when GETDIR() can't see the hidden folders, ADIR() can. On that basis, you could perhaps recursively drill down through all the folders to find ProgramData, and then start GETDIR() "further down the tree".

Finally, is ProgramData the same as what used to be called ApplicationData in earlier versions of Windows? In other words, is it one of the "special" folders set up by Windows, as opposed to one that you created yourself? If so, you can locateit via the Windows SHGetSpecialFolderLocation() API function. I've got some code somewhere for doing tht. I can dig it out if you would like to try it.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I did not test VFP9 (not having 7 or 8) against the hidden directories, thanks for that Mike.

I did *think* about writing my own simple folder selector using a grid or somesuch...

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
I think it is the latest variant of ApplicationData, not 100% sure - but I think so.

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Interesting, there is a set value for it ProgramData=c:\programdata if you type SET in a cmd window

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
there is a set value for it ProgramData=c:\programdata if you type SET in a cmd window

Hmm. I don't get that. Surely SET simply opens the Data Session window. Nothing to do with directory names there.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Here is my routine for finding the ProgramData directory:

Code:
FUNCTION GetSpecial
* Returns the path to one of Windows' "special folders", 
* such as My Documents, Application Data, Program Files, 
* etc.

* Parameters:
* - The folder's CSIDL;
* - A flag to say the folder should be created if it
*   doesn't already exist.

* Returns the path to the specified folder. If the CLSID
* is invalid, or the folder doesn't exist (and the second
* parameter is .F.), returns an empty string.

LPARAMETERS tnFolder, tlCreate

LOCAL lcPath, lnPidl, lnPidlOK, lnFolderOK

#DEFINE MAX_PATH 260
#DEFINE CREATE_FLAG 32768 

* Delcare the API functions
DECLARE LONG SHGetSpecialFolderLocation IN shell32 ;
	LONG Hwnd, LONG lnFolder, LONG @pPidl
DECLARE LONG SHGetPathFromIDList IN shell32 ;
	LONG pPidl, STRING @pszPath
DECLARE CoTaskMemFree IN ole32 LONG pVoid

lcPath = SPACE(MAX_PATH)
lnPidl = 0

* If .T. is passed as second param, we want to create the 
* folder if it doesn't already exist.
IF tlCreate
	tnFolder = tnFolder + CREATE_FLAG
ENDIF 
	
* Get the PIDL
lnPidlOK = SHGetSpecialFolderLocation(0, tnFolder, @lnPidl)

IF lnPidlOK = 0
  * Pidl found OK; get the folder
  lnFolderOK = SHGetPathFromIDList(lnPidl, @lcPath)
  IF lnFolderOK = 1
    * Folder found OK; trim the string at the null terminator
    lcPath = LEFT(lcPath, AT(CHR(0), lcPath) - 1)
  ENDIF 
ENDIF 

* Free the ID List
CoTaskMemFree(lnPidl)

RETURN ALLTRIM(lcPath)

You will need to know the value to pass as the first parameter (the CSIDL). Assuming that ProgramData is the same as ApplicationData (which seems to be the case), then the value to pass is 26 (decimal).

Once you have found the directory, you presumably won't need to prompt the user for it.

I wrote the function a long time ago - probably around 2005 or 2006 - so it might not be up to date. On the other hand, I can't see any reason for things to have changed.

I must acknowledge Doug Hennig's help with this function, but if doesn't work for any reason, the fault will be mine.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I think if you configure Windows Explorer to see hidden folders, it can be seen.

Also, I'd try
Code:
CD C:\ProgramData\sage
lcDir = GetDir()
Because while ProgramData is hidden, folders within it are not, if you start on thet level already it can work..

If even that fails, the user can try to enter "C:\ProgramData" and see whether the diectory dialog changes to it.


Chriss
 
I think Griff meant
Code:
? GetEnv("ProgramData")

If you want to support many OS versions it's better to rely on API to get you special system folders, in that case you might not have had the problems moving the software to a new laptop has, because the API functions work for many OS versions. The last version where the API for getting special folders changed drastically was Vista, so you can cover Vista and later OSes, likely still Windows 12.

But I am also sometimes quite pragmatic and GetEnv is not only goverend by administrators, the OS defaults for many environment variables also cover a wide range of settings that can differ by OS version. In some way you can never 100% rely on them, because, well, admins can change these from their real paths. On the other side they could also add an environment variable %SAGEDATA% and set it to the sage data directory, so you get it without asking the user by GETENV("SAGEDATA").

There is no error to fear, too. Not set environment variables return the empty string. You only have to ask the user for specifying in that case. But you could rely on a well administrated company to take care of that with computer and OS changes which, even when they are incompatible by OS itself. and, well, no OS is responsible for which directory sage data ends in, of course.

Chriss
 
I think Griff meant
Code:
? GetEnv("ProgramData")

Yes, that works. It gives me the same result as calling my function with 26 as the first parameter. But I agree with your point, Chris, about environment variables getting altered.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
No I actually meant typing the word SET into a command window - a DOS prompt effectively - reveals all the current paths and other SETs

Very old school

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
I see, but everyone thinks of the VFP command window if you say cmd window and not command prompt, you meant cmd.exe with cmd, literally cmd, not short for command - which it is anyway, but alas...

It's still all the same: System environment variables...
env_cizxz2.png


env1_zhnt1e.png

env2_gshp1u.png

env3_ald0fc.png


You get everyone of them with GETENV(), too: System environment variables and current user specific environment variables.

Chriss
 
And - to get the cross reference - the API function SHGetSpecialFolderLocation and others are about system paths, and some of them like the temp directory are also set in environment variables. So there is a data overlap about this.

The adminstrator(s) of your customers might be fine to actually define an environment variable, as it can be propagated in a comapnys' domain to all workstations or all users acount profiles.

Chriss
 
>Assuming that ProgramData is the same as ApplicationData (which seems to be the case), then the value to pass is 26 (decimal).

They are not the same! There is a difference between the AppData folder (26) and the CommonApplicationDataFolder (35), and typically they ought to return different folders.

And we can get to them with less code. E.g

Code:
[COLOR=blue]ssfCommonApplicationData = 35 [COLOR=green]' same as PIDL as might be used in API version[/color]
Print CreateObject("shell.application").Namespace(ssfCommonApplicationData).Self.Path[/color]
 
Yes, I know that now. In my post (above), I reported that 26 did not return ProgramData but that 35 did. I wrote a little loop that called the function with all values from 1 to some high number, and that did indeed show that ApplicationData and ProgramData are different. (It also showed lots of other special folders that I never knew existed.)

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
After a bit more searching I found this


Which had a very simple function that works in VFP6

Code:
FUNCTION SelDirDlg
LPARAMETERS cDialogTitle, cStartingFolder, nBrowseFlags
*  Select a directory using the default browser dialog
*  Give a default title if none specified
IF TYPE('cDialogTitle') # 'C'
	cDialogTitle = 'Please select a folder:'
ENDIF
*  Default the start folder to an empty string;  if
*  you specify a starting folder, the browse is
*  anchored there, although you can override it with
*  the dialog's EditBox
IF TYPE('cStartingFolder') # 'C'
	cStartingFolder = ''
ENDIF
IF TYPE('nBrowseFlags') # 'N'
	* uses BROWSEINFO structure ulFlags values
	* by default, set BIF_RETURNONLYFSDIRS (1) and BIF_EDITBOX (16) and BIF_VALIDATE
	* to limit to returning directories, provide an edit box to let user enter a path,
	* and validate manually-entered paths
	*
	* ulFlags values:
	*	BIF_RETURNONLYFSDIRS	1
	*	BIF_DONTGOBELOWDOMAIN	2
	*	BIF_STATUSTEXT			4
	*	BIF_RETURNFSANCESTORS	8
	*	BIF_EDITBOX				0x10
	*	BIF_VALIDATE			0x20
	*	BIF_BROWSEFORCOMPUTER	0x1000
	*	BIF_BROWSEFORPRINTER	0x2000
	*	BIF_BROWSEFOREVERYTHING	0x4000
	nBrowseFlags = 32 + 16 + 1
ENDIF
LOCAL oBrowseObject, cPathToReturn, oShellObj
oShellObj = CREATEOBJ('Shell.Application')
cPathToReturn = ''
*	Get a Folder object
oBrowseObject = oShellObj.BrowseForFolder(0, ;
		cDialogTitle, ;
		nBrowseFlags, ;
		cStartingFolder)
*	Before I used the Items collection of the Folder object
*	to get a path;  it doesn't work if the directory is empty.
*	Instead, spin through the Items collection of the Parent
*	Folder and locate the item whose name matches the
*	Title property of the Folder object;  return that path
IF TYPE('oBrowseObject') = 'O' AND ! ISNULL(oBrowseObject)
	FOR EACH item IN oBrowseObject.ParentFolder.Items
		IF item.name == oBrowseObject.title
			cPathToReturn = Item.Path
			EXIT
		ENDIF
	ENDFOR
ENDIF
RETURN cPathToReturn

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Srongm said:
Code:
ssfCommonApplicationData = 35 ' same as PIDL as might be used in API version
Print CreateObject("shell.application").Namespace(ssfCommonApplicationData).Self.Path

As you say, Strongm, very much more compact. Allow me to translate it to VFP:

Code:
lnCSIDL = 35    && For ProgramData
oShe =  CreateObject("shell.application")
? oShe.NameSpace(lnCSIDL).Self.Path

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Griff, I just tried to run your function. With a start directory above "ProgramData" in the folder tree (for example, "c:\") or indeed leaving that parameter empty, it doesn't show ProgramData (not any other hidden directory).

If I pass the actual path of ProgramData, it correctly shows that folder as well as folders beneath it. But doesn't that defeat the object? If you know the path to ProgramData, why are you asking the user to select it?

By the way, everything we've said here about not seeing hidden folders only applies if the user has selected "Don't show hidden folders or drives" in Windows Explorer / Tools / Options / View. (I suppose that's obvious.)

I've also just noticed that your dialogue has a separate button on the Windows taskbar. Not that that matters, but I thought I would mention it.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top