I have encountered these last few days major problems with our outstation server and saw some things we should have done to give us additional guide or information on some important things.  One tool that I deem now important is to keep a map of the user folders and files that is stored in the server.

Some call this tool a Directory Printer, File and Folder Lister, File Tree Printer, etc.  There are software our there in the internet just for that.  But again, why would you buy something that can be easily made?  Can we do it using VFP?  Sure!  Is it really easy as I claim it to be?  Sure!

As I have said before, VFP is a very mature language and can do a lot of things.  Actually, even without VFP you can achieve it.  All you need to do is look inside your harddrive and you will find a lot of tools there that remains dormant.  My advice, try to find those things and utilize it to your advantage.

As I said, creating it is easy and has involved only a few lines of codes.  But since I want to share this,  I decided to expand the codes a bit to make it a runnable form.  This is developed using VFP9 and is tested on an XP SP2 OS but I think it will work with some lower versions of VFP as well as some other Windows OS.

It is fast!  My home CPU where I tested this afterward have 64,078 Files inside 4,602 Folders, which is on drive C where my Operating System resides.  Total Disk Space Consumption is 17.9GB as can be seen by the image below:

Elapsed time of mapping the entire drive took less than 32 seconds on first run and less than 40 seconds on second run.  I chose to show the second run as I forgot to reposition the editbox on the first run to show a better tree lines, LOL!

For Documents & Settings, it is just less than .5 seconds:

And here is the sample output when opened inside MSWord:

And here are the codes:

Local oForm As Form
oForm = Createobject('TreeForm')
oForm.Show(1)
Return
 
