Welcome to Foxite.COM Community Weblog Sign in | Join | Help

Just to share another themedcontrol prototype I created for own use based VFPX Themedcontrol project - GradientHeaderTitle.

 

Step to use:

  1. Download mythemedcontrol.vcx and theme.xml
  2. Drop ThemedTitleHeader control on to form.
  3. Specify value to the following property:
    • Description - Description of form.
    • Title - Title of form.
    • TitleImage - File path of image.
  4. Run the form.

I just created themedbutton, by referring code of ThemedControls project in VFPX. It is quite easy to follow.

Normal

Hover

Focused

However, it is not completed. There are some limitation/enhancements required:

  • Anchoring is not yet supported. (VFP doesn't anchor internal controls if parent container is adjusted during design time)
  • Only Click event is supported.
  • It is based on control base class. Native command button property such as picture margin, spacing and position is not yet implemented.
  • Only picture with size 16X16 pixels is supported.
  • That are "ugly" parts at four corners.

 

I need to resize user provided image using picturebox with size 48X48 pixels. I "googled" and found the following sample code:

Using DrawImage

FileStream loFileStream = new FileStream(@".\RefreshCL.png", FileMode.Open, FileAccess.Read);
Image loOriginalImage = Bitmap.FromStream(loFileStream);           
Bitmap loBitmap = new Bitmap(48, 48);

using (Graphics g = Graphics.FromImage((Image)loBitmap))
{
    g.DrawImage(loOriginalImage, 0,0, 48,48);
    pictureEdit1.Image = loBitmap;
}
As we can see the result as above, it is not good. The image is broken

 

I "googled" again and get this code

Using GetThumbnailImage

FileStream loFileStream = new FileStream(@".\RefreshCL.png", FileMode.Open, FileAccess.Read);
            
Image loOriginalImage = Bitmap.FromStream(loFileStream);

loOriginalImage = loOriginalImage.GetThumbnailImage(48, 48, new Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero);

pictureEdit1.Image = loOriginalImage;

public bool ThumbnailCallback()
{
    return true;
}

As this is much better.

Microsoft has released Sync Framework couple months ago, enable offline data collection applications to synchronize local data back to another/centralized database.

Eventually, this feature is already available in SQL Server (Data Replication) since long time ago. However, it is not so easy for developers as it required certain level of knowledge of SQL Server. Deployment is not so straight forward as well. At least, it is not by "Developer" way. Sync framework introduced is something that really helps in my opinion.

SQL Server Express is a lightweight edition of SQL Server. Most importantly, it is free! Therefore, It is always the best choose as database server used at branches. Then, data would be sync back to main database intervally.

Nevertheless, Sync framework is more focus on SQL Server CE, mobile devices. More and more providers are released for these few months but not yet any provider for SQL Server Express. Sync provider for SQL Express has been requested at least for six months. And finally, Microsoft has released Sync Provider for SQL Express as sample at Code Gallery. In the future Microsoft certainly plan of including a fully supported version of a SQL Express client provider within a future release of Sync Services for ADO.NET.

Source: Sample - SQL Express Client Synchronization using Sync Services for ADO.NET

Well, it might not any latest news. But it is really rock!

(Updated) Source:

http://weblogs.foxite.com/vfpimaging/archive/2008/05/21/6080.aspx
http://weblogs.foxite.com/vfpimaging/archive/2008/04/24/6040.aspx
http://weblogs.foxite.com/vfpimaging/archive/2008/04/04/5919.aspx

VFPX: http://www.codeplex.com/VFPX/Wiki/View.aspx?title=FoxCharts&referringTitle=Home

 

 

Technorati tags: , , , ,

That day I hit error as above when I want to access my localhost IIS. Luckily I could get it fixed very soon. It is caused by Skype.

http://blog.jtbworld.com/2007/01/skype-behind-iis-error.html

 

Technorati tags: ,

DataEnvironment class has been introduced since VFP8. We could get more info from VFP help.

mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20FoxPro%208\dv_foxhelp.chm::/html/ctlDataEnvironment_Object.htm

Last few days, we tried to migrate some of the form's DataEnvironment to class for customization purpose. we then dynamically assign DEClass and DEClassLibrary property at form to have dynamic DataEnvironment.

However, we just hit error complaint that object "DataEnvironment" is not found. After some troubleshooting, we found that our application framework (VPME8) requires to access DE property values of DataEnvironment during form initialization. No matter what, the framework require DE's name to be DataEnvironment. In fact, DE's naming convention is DE's name + nth.

We found two workarounds:

Using AddObject()

SET CLASSLIB TO <DELib> ADDITIVE

THISFORM.RemoveObject("DataEnvironment")
THISFORM.AddObject("DataEnvironment", "<DE>")

Using Property

THISFORM.RemoveObject("DataEnvironment")

loDE = NEWOBJECT("<DE>", "<DELib>")
THISFORM.AddProperty("DataEnvironment", loDE)

My team is moving to a new web product which would be developed in .NET C#. We are currently 9 developers and going to grown to 12. This is not the first .NET application. In fact, there are couple of them already. One of the problems we are having is not strong in SOP (Standard Operation Procedure). Very simple guideline such coding standard.

Even though we have coding standard documentation downloaded online, most of them are not followed.

  • The document is too long to read.
  • Developers might confuse with so many guidelines.
  • Manual code review required even for simple thing such as naming convention. Very time consumed.

Recently, I am researching on any tool that able to help us to:-

  • Detect any violations.
  • Suggest and auto correct violations.
  • Ability to be integrated with any CI (Continuous Integration) tool.

And I found two tools as below:-

  1. StyleCop
    • Enforce a common set of best practices for layout, readability, maintainability, and documentation of C# source code.
    • Has been used for many years now internally at Microsoft.
    • Analysis source code instead of compiled binary.
    • It is free
    • It contains 200 best practice rules covered the following:-
      • Layout of elements, statements, expressions, and query clauses
      • Placement of curly brackets, parenthesis, square brackets, etc
      • Spacing around keywords and operator symbols
      • Line spacing
      • Placement of method parameters within method declarations or method calls
      • Standard ordering of elements within a class
      • Formatting of documentation within element headers and file headers
      • Naming of elements, fields and variables
      • Use of the built-in types
      • Use of access modifiers
      • Allowed contents of files
      • Debugging text
    • Integrated with Visual Studio IDE, and also MSBuild-based command line builds.
    • Ability to analysis code per file basis.
    • FxCop vs StyleCop
      • StyleCop - focuses on layout, readability and documentation
      • FxCop - focuses on design of the code
    • Limitations
      • Does not allow its rules to be very configurable.
      • Does not provides auto correction actions. Manual correction is required.
    • Downloadable: http://code.msdn.microsoft.com/sourceanalysis/Release/ProjectReleases.aspx?ReleaseId=1047
    • Blog: http://blogs.msdn.com/sourceanalysis/
  2. CodeIt.Right
    • Combines static code analysis and automatic refactoring to best practices in one application. But there is more - CodeIt.Right will automatically correct code errors and violations (e.g. naming conventions, incorrectly implemented coding patterns, etc)!
    • Comprehensive rule set based on Microsoft .NET Guidelines, Framework Design Guidelines and best practices - right out of the box
    • Integrated with Visual Studio IDE, and also MSBuild-based command line builds.
    • Ability configure rules and create custom rules based on existing base rule.
    • SDK available to allow creating of new complex custom rules.
    • It contains 100+ best practice rules.
    • Rule Library Auto-update.
    • Multiple profiles - group rules the way you want and quickly switch between profiles.
    • Safely Undo/Redo the changes - multi-file Visual Studio undo/redo.
    • Ability to analysis code in solution or per project basis.
    • Contains two editions
      • Standard Edition - Rules authoring, machine basis.
      • Enterprise Edition - Standard creation is separated into a separate module, allowing a Team Lead or an Architect create the team guidelines and then enforce them to the rest of the team. Also it comes with two modules included into the package - the Analysis Module and the Team Configuration Module. The latter besides tweaking the guidelines to the team standards is also responsible for publishing and pushing the end result to the developer workstations. (is currently in the works and will be out end of the summer 2008)
    • Commercial: http://submain.com/order.aspx
    • Trial version available: http://submain.com/?nav=download& (30 days evaluation)
    • Blog: http://community.submain.com/blogs/default.aspx
    • Tutorial available: http://community.submain.com/content/Tutorials.aspx
    • More details: http://submain.com/?nav=products.cir
    • Limitations
      • Rule list are not "complete" enough, but many rules to be released every week.
    • Time saving to standardize existing source code since auto correction feature built-in.

Personally, I feel that StyleCop is very good because it contains quite complete rule list. It is suitable for whom:

  • Do not mind to correct violations manually.
  • Apply on new project only since any corrections could be done immediately without long violation list prompted. Apply to existing project would require more resources to correct violations.
  • Willing to follow Microsoft internal coding standard.

On the other hand, CodeIt.Right would be suitable for whom:

  • Would like to apply coding standard to existing "legacy" project. Auto correction would save a lot of time.
  • Do not wish to follow Microsoft internal coding standard exactly.
  • Would like to have additional rules or develop own custom rules.
  • Do not want to correct violations manually.
  • Would like to automate violations correction process during CI (Continuous Integration).

No matter which tool to be used, it would really help because no manual code review is required.

Happy coding!

Sometime we might want to re-use resources such as image that already exist in external assembly. We need to know what resource exist.

Here is sample code that iterate all DLL files and display its resource name.

 

foreach (string filename in System.IO.Directory.GetFiles(@"C:\Documents and Settings\chankk\My Documents\Visual Studio 2008\Projects\ClassLibrary1\bin\debug", "*.dll"))
{
    Assembly loAss = Assembly.LoadFrom(filename);

    foreach (string resname in loAss.GetManifestResourceNames())
    {
        if (resname.EndsWith(".resources"))
        {
            Stream stream = loAss.GetManifestResourceStream(resname);

            ResourceReader rreader = new ResourceReader(stream);

            IDictionaryEnumerator ie = rreader.GetEnumerator();

            while (ie.MoveNext())
            {
                textBox1.Text += ie.Key + Environment.NewLine;
            }
        }
    }
}

I am changing my application to be Vista compatible. I need to run an external EXE and wait for until it terminate. Beside, I also need to run this EXE with Admin privilege under certain condition.

To run external application and wait till it terminate, I found the code as below.

loShell = CREATEOBJECT("WScript.Shell")
loShell.Run("C:\MyApp\MyApp.exe", 1, 1)

However, it doesn't support elevation.

To run application via elevation process, I can

DECLARE INTEGER ShellExecute IN "Shell32.dll" ;
	INTEGER hwnd, STRING lpVerb, STRING lpFile, STRING lpParameters, ;
	STRING lpDirectory, LONG nShowCmd

ShellExecute(0, "runas", "C:\MyApp\MyApp.exe", "", "", 1)

Unfortunately, ShellExecute() doesn't wait but return control back to calling program immediately.

In order to have both features, I found ShellExecuteEX ...

DECLARE LONG ShellExecuteEx IN Shell32 STRING @SHELLEXECUTEINFO

DECLARE INTEGER WaitForSingleObject IN kernel32;
	INTEGER hHandle, INTEGER dwMilliseconds

LOCAL loSHELLEXECUTEINFO AS SHELLEXECUTEINFO OF SHELLEXECUTEINFO.prg, ;
		lcStructure AS String

loSHELLEXECUTEINFO = NEWOBJECT("SHELLEXECUTEINFO", "SHELLEXECUTEINFO.prg")
loSHELLEXECUTEINFO.fld("lpVerb") = "runas" + CHR(0)
loSHELLEXECUTEINFO.fld("lpFile") = "C:\MyApp\MyApp.exe" + CHR(0)
loSHELLEXECUTEINFO.fld("nShow") = 1
loSHELLEXECUTEINFO.fld("lpParameters") = CHR(0)

**********************************************
* Required in order to get process id returned
**********************************************
loSHELLEXECUTEINFO.fld("fMask") = SEE_MASK_NOCLOSEPROCESS

lcStructure = loSHELLEXECUTEINFO.Structure 

ShellExecuteEx(@lcStructure)
loSHELLEXECUTEINFO.Structure = lcStructure

***********************************************************
* Wait for application termination (-1)
***********************************************************
WaitForSingleObject(loSHELLEXECUTEINFO.fld("hProcess"), -1)

**************************************************************
* Define SHELLEXECUTEINFO strucuture using class Struct
* from http://fox.wikis.com/wc.dll?Wiki~ApiStructureClass~VFP
**************************************************************

DEFINE CLASS SHELLEXECUTEINFO AS Struct OF Struct.prg

	#IF .F.
		LOCAL THIS AS SHELLEXECUTEINFO OF SHELLEXECUTEINFO.prg
	#ENDIF

	PROCEDURE INIT()
	
		THIS.AddField("cbSize", "LONG", 60)
		THIS.AddField("fMask", "LONG", 0)
		THIS.AddField("hwnd", "LONG", 0)
		THIS.AddField("lpVerb", "@STRING", CHR(0))
		THIS.AddField("lpFile", "@STRING", CHR(0))
		THIS.AddField("lpParameters", "@STRING", CHR(0))
		THIS.AddField("lpDirectory", "@STRING", CHR(0))

		THIS.AddField("nShow", "LONG", 0)
		THIS.AddField("hInstApp", "LONG", 0)
		THIS.AddField("lpIDList", "@STRING", CHR(0))
		THIS.AddField("lpClass", "@STRING", CHR(0))
		THIS.AddField("hkeyClass", "@STRING", CHR(0))
		THIS.AddField("dwHotKey", "LONG", 0)
		THIS.AddField("hIcon", "LONG", 0)
		THIS.AddField("hProcess", "LONG", 0)

	ENDPROC	

ENDDEFINE

I used ApiStructureClass to create structure that required by ShellExecuteEx. It makes job much easier.

I like to use NEWOVBJECT() to initiate object so that I don't have to care about SET PROCEDURE TO / SET CLASSLIB TO.

Recently, I am re-writing my company application update program. That is a method that loop thru all the necessary directory and files and store the file info (as per array content returned by ADIR()) into collection. The file details are stored as object based (I have class called FileInfo). FileInfo object is created using NEWOBJECT("FileInfo", "FileInfo.prg") function.

During the testing, I found the program takes 6.6 secs to check for 1200 files. The code of NEWOBJECT("FileInfo", "FileInfo.prg") was hit 2400 times due to the same function being called for source and target file. Each hit take average 0.0027 secs. That meant program spent 6.47 secs just to create FileInfo objects. This is unacceptable.

I changed my code SET PROCEDURE TO FileInfo.prg ADDITIVE at program startup and replace NEWOBJECT() with CREATEIBJECT(). Hey, the program now only take 1.5 secs to do the same thing. Therefore, we got to use NEWOBJECT() with care otherwise it might kill your application.

 

Technorati tags: ,

Craig has posted a screencast that demonstrate his current work to use Visual Studio IDE for VFP code editing.

It is amazing. Once again, thank Craig.

Technorati tags: , ,

As I am automating VFP build process as mentioned, I always need to execute VFP code from non-VFP environment. In order to have "full" power of VFP, I choose to use OLE automation using VBscript.

There are two ways to run VFP code from VBScript which I use frequently:

  • Writing some code in prg. This is a very straight forward appraoch
Set oFox = CreateObject("VisualFoxPro.Application")
oFox.DoCmd("DO myPrg WITH '" & MyParameter & "'")
  • Pass VFP code string
'USE myTable
'SCAN
'    IF myField = 'A'
'        ?myField
'    ENIDF
'ENDSCAN

Set oFox = CreateObject("VisualFoxPro.Application") 
oFox.DoCmd("EXECSCRIPT('" & sCommand & "')")

This approach is very useful if we allow other user to enter VFP code in UI which involve multiple lines of code, without need of VFP IDE.It seem very simple, right? Yes, it is. However, I just hit one error "Runtime Error" when we try to run the code above. It is because the VFP code string contains CRLF. The workaround I found is:

oFox.SetVar "lcCode", sCommnad
oFox.DoCmd("EXECSCRIPT(lcCode)")
Do you have any other way to do this?
 
Technorati tags: ,

I just found that is "really" written in C/C++. When I try to replace string from "myfolder\" to "myfolder\trunk" using Find and Replace dialog, VFP would replace my text as below

..\myfolder\myfile.prg ==> ..\myfolder\{TAB}runk\myfile.prg

VFP treat "\t" is tab identifier with is normally used in C/C++ type of language. For workaround, just put one more "\" in replace string column, "myfolder\\trunk".

 

Technorati tags: ,

VFP allow us to edit project version info from build dialog.

We also can edit it programmatically as below.

Both ways is working fine, except VFP IDE is required.

I have written a simple class to enable Project and Version Info edition programmatically without need of VFP IDE. This class would open pjx file, read/write content to DevInfo column.

USE myProj.pjx

loDevInfo = NEWOBJECT("DevInfo", "DevInfo.prg")
loDevInfo.VersionMajor = TRANSFORM(THIS.Major)
loDevInfo.VersionMinor = TRANSFORM(THIS.Minor)
loDevInfo.VersionRevision = TRANSFORM(THIS.Revision)

LOCATE FOR type = "H"
REPLACE DevInfo WITH loDevInfo.GetDevInfo()

** Author	0x00-0x2D
** Company	0x2E-0x5B
** Address	0x5C-0x89
** City		0x8A-0x9E
** State		0x9F-0xA4
** Postcode	0xA5-0xAF
** Country	0xB0-0xDD
** Comment	0xDE-0x1DC
** Company	0x1DD-0x2DB
** Descr		0x2DC-0x3DA
** (C)		0x3DB-0x4D9
** Trade		0x4DA-0x5D8
** Product	0x5d9-0x6D7
** Major		0x6D8-0X6DC
** Minor		0x6DD-0x6E1
** Revision	0x6E2-0x6E6
** Language	0x6E7-0x702

#DEFINE	AUTHOR_LENGTH				0X2D - 0X00 + 1
#DEFINE	COMPANY_LENGTH				0X5B - 0X2E + 1
#DEFINE	ADDRESS_LENGTH				0X89 - 0X5C + 1
#DEFINE	CITY_LENGTH					0X9E - 0X8A + 1
#DEFINE	STATE_LENGTH				0XA4 - 0X9F + 1
#DEFINE	POSTCODE_LENGTH			0XAF - 0XA5 + 1
#DEFINE	COUNTRY_LENGTH				0XDD - 0XB0 + 1
#DEFINE	COMMENTS_LENGTH			0X1DC - 0XDE + 1
#DEFINE	COMPANYNAME_LENGTH		0X2DB - 0X1DE + 1
#DEFINE	FILEDESCRIPTION_LENGTH	0X3DA - 0X2DD + 1
#DEFINE	COPYRIGHT_LENGTH			0X4D9 - 0X3DB + 1
#DEFINE	TRADEMARKS_LENGTH			0X5D8 - 0X4DA + 1
#DEFINE	PRODUCTNAME_LENGTH		0X6D7 - 0X5DA + 1
#DEFINE	VERSIONMAJOR_LENGTH		0X6DC - 0X6D8 + 1
#DEFINE	VERSIONMINOR_LENGTH		0X6E1 - 0X6DD + 1
#DEFINE	VERSIONREVISION_LENGTH	0X6E6 - 0X6E2 + 1
#DEFINE	LANGUAGEID_LENGTH			0X702 - 0X6E7 + 1

#DEFINE	AUTHOR_POSITION	 			1
#DEFINE	COMPANY_POSITION	 			0X2E + 1
#DEFINE	ADDRESS_POSITION	 			0X5C + 1
#DEFINE	CITY_POSITION	 				0X8A + 1
#DEFINE	STATE_POSITION	 				0X9F + 1
#DEFINE	POSTCODE_POSITION	 			0XA5 + 1
#DEFINE	COUNTRY_POSITION	 			0XB0 + 1
#DEFINE	COMMENTS_POSITION	 			0XDE + 1
#DEFINE	COMPANYNAME_POSITION	 		0X1DE + 1
#DEFINE	FILEDESCRIPTION_POSITION	0X2DD + 1
#DEFINE	COPYRIGHT_POSITION	 		0X3DB + 1
#DEFINE	TRADEMARKS_POSITION	 		0X4DA + 1
#DEFINE	PRODUCTNAME_POSITION	 		0X5DA + 1
#DEFINE	VERSIONMAJOR_POSITION	 	0X6D8 + 1
#DEFINE	VERSIONMINOR_POSITION	 	0X6DD + 1
#DEFINE	VERSIONREVISION_POSITION	0X6E2 + 1
#DEFINE	LANGUAGEID_POSITION	 		0X6E7 + 1

DEFINE CLASS DevInfo AS Line

	#IF .F.
		LOCAL THIS AS DevInfo OF DevInfo.prg
	#ENDIF

	Author = ""
	Company = ""
	Address = ""
	City = ""
	State = ""
	Postcode = ""
	Country = ""
	Comments = ""
	CompanyName = ""
	FileDescription = ""
	Copyright = ""
	Trademarks = ""
	ProductName = ""
	VersionMajor = ""
	VersionMinor = ""
	VersionRevision = ""
	LanguageID = ""

	PROCEDURE Parse(tcDevInfo AS String)
	
		THIS.Author = STRTRAN(SUBSTR(tcDevInfo, AUTHOR_POSITION, AUTHOR_LENGTH), CHR(0), "")
		THIS.Company = STRTRAN(SUBSTR(tcDevInfo, COMPANY_POSITION, COMPANY_LENGTH), CHR(0), "")
		THIS.Address = STRTRAN(SUBSTR(tcDevInfo, ADDRESS_POSITION, ADDRESS_LENGTH), CHR(0), "")
		THIS.City = STRTRAN(SUBSTR(tcDevInfo, CITY_POSITION, CITY_LENGTH), CHR(0), "")
		THIS.State = STRTRAN(SUBSTR(tcDevInfo, STATE_POSITION, STATE_LENGTH), CHR(0), "")
		THIS.Postcode = STRTRAN(SUBSTR(tcDevInfo, POSTCODE_POSITION, POSTCODE_LENGTH), CHR(0), "")
		THIS.Country = STRTRAN(SUBSTR(tcDevInfo, COUNTRY_POSITION, COUNTRY_LENGTH), CHR(0), "")
		THIS.Comments = STRTRAN(SUBSTR(tcDevInfo, COMMENTS_POSITION, COMMENTS_LENGTH), CHR(0), "")
		THIS.CompanyName = STRTRAN(SUBSTR(tcDevInfo, COMPANYNAME_POSITION, COMPANYNAME_LENGTH), CHR(0), "")
		THIS.FileDescription = STRTRAN(SUBSTR(tcDevInfo, FILEDESCRIPTION_POSITION, FILEDESCRIPTION_LENGTH), CHR(0), "")
		THIS.Copyright = STRTRAN(SUBSTR(tcDevInfo, COPYRIGHT_POSITION, COPYRIGHT_LENGTH), CHR(0), "")
		THIS.Trademarks = STRTRAN(SUBSTR(tcDevInfo, TRADEMARKS_POSITION, TRADEMARKS_LENGTH), CHR(0), "")
		THIS.ProductName = STRTRAN(SUBSTR(tcDevInfo, PRODUCTNAME_POSITION, PRODUCTNAME_LENGTH), CHR(0), "")
		THIS.VersionMajor = STRTRAN(SUBSTR(tcDevInfo, VERSIONMAJOR_POSITION, VERSIONMAJOR_LENGTH), CHR(0), "")
		THIS.VersionMinor = STRTRAN(SUBSTR(tcDevInfo, VERSIONMINOR_POSITION, VERSIONMINOR_LENGTH), CHR(0), "")
		THIS.VersionRevision = STRTRAN(SUBSTR(tcDevInfo, VERSIONREVISION_POSITION, VERSIONREVISION_LENGTH), CHR(0), "")
		THIS.LanguageID = STRTRAN(SUBSTR(tcDevInfo, LANGUAGEID_POSITION, LANGUAGEID_LENGTH), CHR(0), "")
		
	ENDPROC
	
	PROCEDURE GetDevInfo() AS String
	
		LOCAL lcDevInfo AS String
		
		lcDevInfo = "" 

		lcDevInfo = lcDevInfo + PADR(THIS.Author, AUTHOR_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.Company, COMPANY_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.Address, ADDRESS_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.City, CITY_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.State, STATE_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.Postcode, POSTCODE_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.Country, COUNTRY_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.Comments, COMMENTS_LENGTH, CHR(0)) + CHR(0x20)
		lcDevInfo = lcDevInfo + PADR(THIS.CompanyName, COMPANYNAME_LENGTH, CHR(0)) + CHR(0x20)
		lcDevInfo = lcDevInfo + PADR(THIS.FileDescription, FILEDESCRIPTION_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.Copyright, COPYRIGHT_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.Trademarks, TRADEMARKS_LENGTH, CHR(0)) + CHR(0x20)
		lcDevInfo = lcDevInfo + PADR(THIS.ProductName, PRODUCTNAME_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.VersionMajor, VERSIONMAJOR_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.VersionMinor, VERSIONMINOR_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.VersionRevision, VERSIONREVISION_LENGTH, CHR(0))
		lcDevInfo = lcDevInfo + PADR(THIS.LanguageID, LANGUAGEID_LENGTH, CHR(0))
	
		RETURN lcDevInfo
		 
	ENDPROC
	
ENDDEFINE
Technorati tags: ,
More Posts Next page »