David Brown

The Web Development Guy

  Home  |   Contact  |   Syndication    |   Login
  6 Posts | 0 Stories | 17 Comments | 0 Trackbacks

News

View David Brown's profile on LinkedIn

Twitter












Tag Cloud


Archives

Monday, June 01, 2009 #

I just wanted to fill everyone in on the status of the alpha version of my .NET Btrieve Wrapper that I promised to release a long time ago. Job issues have forced me to focus my time more on finding a way to pay bills. Don't worry, I'm not going to waste your time by complaining about the economy or anything like that.

Basically, the wrapper hasn't changed since I first blogged about it and I don't think I'll be working on projects that need it. Additionally, I'm probably not the most qualified person to work on something like that, as I've only been using Btrieve for the last year or so.

The bottom line is: I can't guarantee that the wrapper will ever see the light of day and I apologize to those of you that were hoping to mess with it. Fear not, however! If you absolutely need to see the code, shoot me an email and I'll see what I can do.

Monday, April 06, 2009 #

For my current project, I've implemented a Shared AssemblyInfo.cs file, mostly because every project is from the same company and share some common version numbers. In one of my console applications, I needed to construct a header using the Assembly Title and Copyright information from my SharedAssemblyInfo.cs file. To achieve this, I added the following to the end of SharedAssemblyInfo.cs:

namespace MyNamespace {
    public static class AssemblyInfo {
        public static string Title {
            get {
                var attr = GetAssemblyAttribute<AssemblyTitleAttribute>();
                if (attr != null)
                    return attr.Title;
                return string.Empty;
            }
        }

        public static string Company {
            get {
                var attr = GetAssemblyAttribute<AssemblyCompanyAttribute>();
                if (attr != null)
                    return attr.Company;
                return string.Empty;
            }
        }

        public static string Copyright {
            get {
                var attr = GetAssemblyAttribute<AssemblyCopyrightAttribute>();
                if (attr != null)
                    return attr.Copyright;
                return string.Empty;
            }
        }

        private static T GetAssemblyAttribute<T>() where T : Attribute {
            object[] attributes = Assembly.GetExecutingAssembly()
                .GetCustomAttributes(typeof(T), true);
            if (attributes == null || attributes.Length == 0) return null;
            return (T)attributes[0];
        }
    }
}

Now, I can use it like this:

Console.WriteLine(AssemblyInfo.Title);
Console.WriteLine(AssemblyInfo.Copyright);

The best part is that since SharedAssemblyInfo.cs is linked to each of my projects, I can use the AssemblyInfo class wherever I want!

Also, the class is reading attributes from the assembly that's currently executing, which means that if you call AssemblyInfo.Title in Project 1, it will return the value of Project 1's AssemblyTitleAttribute, while Project 2 will return its own value. If you have defined the assembly title in a linked file, however, both Project 1 and Project 2 will return that value.


Sunday, April 05, 2009 #

Here's an extension method that packs a C# BitArray to a byte array:

public static byte[] ToByteArray(this BitArray bits) {
    int numBytes = bits.Count / 8;
    if (bits.Count % 8 != 0) numBytes++;

    byte[] bytes = new byte[numBytes];
    int byteIndex = 0, bitIndex = 0;

    for (int i = 0; i < bits.Count; i++) {
        if (bits[i])
            bytes[byteIndex] |= (byte)(1 << (7 - bitIndex));

        bitIndex++;
        if (bitIndex == 8) {
            bitIndex = 0;
            byteIndex++;
        }
    }

    return bytes;
}

Special thanks to the good people at StackOverflow! Enjoy!


Friday, March 20, 2009 #

I decided to use DocBook to generate documentation for SalesWeb, but I was finding it difficult to remember which tags I could use. Since DocBook is XML-based, it has a schema that can be plugged into Visual Studio 2005/2008 to enable intellisense.

I’ve put together a tiny ZIP file that has everything you need to get started:

DocBook 4.5 Intellisense for Visual Studio

It contains a VS catalog file and the DocBook 4.5 schemas (which I hope are redistributable… if not, let me know and I’ll remove them). Simply extract the zip file to one of the following directories, depending on your version of Visual Studio:

Visual Studio 2005 (8.0)

C:\Program Files\Visual Studio 8\Xml\Schemas

Visual Studio 2008 (9.0)

C:\Program Files\Visual Studio 9.0\Xml\Schemas

DocBook intellisense will be enabled for files with the .docbook extension. If your solution contains DocBook files, you can simply right-click and choose “Open With…” then select “XML Editor”. In my case, however, my Documentation is outside of my solution, so I had to do the following:

  1. Click Tools –> Options
  2. Choose the Text Editor –> File Associations page
  3. Enter “docbook” as the extension
  4. Choose “XML Editor” as the editor
  5. Click Add
  6. Click Ok

Let me know if you run into any problems!

Technorati Tags: ,,

Saturday, March 14, 2009 #

Btrieve is

a transactional database product based on Indexed Sequential Access Method (ISAM), which is a way of storing data for fast retrieval.

It's also the backend for Pervasive Software's Pervasive.SQL product, which is a combination of the Micro-Kernel Database Engine and Scalable SQL relational database. At work, our Service Business Management Suite stores data in a Pervasive.SQL database, which is actually a collection of Btrieve data files and the DDFs (Data Definition Files) that describe their layout.

If you're a Btrieve user, then all of that made perfect sense.

Admittedly, I was only introduced to Btrieve and Pervasive.SQL eight months ago, so I don't claim to be an expert by any means. But, after using it for a while, I've begun to see how interesting Btrieve actually is. It's incredibly easy to use when interfacing via Pervasive's ADO.NET provider, but I wanted to bypass the relational engine and use the transactional engine instead (cause I like to make things difficult for myself).

The only .NET wrapper for the transactional Btrieve API that I could find was Btrieve Classes for .NET from TechKnowledge. It looks cool, but I don't really want to spend that kind of cash. So, I decided to write my own, which wasn't as difficult as I thought it would be.

I might upload a very, very, very early alpha version soon, but until then, here's a little sneak peek at what my Btrieve .NET library looks like.

At the moment, there are no classes to read DDFs, mostly because I can't find any documentation of their structure. If anyone has any information about that, please let me know in the comments. Anyway, until a DDF reader is implemented, you have to define record layouts in code. Even after a DDF reader is added, though, manual record layout construction will still be possible. Here's an example of a simple record:

public static class Note {
    public static BtrieveRecordDefinition Record = BtrieveRecordDefinition { 
        Fields = new BtrieveFieldDefinitionList {
            new BtrieveFieldDefinition("ID", 0, BtrieveDataTypes.INTEGER),
            new BtrieveFieldDefinition("BODY", 4, 255, BtrieveDataTypes.CHAR)
        }
    }
}

This record contains two fields: ID and BODY. ID begins at offset 0 of the data buffer and is an INTEGER data type. BODY begins at offset 4, is up to 255 bytes in length, and is a CHAR data type. Now, how do we actually get some data?

Btrieve data files are read using the BtrieveFile class. Here's a quick example:

BtrieveFile notesFile = BtrieveFile.Open(@"C:\Data\NOTES.DAT", Note.Record);

As you can see, we're opening the Btrieve data file containing notes and telling it to read the records according to the record definition that we defined in our static Note class. The BtrieveFile class is enumerable, so reading records is incredibly easy:

foreach (BtrieveRecord note in notesFile) {
    Console.WriteLine(note.Get<string>("BODY"));
}

You can also navigate through records manually:

notesFile.MoveNext();
notesFile.MovePrevious();
notesFile.MoveFirst();
notesFile.MoveLast();

Btrieve data types are implemented as classes derived from the abstract BtrieveDataType class. Each data type class handles conversion from data in the buffer to the appropriate managed data type. For example, the BtrieveCharType class converts the value of CHAR fields to a .NET string and back again. A single static class called BtrieveDataTypes (notice the "s" on the end) contains static instances of each common data type, so you don't have to create a new instance for every field definition. Some data types, such as INTEGER are fixed-length. Others, such as CHAR, are variable-length. If you attempt to assign a variable-length data type to a field without specifying it's Size parameter, you will get an exception.

You can also create your own data types, if you need to convert a value in your records to a custom class. All you have to do is derive from BtrieveDataType and tell the wrapper how to read and convert your data from the buffer. Then just specify your custom class in your field definition.

I've been talking about the data buffer, but I haven't actually explained what it is. The Btrieve API passes data to applications via a data buffer, which is an allocated block of memory. A class called BtrieveDataBuffer handles the allocation of memory and the conversion of common types to and from the buffer. It's unlikely that you will need to create your own buffer, but you may need to interact with it directly (for example, when creating a custom Btrieve data type).

BtrieveDataBuffer buffer = new BtrieveDataBuffer(255);
string helloWorld = "Hello, world";
buffer.WriteString(helloWorld, 0);
Console.WriteLine(buffer.ReadString(0, helloWorld.Length));

Above, we created a new 255 byte buffer, wrote the strong "Hello, world" to offset 0, and read it back again. It's important to point out that both BtrieveFile and BtrieveDataBuffer implement IDisposable. Both classed will handle the freeing of memory and the closing of Btrieve files automatically during garbage collection. This also means that both can be used in "using" blocks.

Certain data types may require a little bit of configuration on your part. For example, the LOGICAL data type doesn't define a specific value for true and false. It's up to your application to define them (it's an annoying Btrieve quirk, but I suppose it could be useful in some cases). By default, my wrapper defines true as 1 and false as 0. You can change these values like this:

BtrieveLogicalType.TrueValue = "Y";
BtrieveLogicalType.FalseValue = "N";

Now when reading a LOGICAL field, the wrapper will convert "Y" to a .NET boolean with a value of true and "N" to a .NET boolean with a value of false. If it encounters a LOGICAL field whose value does not match either of those values, an exception will be thrown. You can, however, specify that you want all unknown values automatically converted to false:

BtrieveLogicalType.ExceptionOnValueMismatch = false;

If you really want to get your hands dirty, you can call up Btrieve operations yourself using a handy little wrapper method around the BTRCALL Win32 function called BtrieveAPI.Call(). This method does some extra things to make your life a little easier and also raises a BtrieveException when the returned status code is anything but NO_ERROR.

Anyway, that's just a little sneak peek at what you will be able to do. Things will likely change in the future, but I think this is a good starting point, at least. For my closing act, here's a list of features implemented and not implemented...

Implemented

  • Open and close Btrieve data files
  • Define records using BtrieveRecordDefinition and BtrieveFieldDefinition
  • Enumerate records or move through them manually using MoveNext, MovePrevious, MoveFirst, and MoveLast
  • Read and write data in an unmanaged Btrieve data buffer
  • Data Types: CHAR, INTEGER, LOGICAL, DATE
  • Define your own custom Btrieve data types
  • Retrieve client/server version information

Not Implemented

  • Floating-point data types and others not mentioned in the Implemented section
  • Insert and update records
  • Generate record definitions from DDFs
  • Null values
  • Index/key information retrieval
  • Extended operations
  • Chunk operations
  • and a bunch of other stuff that I probably missed

If there are any major Btrieve geeks out there who would be interested in giving some pointers from time to time, please let me know. I have to admit that data type conversion is getting pretty complicated.

Thanks for reading!


Sunday, May 11, 2008 #

I don't like messing with DVDs and their cases when I want to watch a movie, so I went and bought a 250 GB Maxtor OneTouch 4 external hard drive. Over the next few days, I plan to rip my DVD collection to it so I can watch them on Windows Media Center via my Xbox 360. As a test, I ripped "Leon The Professional" to my new drive and attempted to play it in WMC.

The movie showed up in my DVD Library just fine.

However, when trying to play it, I got the following message:

DVD Error:
Windows Media Center cannot play this DVD. Please Restart Windows Media Center and insert the DVD again.

To troubleshoot, I attempted to play one of the VOB files in Windows Media Player. However, I was met with this:

Windows Media Player cannot play the DVD because the disc prohibits playback in your region of the world. You must obtain a disc that is intended for your geographic region.

That's odd. It was ripped from an NTSC disk, so I should be able to play it. The solution?

Well, it's not very obvious. First, I removed the drive from my list of watched folders. Then, I shared the external hard drive to the network. I then re-added the drive to WMC, but this time, I referenced it on the network instead of on my local computer, like this:

Viola! "Leon The Professional" plays perfectly now!