Define Class TreeForm As Form
	AutoCenter = .T.
	Height = 500
	Width = 390
	Caption = "Sandstorm's Files and Folder Lister"
	MinButton = .F.
	MaxButton = .F.
	BorderStyle = 1
 
	Add Object label1 As Label With;
		top=35, Left= 10, Caption = "Tree RootLines Style", AutoSize = .T., FontBold = .T.
 
	Add Object opttree As OptionGroup With ;
		top = 50, Left = 10, ButtonCount = 2, Value = 1, AutoSize = .T., BorderStyle = 0
 
	Add Object chkInclude As Checkbox With;
		top=105, Left = 10, Caption = "Include Files", AutoSize = .T.
 
	Add Object cmdSelFolder As CommandButton With;
		top=140, Left= 15, Caption = "Select A Folder", AutoSize = .T.
 
	Add Object lblFolder As Label With;
		top=170, Left= 15, Caption = "None Selected...", AutoSize = .T.,;
		FontUnderline = .T., ForeColor = Rgb(255,0,0)
 
	Add Object lblElapsed As Label With;
		top=170, Left= 290, Caption = "Elapsed: ", AutoSize = .T., ForeColor = Rgb(255,0,255)
 
	Add Object edit1 As EditBox With;
		top = 195, Left = 10, Width = 370, Height = 250, FontName = "Tahoma"
 
	Add Object cmdTree As CommandButton With ;
		top = 460, Left = 10, AutoSize = .T., Caption = "\<Create Tree Now"
 
	Add Object cmdWord As CommandButton With ;
		top = 460, Left = 260, AutoSize = .T., Caption = "\<Open Inside Word"
 
	Procedure Init
		With Thisform.opttree
			.option1.Caption = "Extended Characters (Shown Properly Inside Word)"
			.option1.AutoSize = .T.
			.option2.Caption = "ASCII Characters"
			.option2.AutoSize = .T.
		Endwith
		Thisform.AddProperty("_SourceFolder","")
		Thisform.AddProperty("_TreeFile",Addbs(Getenv("TMP"))+Sys(3))
 
	Function cmdSelFolder.Click
		Thisform._SourceFolder  = Justpath(Getdir())
		If !Empty(Thisform._SourceFolder)
			Thisform.lblFolder.Caption = Iif(Len(Thisform._SourceFolder)>35,Left(Thisform._SourceFolder,35)+"...",;
				Thisform._SourceFolder)
		Else
			Thisform.lblFolder.Caption = "None Selected..."
		Endif
	Endfunc
 
	Function cmdTree.Click
		If !Empty(Thisform._SourceFolder)
			* Create treeview for edit and compute elapsed time
			Thisform.lblElapsed.Caption = 'Creating List...'
			Local lnMin, lnSec
			lnMin = Minute(Datetime())
			lnSec = Seconds()
			oShell = Createobject("WScript.Shell")
			If Thisform.opttree.Value = 1
				oShell.Run('cmd /c '+Iif(Thisform.chkInclude.Value = 0,'tree "','tree /f "')+;
					Thisform._SourceFolder+'" > '+Thisform._TreeFile,2,.T.)
			Else
				oShell.Run('cmd /c '+Iif(Thisform.chkInclude.Value = 0,'tree /a "','tree /a /f "')+;
					Thisform._SourceFolder+'" > '+Thisform._TreeFile,2,.T.)
			Endif
			*change appearance in editbox to make it more readable
			lcFile = Strtran(Filetostr(Thisform._TreeFile),Chr(196),Chr(173))
			lcFile = Strtran(lcFile,Chr(195),"+")
			lcFile = Strtran(lcFile,Chr(179),"|")
			lcFile = Strtran(lcFile,Chr(192),"+")
			Thisform.edit1.Value = m.lcFile
			Thisform.lblElapsed.Caption = Iif(Minute(Datetime())-lnMin <= 0,;
				TRANSFORM(Seconds()-lnSec)+" seconds",;
				TRANSFORM(Minute(Datetime())-lnMin)+" Minutes")
			* Disable Timer to stop blinking of elapsed time
		Else
			Messagebox("Operation Aborted!",0+64,"Cancelled")
		Endif
	Endfunc
 
	Function cmdWord.Click
		If !Empty(Thisform._SourceFolder)
			* Create a new one to ensure it does not overwrite the old instance in Word
			* in case user decides to repititively open it inside MSWord without recreating a new TreeView
			* INT(Seconds()) is a trick to ensure of uniqueness
			Local lcTempFile
			lcTempFile = Thisform._TreeFile+Transform(Int(Seconds()))
			Strtofile(Filetostr(Thisform._TreeFile),m.lcTempFile)
 
			* Automate
			Wait Window "Please wait while converting the file to a viewable format inside MSWord!" Nowait
			Local loword As Word.Application
			loword = Createobject("word.application")
			loword.Documents.Open(m.lcTempFile,,,,,,,,,,437)
			loword.Visible = .T.
		Else
			Messagebox("Please Select a Folder then click Create Tree Now button first!",0+64,"Ooooppssss!")
		Endif
	Endfunc
 
Enddefine

So there you have it, a files and folders lister; free, fast, and accurate to the end.  I hope that in addition to helping you with your files and folders mapping need, this tool can guide you also on how to use Native VFP and external objects.

Enjoy!

 

Participating in the forum makes you aware of some things where members got some confusion and that is the reason why I decided to add this in my tutorials.  With this one, I will limit the topic to combobox to help us understand better how to manipulate things within it.  I said before with MDOT and NOFILTER articles,  if you will use one, at least you know why.  This time however, the purpose is if you will use a combobox, at least you know how.

One scenario with combobox that I recently encountered inside Foxite Forum is hiding of columns.  Sometimes you would want to include two RowSource but doesn’t want to show the 2nd one, right?  Problem is even if you put the 2nd column as 0, there will be a space shown for that column and that makes the combobox “ugly”.  Take a look at the image below:

Data:

CREATE CURSOR junk (Country c(20), CtyPK I)
INSERT INTO junk VALUES ("Philippines",1)
INSERT INTO junk VALUES ("America",2)
INSERT INTO junk VALUES ("Australia",3)
INSERT INTO junk VALUES ("Canada",4)

Settings:

.RowSource = "junk.Country,CtyPk"
.RowSourceType = 6
.ColumnCount = 2
.ColumnWidths = "100,0"


So the question is, why would you make the ColumnCount property as 2 if what you plan to show anyway is just the first column?  The solution to hiding it is retaining the columncount as 1:

Should be Settings:

.RowSource = "junk.Country,CtyPk"
.RowSourceType = 6
.ColumnCount = 1  && you don't want to show column 2 so make columncount 1
.ColumnWidths = "100"  && or just leave this and allow the combobox to automatically adjust the width based on field with, i.e., c(20)


Question now is if you don’t show the other column, will the data also disappear?

No!

Can we therefore still get the value from that missing column?

Of course, it is actually not missing.  It is just there but is hidden.  By default, value of combobox is binded to column1 or the first field of the RowSource property.  To tell VFP that what we want to get instead is the value of the hidden column, you simply have to change the BoundColumn property to your target column, be it hidden or shown.  So to get the value of column 2, all you need to do is:

.BoundColumn=2

And VFP will know that you are instructing it to get the value, not from the visible column, but from that hidden column of the RowSource property.

One thing I believe that is misunderstood about combobox’ RowSource and ColumnCount property is that those two, although works in harmony, is totally dependent of each other. No it is not.  You can create a number of fields as rowsource and create less or more than that as columns.   And it will still work or run without any error.

You have to analyze and realize that the reason why RowSource is in Data Tab is because it deals with data while ColumnCount is in Layout Tab as it deals with Appearance. You should not confuse yourself with the linkings VFP made between the two.

In the above image, I have shown how ColumnCount works when it is less than the number of fields in the RowSource.  Below I will show you that ColumnCount can have more columns than that of the fields of the RowSource:

Increasing Columns beyond RowSource fields:

.RowSource = "junk.Country,CtyPK"
.RowSourceType = 6
.ColumnCount = 5    && I make 5 columns here but RowSource only have two fields
.ColumnWidths = "100,100,100,100,100"

And here is what will happen based on the settings above:



Can we hide more than one column?

Yes we can, you can hide as many as you want but the minimum is 1 column because it needs something to drop to.

The reason why even though columns are hidden, we put those as part of RowSource fields is because we intend to use those.  Again, to get the values from the hidden columns, all you need to do is use the BoundColumn property.  So if we put:

this.RowSource = "mycursor.field1,field2,field3,field4,field5"
this.RowSourceType = 6
this.ColumnCount = 2

By default, value is binded to column1.  To get the value from a different column, shown or hidden, just change the BoundColumn as follows:

this.BoundColumn = 2   && gets the value of column2  (shown)
this.BoundColumn = 3   && gets the value of column3  (hidden because columncount = 2)
this.BoundColumn = 4   && gets the value of column4  (hidden)
this.BoundColumn = 5   && gets the value of column5  (hidden)

When I reduced the ColumnCount of the Combobox, it does not look good as the dropdown portion is shorter than the width of the combobox.  Can we increase the width of the dropdown portion?

Sure, you can for instance pad spaces to the right of the items of combobox by doing something like this:

.RowSource = "PADR(mycursor.country,50)"



How about making the width smaller than the source’s field’s width in case I have a very long width that does not look good on dropdown portion?

You can trim it via LEFT() function like:

.RowSource = "LEFT(mycursor.country,10)"

Can we get more than one value from a combobox at a time?

Yes we can.  Normally we get the value via Value property but there is one property that is again overlooked which is DisplayValue property.

What is this DisplayValue Property?

DisplayValue property specifies the contents of the first column of the selected item in a ListBox or ComboBox control.  Meaning if the value you wanted will come from the first column, then you can use that instead of the Value property.

Value and DisplayValue property will have the same result when BoundColumn is set to 1 (default).   Setting the BoundColumn property other than one will then enable you to get two values at the same time.

thisform.combo1.BoundColumn=2   && set this to the target column you want value to return
MESSAGEBOX(thisform.combo1.DisplayValue)   && This will always return value of column1
MESSAGEBOX(thisform.combo1.Value)          && this will always be based on BoundColumn property, in our sample here from column2

I hope that this simple tutorial will help clear up some confusions over the usage of combobox and I hope that this can guide you make your combobox appear neater.  Now go make your combobox presentation neater!

 

Here is a trick on Restricting users changing the local CPU time.  Some people need it especially when a software is based on the running local unit’s clock, so by restricting the users’ ability to change it, they cannot cheat.

There are several ways to restrict it, but we will focus on Group Policies and some outside tricks.  To start, open Group Policy Editor by clicking Start button, Run, typing GPEDIT.MSC and hitting enter.

Once inside, navigate to these Folders:

a. Local Computer Policy
b. User Configuration
c. Administrative Templates

* Hiding the clock on system tray of Taskbar (User Notification Area)

On Start Menu and Taskbar Folder:
- Remove Clock from the system notification area – set to Enabled

* Hiding the Date and Time icon on Control Panel
On Control Panel Folder:
- Hide Specified Control Panel applets – set to Enabled.  Then click Show button, click Add button, type timedate.cpl, click Ok 3x

So we have hidden both clock in the system tray and the control panel.  So users can not change the time anymore?  Wrong!

User can still go to CMD and type TIME, enter and presto they can still change the time.  So what we need to do is prevent also the access to DOS prompt.  Here is how to do that:

On System Folder:
- Prevent access to command prompt – set to Enabled

Wonderful!  Now we have totally restricted users in changing the time!  Err… not quite yet, there is still a trick the user can do to change time although it is normally not thought of.  User can use RUN command and type timedate.cpl and press enter, and presto the hidden timedate applet in control panel will be triggered.  Or they can go to \system32 and look for that timedate.cpl and double-click it.

This last one proves to be a little tricky and actually all we can do to is employ some tricks.  I haven’t been able to properly disable this even in registry so be contented with certain tricks I will show here.

Trick#1.
Go to \Windows\System32 and look for timedate.cpl.  Once found, rename it to another name like screen.cpl or any other name.  Use names that might not attract attention.

There is a backup of every native control panel applet and it is located in \system32\dllcache.  Look for timedate.cpl and rename it as well with the same name you will use for the one located in \system32.  If you fail to do it, on next boot, your OS will check those .cpl and if it find it missing, it will restore it from that backup

However, now that the applet is no longer named timedate.cpl, policy against it on hiding its presence in control panel will no longer work as it will look for the file named timedate.cpl and not screen.cpl.  I have mentioned those steps ahead to show how things can be done but now we also need to change the name of the applet that needs to be hidden inside control panel.  So open Group Policy Editor (GPEDIT.MSC) again and repeat steps for Control Panel Folder above with one exception:  Instead of adding timedate.cpl, add now screen.cpl.  Now we are preventing two applets to show, one is named timedate.cpl and another is screen.cpl.

Or you can do this instead:

Trick#2.
Right click that timedate.cpl and choose properties, click Change button and Select Paint.  Now all applets will be associated with Paint and it won’t open when double-clicking that applet in the explorer or using RUN, typing that applet name there and pressing enter.

These tricks are not foolproof and can be easily reversed if a user will know what has been done to restrict its usage.  But that will need a very experienced user for the tricks implented here to be realized by them then later countered.

Now, go restrict time changing ability by a user.

P.S.  Actually you can leave the clock showing in System Tray.  Disabling the control panel applet as shown in Tricks# 1 & 2 plus restricting command prompt is already enough.

 

Last year, I have the need to snoop PC Names and the corresponding IP Addresses so I created a tool and posted it in Weblogs.  It is okay but is slow, but I don’t mind as it is not that important to me.  I have it, I use it, and forget about it; and use it until once again needed.

Today however, another member of foxite forum downloaded that weblogs sample I gave and told me inside the forum that he has been encountering minor problem in it so I told him once our problem with VSAT (we cannot communicate to the sattelite) is fixed, I will work on it.  The VSAT problem is solved so I worked on it.  Whereas before I attached a zip of that small project, now I will post here in pure codes so you can test outright.  And here it is:

  1. Local oForm As Form
  2. oForm = Createobject('TestForm')
  3. oForm.Show(1)
  4. Return
  5.  
  6. Define Class TestForm As Form
  7. AutoCenter = .T.
  8. Height = 500
  9. Width = 390
  10. Caption = "Computers & IP Addresses Snooper"
  11. Showtips = .T.
  12.  
  13. Add Object grid1 As Grid With ;
  14. Left = 10, Top = 45, Width = 370, Height = 420, ColumnCount = 2,;
  15. deletemark = .F., ScrollBars = 2
  16.  
  17. Add Object command1 As CommandButton With ;
  18. left = 10,;
  19. top = 10,;
  20. caption = "\<See who is connected!",;
  21. autosize = .T.
  22.  
  23. Procedure Load
  24. Create Cursor junk ( PCName c(30), ipaddress c(15), nsresult m)
  25. Index On ipaddress Tag ipaddress
  26. Index On PCName Tag PCName
  27. Endproc
  28.  
  29. Procedure Init
  30.  
  31. Set Default To Sys(2023)
  32. With Thisform.grid1 As Grid
  33. .RecordSource = "junk"
  34. .RecordSourceType = 6
  35. .column1.ControlSource="junk.pcname"
  36. .column2.ControlSource="junk.ipaddress"
  37. .column1.Width = 200
  38. .column2.Width = 130
  39. .column1.header1.Caption = "Computer Name"
  40. .column2.header1.Caption = "IP Address"
  41. .column1.header1.tooltiptext = "Sort by PC Name...."
  42. .column2.header1.tooltiptext = "Sort by IP Addresses...."
  43. Endwith
  44. Bindevent(Thisform.grid1.column1.header1,'click',This,'_SortName')
  45. Bindevent(Thisform.grid1.column2.header1,'click',This,'_SortIP')
  46. Endproc
  47.  
  48. Procedure command1.Click
  49. Local lcText, lcPCName, lcTextIP, lcExtractIP
  50. Set Default To Sys(2023)
  51. Clear
  52. Local owsh As wscript.Shell
  53. owsh = Newobject('wscript.shell')
  54. cmdDos = "CMD /c NET VIEW > CPUIP.txt"
  55. owsh.Run(cmdDos,0,.T.)
  56. lcText = Filetostr("CPUIP.txt")
  57.  
  58. For lnVar = 1 To Occurs('\\',m.lcText)
  59. Wait "Making a list of all units connected in the LAN" Window Nowait
  60. lcPCName = Strextract(m.lcText,'\\',Space(1),lnVar)
  61. Insert Into junk (PCName) Values (m.lcPCName)
  62. Endfor
  63. Go Top
  64.  
  65. Scan
  66. cmdDos = "CMD /c NSLOOKUP "+PCName+" > nslook2.txt"
  67. owsh.Run(cmdDos,0,.T.)
  68.  
  69. Replace nsresult With Filetostr("nslook2.txt"),;
  70. ipaddress With Iif(Left(Substr(Mline(nsresult,5,20),11,15),1)="t",;
  71. "Failed....",Alltrim(Substr(Mline(nsresult,5,20),11,15)))
  72. Thisform.grid1.Refresh
  73. Endscan
  74. Go Top
  75. Thisform.grid1.Refresh
  76. Endproc
  77.  
  78. Procedure _SortName
  79. Set Order To PCName
  80. Go Top
  81. Thisform.grid1.Refresh
  82. Endproc
  83.  
  84. Procedure _SortIP
  85. Set Order To ipaddress
  86. Go Top
  87. Thisform.grid1.Refresh
  88. Endproc
  89.  
  90. Enddefine

This is faster and better than the first one in my “Who is stealing my IP address?” blog.  So Enjoy and I hope this can help you when needed!

Cheers!

 
 

Mr Jun Himself:

Sponsors

Social

Search