Storing binary data in the registry

by Kent Reisdorph

Saving user settings in the registry is common fare for most Windows programs today. Just take a look at the C++Builder entries in the registry and youíll see what I mean. You may already be using the registry to store user configuration data. Itís fairly easy to store integer values or strings in registry keys. Storing binary data is just as easy, but is something that is often overlooked by C++Builder programmers.

This article will explain how to store binary data in the registry. To illustrate, I will show how to store the font attributes of a TFont object to the registry and read it back again.

Writing binary data to the registry

Writing binary data to the registry is accomplished by calling TRegistryís WriteBinaryData() method. Letís take the case of TFont for example. Users on the Borland newsgroups frequently ask how to save a TFont to a file or to the registry. The key to saving and retrieving any binary data is to start with a fixed-length structure. If you have a fixed-length structure you can write exactly that many bytes to the binary stream and later, when you want to retrieve that data, you read exactly that number of bytes again. Windows provides such a structure for font data in the LOGFONT structure (this structure also has a corresponding VCL version called TLogFont). I should mention that the LOGFONT structure doesnít know anything about a fontís Color property. If you want to store a fontís color in addition to itís other attributes (size, typeface name, style, etc.) then youíll have to write the font color to the registry in a separate step.

The first step is to get the font data from a TFont object and convert it to a LOGFONT structure. This is achieved by calling the API function GetObject(), as shown here:

LOGFONT lf;
GetObject(Font->Handle, sizeof(lf), &lf);

GetObject() takes the font specified by the TFont objectís Handle property and returns its font data in a LOGFONT structure. Now that you have the font data in a fixed-length structure, writing it to the registry is a trivial matter:

TRegistry* reg = new TRegistry;
reg->OpenKey(ď\\software\\testĒ, true);
reg->WriteBinaryData('font', &lf, sizeof(lf));
delete reg;

Thatís all there is to it! Windows and the TRegistry class do all the hard work behind the scenes so you donít have to worry about it. Figure A shows the Windows Registry Editor displaying the binary data created by the preceding code.

Figure A

The Registry Editor shows the binary font data created by the example program.

Reading binary data from the registry

OK, so youíve stored the fontís data in the registry. Naturally you have to retrieve that data from the registry in order for it to be useful. Reading binary data from the registry is also fairly trivial. Simply call the ReadBinaryData() method of TRegistry as this code illustrates:

LOGFONT lf;
TRegistry* reg = new TRegistry;
reg->OpenKey(ď\\software\\testĒ, false);
reg->ReadBinaryData('font', &lf, sizeof(lf));
delete reg;

Since I stored a specific number of bytes to the registry key (sizeof(LOGFONT)) I simply read exactly that number of bytes again. The second parameter of ReadBinaryData() takes a void* so I simply pass the address of the LOGFONT variable in the second parameter, and the size of a LOGFONT structure in the third parameter. TRegistry loads the binary data into the memory location of the LOGFONT variable.

All that remains is to assign the just-retrieved LOGFONT data to the Font property of a given component (or the form itself). Here again, a little API code does the trick:

Font->Handle = CreateFontIndirect(&lf);

CreateFontIndirect() takes the data in a LOGFONT structure, creates a font from that data, and returns a font handle (and HFONT). You can assign the result of CreateFontIndirect() directly to a TFont objectís Handle property. The VCL disposes of the old font handle, applies the new font handle, and updates the label with the new font.

Conclusion

You can store any type of binary data to the registry using WriteBinaryData() and read it back again using ReadBinaryData(). The key is to read exactly the number of bytes that you originally wrote. Saving user-configuration data to the registry in binary form is simple once you know how. Listings A and B show the header and main unit of a program that illustrates the technique discussed in this article. The program displays the font selection dialog on a button click and saves the selected fontís data to the registry. A second buttonís OnClick event handler reads the font data from the registry and shows the results by applying the font to a label on the form. Figure B shows the example program running. You can download the example program from our web site at www.bridgespublishing.com.

Figure B

The example program writes a font to the registry, reads it back again, and applies the font to a label.

Listing A: The main unitís header for the example program

#ifndef MainUH
#define MainUH

#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Dialogs.hpp>

class TMainForm : public TForm
{
__published:    // IDE-managed Components
  TButton *WriteBtn;
  TButton *ReadBtn;
  TLabel *Label1;
  TFontDialog *FontDialog1;
  void __fastcall WriteBtnClick(TObject *Sender);
  void __fastcall ReadBtnClick(TObject *Sender);
private:    // User declarations
public:        // User declarations
  __fastcall TMainForm(TComponent* Owner);
};

extern PACKAGE TMainForm *MainForm;

#endif

Listing B: The main unit for the example program

#include <vcl.h>
#pragma hdrstop

#include <Registry.hpp>
#include "MainU.h"

#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;

__fastcall TMainForm::TMainForm(TComponent* Owner)
  : TForm(Owner)
{
}

void __fastcall
TMainForm::WriteBtnClick(TObject *Sender)
{
  // Show the Font dialog.
  if (FontDialog1->Execute()) {
    LOGFONT lf;
    // Create a TRegistry object.
    TRegistry* reg = new TRegistry;
    try {
      // Open a key in the registry. Create
      // the key if it doesn't yet exist.
      reg->OpenKey("\\software\\test", true);
      // Put the font data in a LOGFONT structure.
      GetObject(FontDialog1->Font->Handle,
        sizeof(lf), &lf);
      // Write the data to the registry.
      reg->WriteBinaryData(
        "font", &lf, sizeof(lf));
      ShowMessage("Registry key created.");
    }
    __finally {
      // Clean up.
      delete reg;
    }
  }
}

void __fastcall 
TMainForm::ReadBtnClick(TObject *Sender)
{
  LOGFONT lf;
  // Create a TRegistry object.
  TRegistry* reg = new TRegistry;
  try {
    // Open the key created earlier.
    reg->OpenKey("\\software\\test", false);
    // Read the data directly into the
    // LOGFONT variable, lf.
    reg->ReadBinaryData("font", &lf, sizeof(lf));
    // Create a font from the LOGFONT data and
    // assign it to the label's Handle property.
    Label1->Font->Handle = CreateFontIndirect(&lf);
  }
  __finally {
    // Clean up.
    delete reg;
  }
}