Monday, September 10, 2007

Using VFP Resource File

There are several ways to use the resource file. To load a bitmap from RT_BITMAP resource section, you can use GDI or GDI+ to load directly into a (handle) bitmap
[CODE]
*** Updated: Nov 23, 2007 - 07:45 AM


*** API Declaration
Declare Long LoadLibrary in Kernel32 String cFilename
Declare Long FreeLibrary in Kernel32 Long hModule

Declare Long LoadImage in User32 ;
Long hInstance, String lpszName, Long uType, ;
Integer cxDesired, Integer cyDesired, Long nLoadFlags

Declare Long GdipCreateBitmapFromResource in GdiPlus.dll ;
Long hInstance, String cBitmapName, Long @O_Bitmap

hBitmap = 0
hLibrary = LoadLibrary( 'myResource.LIB' )
If (hLibrary != 0)
** Use GDI
cResName = 'myImage' && Resource name
hBitmap = LoadImage( hLibrary, cResName, 0, 0, 0, 0 )

** Use GDI+
wResName = strconv( strconv( cResName + chr(0), 1 ), 5 )
GdipCreateBitmapFromResource( hLibrary, wResName, @hBitmap )

FreeLibrary( hLibrary )
endif

If (hBitmap != 0)
** We have a bitmap, do what you want to do here
** Don't forget to delete the bitmap,
** DeleteObject() for GDI, GdipDisposeImage() for GDI+
endif
[/CODE]

I also shown you about putting an image (PNG) to custom resource section. Since the image was put as a raw data, you can only load it back as raw data. After you get the raw data, you can save it to a file, or you can also create a stream data to create the bitmap from the stream. GdipCreateBitmapFromResource() doesn't work for this resource type.
[CODE]
*** API Declaration
Declare Long FindResource in Kernel32 as FindResourceStr ;
Long hModule, String lpName, String lpType

Declare Long SizeofResource in Kernel32 Long hModule, Long hResource
Declare Long LoadResource in Kernel32 Long hModule, Long hResource
Declare Long LockResource in Kernel32 Long hResData

cData = ''
hLibrary = LoadLibrary( 'myResource.LIB' )
If (hLibrary != 0)
cResName = 'myPNG'
hResource = FindResourceStr( hLibrary, cResName, 'MYIMAGES' )
If (hResource != 0)
nSize = SizeofResource( hLibrary, hResource )
hData = LoadResource( hLibrary, hResource )
pData = LockResource( hData )
cData = sys( 2600, pData, nSize )
endif
FreeLibrary( hLibrary )

endif

If !empty( cData )
** We got the raw data, do what you want to do here
endif
[/CODE]

In my last tips, I didn't show you all the predefined resources. there are several others actually, such as RT_ICON, RT_CURSOR, etc. It is my intention to not using resource file for other images type. Because those images are usually use only with VFP Image object. So, you can still use your project to put other images. Just consider to use the resource file when you have to distribute files physically that you can't put in your project.

Happy coding!


Monday, September 03, 2007

Creating a VFP Resource File

What is a resource file? A resource file is sort of a container file. Using resource file, you only have to distribute one single file instead of distribute many files. You can use this file to put any file such as image (BMP, JPG, PNG, etc). You can use this to distribute a hidden report file. You can also use this file in conjunction with my OwnerDrawn menu class. In short, you can use this file for many purposes.

How to create it? Very simple! Here is the step:
1. Create a new program (for the Main program)
2. Put a QUIT command into it
3. Create a new project.
4. Add the Main program into the project
5. Uncheck the project debug info
6. Build the project as an EXE, but name it as "myResource.LIB" (give the extension yourself)

Once you created your LIB file, how can you put your file into it? You must put the raw data into the resource file. There are three WinAPI functions to help you to put the raw data into it. BeginUpdateResource(), UpdateResource() and EndUpdateResource().

Let's look at the example. Suppose you want to put an image file ("myPNG.PNG") into it, here's how you do it:
[CODE]

*** Updated: Nov 23, 2007 - 07:45 AM

*** API Declaration
Declare Long BeginUpdateResource in Kernel32 ;
String cFileName, Long bDeleteExistingResources

Declare Long UpdateResource in Kernel32 as UpdateResourceStr ;
Long hUpdate, String lpType, String lpName, ;
Short wLanguage, String lpData, Long nLenData

Declare Long UpdateResource in Kernel32 as UpdateResourceStr2 ;
Long hUpdate, Long lpType, String lpName, ;
Short wLanguage, String lpData, Long nLenData

Declare Long EndUpdateResource in Kernel32 ;
Long hUpdate, Long fDiscard

*** Start code
cImageFile = 'myPNG.PNG'
cResFile = 'myResource.LIB'
cResGroup = 'MYIMAGES' && a resource section group, make uppercase
cResName = upper( JustStem( cImageFile ))
&& a resource name, make uppercase


cRawData = FileToStr( cImageFile )
hResFile = BeginUpdateResource( cResFile, .F. )

If (hResFile != 0)
UpdateResourceStr( hResFile, cResGroup, cResName, 0, ;
cRawData, len( cRawData))
EndUpdateResource( hResFile, .F. )

endif
[/CODE]

Suppose you want to put an image file that has a BMP extension, you better put it into the predefined resource section group. Change the code above into this:

[CODE]
*** Start code
cImageFile = 'myImage.BMP'
cResFile = 'myResource.LIB'
nResGroup = 2 && RT_BITMAP, a predefined resource section group
cResName = upper( JustStem( cImageFile ))
&& a resource name, make uppercase

cRawData = FileToStr( cImageFile )
cRawData = substr( cRawData, 15 ) && eliminate BITMAPFILEHEADER structure (14 chars)
hResFile = BeginUpdateResource( cResFile, .F. )
If (hResFile != 0)

UpdateResourceStr2( hResFile, nResGroup, cResName, 0, ;
cRawData, len( cRawData))
EndUpdateResource( hResFile, .F. )

endif
[/CODE]

That's it, try and experiment yourself. In the next tips, I will show you how to use the resource file.

Happy coding!




Sunday, August 12, 2007

Adding Custom Paper Size Programmatically

There are times when we need to make a custom report size. Before you can create a custom size, you have to manually add a Printer Form Size (Control Panel -> Printer & Faxes -> File -> Server Properties). Then you can set the Page Setup in your report to use the custom printer form.

The hard part is, when you distribute the application to the client, you must also set the same Custom Form Size for their printer configuration. Of course you don't want to do it one by one manually! So, what you need is create a small procedure to detect whether the custom printer form is already exist. If not then add it.

Notes:
- Cut * paste the code below into PRG, then run "beautify" to make the code readable
- BinToC() function in this code is an Enhanced function in VFP9. For VFP8 and lower version, use UDF called Long2Str or Num2Str. You can find the function on UT or other VFP forums.

[Code]
Local hPrinter
Local cPrinterName, cPaperName
Local pPaperName, sPaperSize
Local nResult, nBufLen, nPaperWidth, nPaperHeight


Declare Long GetLastError in Kernel32
Declare Long ClosePrinter in WinSpool.Drv Long hPrinter
Declare Long OpenPrinter in WinSpool.Drv ;
String cPrinterName, Long @O_hPrinter, Long pDefault

Declare Long GetForm in WinSpool.drv as GetPrinterForm ;
Long hPrinter, String pFormName, ;
Long nLevelInfo, String @O_pFormInfo, ;
Long nBufSize, Long @O_nBufNeeded

Declare Long AddForm in WinSpool.drv as AddPrinterForm ;
Long hPrinter, Long nLevelInfo, String @pFormInfo

Declare Long LocalAlloc in Kernel32 Long uFlags, Long dwBytes
Declare Long LocalFree in Kernel32 Long hMem

cPrinterName = set( 'Printer', 2 ) && Get default Windows printer
hPrinter = 0

If (OpenPrinter( cPrinterName, @hPrinter, 0 ) != 0)
cPaperName = 'MyCustom - Half A4'
nBufLen = 32 && FORM_INFO_1_Size
cInfo = replicate( chr(0), 32 )
nResult = GetPrinterForm( hPrinter, cPaperName, 1, ;
@cInfo, nBufLen, @nBufLen )

If (nResult == 0) && Get printer form failed
nResult = GetLastError()

If (nResult == 1902) && ERROR_INVALID_FORM_NAME
** Custom Printer Form not exist, add the new one
nPaperWidth = 210000 / 2 && Paper size is in 1/1000 millimeters
nPaperHeight = 297000 / 2
sPaperSize = BinToC( nPaperWidth, '4rs' ) + BinToC( nPaperHeight, '4rs' )
pPaperName = LocalAlloc( 64, 32 )

If (pPaperName != 0)
sys( 2600, pPaperName, len( cPaperName ), cPaperName )
cInfo = BinToC( 0, '4rs' ) + BinToC( pPaperName, '4rs' ) + ;
sPaperSize + BinToC( 0, '4rs' ) + BinToC( 0, '4rs' ) + sPaperSize

If (AddPrinterForm( hPrinter, 1, cInfo ) != 0)
? 'Custom paper form (' + cPaperName + ') has been added! '
else
? 'Error:', GetLastError()
endif

LocalFree( pPaperName )
endif

else
If (nResult == 122) && Insufficient buffer
? 'Error: Custom Paper Form already exist!'
else
? 'Error: ', nResult
endif
endif
else
? 'Error: ', nResult
endif
ClosePrinter( hPrinter )
endif

[/Code]

Happy coding! :)