Click here to Skip to main content
15,897,151 members
Articles / Programming Languages / C#

HtmlHelp library and example viewer

Rate me:
Please Sign up or sign in to vote.
4.90/5 (65 votes)
11 Aug 2004CPOL26 min read 478.8K   15.5K   231  
A class library for reading compiled HTML help (chm) files and a sample viewer application using this library.
using System;
using System.IO;
using System.Collections;
using System.Collections.Specialized;

namespace HtmlHelp.ChmDecoding
{
	/// <summary>
	/// The class <c>CHMTopics</c> implements functionality to decode the #TOPICS internal file
	/// </summary>
	public sealed class CHMTopics : IDisposable
	{
		/// <summary>
		/// Internal flag specifying if the object is going to be disposed
		/// </summary>
		private bool disposed = false;
		/// <summary>
		/// Internal member storing the binary file data
		/// </summary>
		private byte[] _binaryFileData = null;
		/// <summary>
		/// Internal member storing the associated chmfile object
		/// </summary>
		private CHMFile _associatedFile = null;
		/// <summary>
		/// Internal member storing the topic list
		/// </summary>
		private ArrayList _topicTable = new ArrayList();

		/// <summary>
		/// Constructor of the class
		/// </summary>
		/// <param name="binaryFileData">binary file data of the #TOPICS file</param>
		/// <param name="associatedFile">associated chm file</param>
		public CHMTopics(byte[] binaryFileData, CHMFile associatedFile)
		{
			_binaryFileData = binaryFileData;
			_associatedFile = associatedFile;
			DecodeData();

			// clear internal binary data after extraction
			_binaryFileData = null;
		}

		
		/// <summary>
		/// Standard constructor
		/// </summary>
		internal CHMTopics()
		{
		}

		#region Data dumping
		/// <summary>
		/// Dump the class data to a binary writer
		/// </summary>
		/// <param name="writer">writer to write the data</param>
		internal void Dump(ref BinaryWriter writer)
		{
			writer.Write( _topicTable.Count );
			foreach(TopicEntry curItem in _topicTable)
			{
				curItem.Dump(ref writer);
			}
		}

		/// <summary>
		/// Reads the object data from a dump store
		/// </summary>
		/// <param name="reader">reader to read the data</param>
		internal void ReadDump(ref BinaryReader reader)
		{
			int i=0;
			int nCnt = reader.ReadInt32();

			for(i=0; i<nCnt;i++)
			{
				TopicEntry newItem = new TopicEntry();
				newItem.SetCHMFile(_associatedFile);
				newItem.ReadDump(ref reader);
				_topicTable.Add(newItem);
			}
		}

		/// <summary>
		/// Sets the associated CHMFile instance
		/// </summary>
		/// <param name="associatedFile">instance to set</param>
		internal void SetCHMFile(CHMFile associatedFile)
		{
			_associatedFile = associatedFile;

			foreach(TopicEntry curEntry in _topicTable)
			{
				curEntry.SetCHMFile(associatedFile);
			}
		}
		#endregion

		/// <summary>
		/// Decodes the binary file data and fills the internal properties
		/// </summary>
		/// <returns>true if succeeded</returns>
		private bool DecodeData()
		{
			bool bRet = true;

			MemoryStream memStream = new MemoryStream(_binaryFileData);
			BinaryReader binReader = new BinaryReader(memStream);
			
			int nCurOffset = 0;

			while( (memStream.Position < memStream.Length) && (bRet) )
			{
				int entryOffset = nCurOffset;
				int tocIdx = binReader.ReadInt32();
				int titleOffset = binReader.ReadInt32();
				int urltablOffset = binReader.ReadInt32();
				int visibilityMode = binReader.ReadInt16();
				int unknownMode = binReader.ReadInt16();

				TopicEntry newEntry = new TopicEntry(entryOffset, tocIdx, titleOffset, urltablOffset, visibilityMode, unknownMode, _associatedFile);
				_topicTable.Add( newEntry );

				nCurOffset = (int)memStream.Position;
			}

			return bRet;
		}

		/// <summary>
		/// Gets the arraylist containing all topic entries.
		/// </summary>
		public ArrayList TopicTable
		{
			get
			{
				return _topicTable;
			}
		}

		/// <summary>
		/// Gets the topic entry of a given offset
		/// </summary>
		public TopicEntry this[int offset]
		{
			get
			{
				foreach(TopicEntry curEntry in _topicTable)
					if(curEntry.EntryOffset == offset)
						return curEntry;

				return null;
			}
		}

		/// <summary>
		/// Searches a topic by the locale name
		/// </summary>
		/// <param name="locale">locale name to search</param>
		/// <returns>The topicentry instance if found, otherwise null</returns>
		public TopicEntry GetByLocale(string locale)
		{
			foreach(TopicEntry curEntry in TopicTable)
			{
				if(curEntry.Locale.ToLower() == locale.ToLower())
					return curEntry;
			}

			return null;
		}

		/// <summary>
		/// Searches the topics for all files with a given file extension
		/// </summary>
		/// <param name="fileExtension">extension to search</param>
		/// <returns>An arraylist of TopicEntry instances or null if no topic was found</returns>
		public ArrayList GetByExtension(string fileExtension)
		{
			ArrayList arrRet = new ArrayList();

			foreach(TopicEntry curEntry in TopicTable)
			{
				if(curEntry.Locale.ToLower().EndsWith(fileExtension.ToLower()))
					arrRet.Add(curEntry);
			}

			if(arrRet.Count > 0)
				return arrRet;

			return null;
		}

		/// <summary>
		/// Implement IDisposable.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			// This object will be cleaned up by the Dispose method.
			// Therefore, you should call GC.SupressFinalize to
			// take this object off the finalization queue 
			// and prevent finalization code for this object
			// from executing a second time.
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Dispose(bool disposing) executes in two distinct scenarios. 
		/// If disposing equals true, the method has been called directly 
		/// or indirectly by a user's code. Managed and unmanaged resources 
		/// can be disposed. 
		/// If disposing equals false, the method has been called by the 
		/// runtime from inside the finalizer and you should not reference  
		/// other objects. Only unmanaged resources can be disposed.
		/// </summary>
		/// <param name="disposing">disposing flag</param>
		private void Dispose(bool disposing)
		{
			// Check to see if Dispose has already been called.
			if(!this.disposed)
			{
				// If disposing equals true, dispose all managed 
				// and unmanaged resources.
				if(disposing)
				{
					// Dispose managed resources.
					_binaryFileData = null;
					_topicTable=null;
				}
			}
			disposed = true;         
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions