Sunday, February 3, 2008

More questions

Q. How is AI research done?
A. AI research has both theoretical and experimental sides. The experimental side has both basic and applied aspects.
There are two main lines of research. One is biological, based on the idea that since humans are intelligent, AI should study humans and imitate their psychology or physiology. The other is phenomenal, based on studying and formalizing common sense facts about the world and the problems that the world presents to the achievement of goals. The two approaches interact to some extent, and both should eventually succeed. It is a race, but both racers seem to be walking.
Q. What are the relations between AI and philosophy?
A. AI has many relations with philosophy, especially modern analytic philosophy. Both study mind, and both study common sense. The best reference is [Tho03].
Q. How are AI and logic programming related?
A. At the very least, logic programming provides useful programming languages (mainly Prolog).
Beyond that, sometimes a theory useful in AI can be expressed as a collection of Horn clauses, and goal to be achieved can be expressed as that of finding values of variables satisfying an expression . The problem can sometimes be solved by running the Prolog program consisting of and .
There are two possible obstacles to regarding AI as logic programming. First, Horn theories do not exhaust first order logic. Second, the Prolog program expressing the theory may be extremely inefficient. More elaborate control than just executing the program that expresses the theory is often needed. Map coloring provides examples.
Q. What should I study before or while learning AI?
A. Study mathematics, especially mathematical logic. The more you learn about sciences, e.g. physics or biology, the better. For the biological approaches to AI, study psychology and the physiology of the nervous system. Learn some programming languages--at least C, Lisp and Prolog. It is also a good idea to learn one basic machine language. Jobs are likely to depend on knowing the languages currently in fashion. In the late 1990s, these include C++ and Java.
Q. What is a good textbook on AI?
A. Artificial Intelligence by Stuart Russell and Peter Norvig, Prentice Hall is the most commonly used textbbook in 1997. The general views expressed there do not exactly correspond to those of this essay. Artificial Intelligence: A New Synthesis by Nils Nilsson, Morgan Kaufman, may be easier to read. Some people prefer Computational Intelligence by David Poole, Alan Mackworth and Randy Goebel, Oxford, 1998.
Q. What organizations and publications are concerned with AI?
A. The American Association for Artificial Intelligence (AAAI), the European Coordinating Committee for Artificial Intelligence (ECCAI) and the Society for Artificial Intelligence and Simulation of Behavior (AISB) are scientific societies concerned with AI research. The Association for Computing Machinery (ACM) has a special interest group on artificial intelligence SIGART.
The International Joint Conference on AI (IJCAI) is the main international conference. The AAAI runs a US National Conference on AI. Electronic Transactions on Artificial Intelligence, Artificial Intelligence, and Journal of Artificial Intelligence Research, and IEEE Transactions on Pattern Analysis and Machine Intelligence are four of the main journals publishing AI research papers. I have not yet found everything that should be in this paragraph.

Applications of AI

Q. What are the applications of AI?
A. Here are some.
game playing
You can buy machines that can play master level chess for a few hundred dollars. There is some AI in them, but they play well against people mainly through brute force computation--looking at hundreds of thousands of positions. To beat a world champion by brute force and known reliable heuristics requires being able to look at 200 million positions per second.
speech recognition
In the 1990s, computer speech recognition reached a practical level for limited purposes. Thus United Airlines has replaced its keyboard tree for flight information by a system using speech recognition of flight numbers and city names. It is quite convenient. On the the other hand, while it is possible to instruct some computers using speech, most users have gone back to the keyboard and the mouse as still more convenient.
understanding natural language
Just getting a sequence of words into a computer is not enough. Parsing sentences is not enough either. The computer has to be provided with an understanding of the domain the text is about, and this is presently possible only for very limited domains.
computer vision
The world is composed of three-dimensional objects, but the inputs to the human eye and computers' TV cameras are two dimensional. Some useful programs can work solely in two dimensions, but full computer vision requires partial three-dimensional information that is not just a set of two-dimensional views. At present there are only limited ways of representing three-dimensional information directly, and they are not as good as what humans evidently use.
expert systems
A ``knowledge engineer'' interviews experts in a certain domain and tries to embody their knowledge in a computer program for carrying out some task. How well this works depends on whether the intellectual mechanisms required for the task are within the present state of AI. When this turned out not to be so, there were many disappointing results. One of the first expert systems was MYCIN in 1974, which diagnosed bacterial infections of the blood and suggested treatments. It did better than medical students or practicing doctors, provided its limitations were observed. Namely, its ontology included bacteria, symptoms, and treatments and did not include patients, doctors, hospitals, death, recovery, and events occurring in time. Its interactions depended on a single patient being considered. Since the experts consulted by the knowledge engineers knew about patients, doctors, death, recovery, etc., it is clear that the knowledge engineers forced what the experts told them into a predetermined framework. In the present state of AI, this has to be true. The usefulness of current expert systems depends on their users having common sense.
heuristic classification
One of the most feasible kinds of expert system given the present knowledge of AI is to put some information in one of a fixed set of categories using several sources of information. An example is advising whether to accept a proposed credit card purchase. Information is available about the owner of the credit card, his record of payment and also about the item he is buying and about the establishment from which he is buying it (e.g., about whether there have been previous credit card frauds at this establishment).

Branches of AI

Q. What are the branches of AI?
A. Here's a list, but some branches are surely missing, because no-one has identified them yet. Some of these may be regarded as concepts or topics rather than full branches.
logical AI
What a program knows about the world in general the facts of the specific situation in which it must act, and its goals are all represented by sentences of some mathematical logical language. The program decides what to do by inferring that certain actions are appropriate for achieving its goals. The first article proposing this was [McC59]. [McC89] is a more recent summary. [McC96b] lists some of the concepts involved in logical aI. [Sha97] is an important text.
search
AI programs often examine large numbers of possibilities, e.g. moves in a chess game or inferences by a theorem proving program. Discoveries are continually made about how to do this more efficiently in various domains.
pattern recognition
When a program makes observations of some kind, it is often programmed to compare what it sees with a pattern. For example, a vision program may try to match a pattern of eyes and a nose in a scene in order to find a face. More complex patterns, e.g. in a natural language text, in a chess position, or in the history of some event are also studied. These more complex patterns require quite different methods than do the simple patterns that have been studied the most.
representation
Facts about the world have to be represented in some way. Usually languages of mathematical logic are used.
inference
From some facts, others can be inferred. Mathematical logical deduction is adequate for some purposes, but new methods of non-monotonic inference have been added to logic since the 1970s. The simplest kind of non-monotonic reasoning is default reasoning in which a conclusion is to be inferred by default, but the conclusion can be withdrawn if there is evidence to the contrary. For example, when we hear of a bird, we man infer that it can fly, but this conclusion can be reversed when we hear that it is a penguin. It is the possibility that a conclusion may have to be withdrawn that constitutes the non-monotonic character of the reasoning. Ordinary logical reasoning is monotonic in that the set of conclusions that can the drawn from a set of premises is a monotonic increasing function of the premises. Circumscription is another form of non-monotonic reasoning.
common sense knowledge and reasoning
This is the area in which AI is farthest from human-level, in spite of the fact that it has been an active research area since the 1950s. While there has been considerable progress, e.g. in developing systems of non-monotonic reasoning and theories of action, yet more new ideas are needed. The Cyc system contains a large but spotty collection of common sense facts.
learning from experience
Programs do that. The approaches to AI based on connectionism and neural nets specialize in that. There is also learning of laws expressed in logic. [Mit97] is a comprehensive undergraduate text on machine learning. Programs can only learn what facts or behaviors their formalisms can represent, and unfortunately learning systems are almost all based on very limited abilities to represent information.
planning
Planning programs start with general facts about the world (especially facts about the effects of actions), facts about the particular situation and a statement of a goal. From these, they generate a strategy for achieving the goal. In the most common cases, the strategy is just a sequence of actions.
epistemology
This is a study of the kinds of knowledge that are required for solving problems in the world.
ontology
Ontology is the study of the kinds of things that exist. In AI, the programs and sentences deal with various kinds of objects, and we study what these kinds are and what their basic properties are. Emphasis on ontology begins in the 1990s.
heuristics
A heuristic is a way of trying to discover something or an idea imbedded in a program. The term is used variously in AI. Heuristic functions are used in some approaches to search to measure how far a node in a search tree seems to be from a goal. Heuristic predicates that compare two nodes in a search tree to see if one is better than the other, i.e. constitutes an advance toward the goal, may be more useful. [My opinion].
genetic programming
Genetic programming is a technique for getting programs to solve a task by mating random Lisp programs and selecting fittest in millions of generations. It is being developed by John Koza's group and here's a tutorial.

Basic Questions about Artificial Intelligence

Q. What is artificial intelligence?
A. It is the science and engineering of making intelligent machines, especially intelligent computer programs. It is related to the similar task of using computers to understand human intelligence, but AI does not have to confine itself to methods that are biologically observable.
Q. Yes, but what is intelligence?
A. Intelligence is the computational part of the ability to achieve goals in the world. Varying kinds and degrees of intelligence occur in people, many animals and some machines.
Q. Isn't there a solid definition of intelligence that doesn't depend on relating it to human intelligence?
A. Not yet. The problem is that we cannot yet characterize in general what kinds of computational procedures we want to call intelligent. We understand some of the mechanisms of intelligence and not others.
Q. Is intelligence a single thing so that one can ask a yes or no question ``Is this machine intelligent or not?''?
A. No. Intelligence involves mechanisms, and AI research has discovered how to make computers carry out some of them and not others. If doing a task requires only mechanisms that are well understood today, computer programs can give very impressive performances on these tasks. Such programs should be considered ``somewhat intelligent''.
Q. Isn't AI about simulating human intelligence?
A. Sometimes but not always or even usually. On the one hand, we can learn something about how to make machines solve problems by observing other people or just by observing our own methods. On the other hand, most work in AI involves studying the problems the world presents to intelligence rather than studying people or animals. AI researchers are free to use methods that are not observed in people or that involve much more computing than people can do.
Q. What about IQ? Do computer programs have IQs?
A. No. IQ is based on the rates at which intelligence develops in children. It is the ratio of the age at which a child normally makes a certain score to the child's age. The scale is extended to adults in a suitable way. IQ correlates well with various measures of success or failure in life, but making computers that can score high on IQ tests would be weakly correlated with their usefulness. For example, the ability of a child to repeat back a long sequence of digits correlates well with other intellectual abilities, perhaps because it measures how much information the child can compute with at once. However, ``digit span'' is trivial for even extremely limited computers.
However, some of the problems on IQ tests are useful challenges for AI.
Q. What about other comparisons between human and computer intelligence?
Arthur R. Jensen [Jen98], a leading researcher in human intelligence, suggests ``as a heuristic hypothesis'' that all normal humans have the same intellectual mechanisms and that differences in intelligence are related to ``quantitative biochemical and physiological conditions''. I see them as speed, short term memory, and the ability to form accurate and retrievable long term memories.
Whether or not Jensen is right about human intelligence, the situation in AI today is the reverse.
Computer programs have plenty of speed and memory but their abilities correspond to the intellectual mechanisms that program designers understand well enough to put in programs. Some abilities that children normally don't develop till they are teenagers may be in, and some abilities possessed by two year olds are still out. The matter is further complicated by the fact that the cognitive sciences still have not succeeded in determining exactly what the human abilities are. Very likely the organization of the intellectual mechanisms for AI can usefully be different from that in people.
Whenever people do better than computers on some task or computers use a lot of computation to do as well as people, this demonstrates that the program designers lack understanding of the intellectual mechanisms required to do the task efficiently.
Q. When did AI research start?
A. After WWII, a number of people independently started to work on intelligent machines. The English mathematician Alan Turing may have been the first. He gave a lecture on it in 1947. He also may have been the first to decide that AI was best researched by programming computers rather than by building machines. By the late 1950s, there were many researchers on AI, and most of them were basing their work on programming computers.
Q. Does AI aim to put the human mind into the computer?
A. Some researchers say they have that objective, but maybe they are using the phrase metaphorically. The human mind has a lot of peculiarities, and I'm not sure anyone is serious about imitating all of them.
Q. What is the Turing test?
A. Alan Turing's 1950 article Computing Machinery and Intelligence [Tur50] discussed conditions for considering a machine to be intelligent. He argued that if the machine could successfully pretend to be human to a knowledgeable observer then you certainly should consider it intelligent. This test would satisfy most people but not all philosophers. The observer could interact with the machine and a human by teletype (to avoid requiring that the machine imitate the appearance or voice of the person), and the human would try to persuade the observer that it was human and the machine would try to fool the observer.
The Turing test is a one-sided test. A machine that passes the test should certainly be considered intelligent, but a machine could still be considered intelligent without knowing enough about humans to imitate a human.
Daniel Dennett's book Brainchildren [Den98] has an excellent discussion of the Turing test and the various partial Turing tests that have been implemented, i.e. with restrictions on the observer's knowledge of AI and the subject matter of questioning. It turns out that some people are easily led into believing that a rather dumb program is intelligent.
Q. Does AI aim at human-level intelligence?
A. Yes. The ultimate effort is to make computer programs that can solve problems and achieve goals in the world as well as humans. However, many people involved in particular research areas are much less ambitious.
Q. How far is AI from reaching human-level intelligence? When will it happen?
A. A few people think that human-level intelligence can be achieved by writing large numbers of programs of the kind people are now writing and assembling vast knowledge bases of facts in the languages now used for expressing knowledge.
However, most AI researchers believe that new fundamental ideas are required, and therefore it cannot be predicted when human-level intelligence will be achieved.
Q. Are computers the right kind of machine to be made intelligent?
A. Computers can be programmed to simulate any kind of machine.
Many researchers invented non-computer machines, hoping that they would be intelligent in different ways than the computer programs could be. However, they usually simulate their invented machines on a computer and come to doubt that the new machine is worth building. Because many billions of dollars that have been spent in making computers faster and faster, another kind of machine would have to be very fast to perform better than a program on a computer simulating the machine.
Q. Are computers fast enough to be intelligent?
A. Some people think much faster computers are required as well as new ideas. My own opinion is that the computers of 30 years ago were fast enough if only we knew how to program them. Of course, quite apart from the ambitions of AI researchers, computers will keep getting faster.
Q. What about parallel machines?
A. Machines with many processors are much faster than single processors can be. Parallelism itself presents no advantages, and parallel machines are somewhat awkward to program. When extreme speed is required, it is necessary to face this awkwardness.
Q. What about making a ``child machine'' that could improve by reading and by learning from experience?
A. This idea has been proposed many times, starting in the 1940s. Eventually, it will be made to work. However, AI programs haven't yet reached the level of being able to learn much of what a child learns from physical experience. Nor do present programs understand language well enough to learn much by reading.
Q. Might an AI system be able to bootstrap itself to higher and higher level intelligence by thinking about AI?
A. I think yes, but we aren't yet at a level of AI at which this process can begin.
Q. What about chess?
A. Alexander Kronrod, a Russian AI researcher, said ``Chess is the Drosophila of AI.'' He was making an analogy with geneticists' use of that fruit fly to study inheritance. Playing chess requires certain intellectual mechanisms and not others. Chess programs now play at grandmaster level, but they do it with limited intellectual mechanisms compared to those used by a human chess player, substituting large amounts of computation for understanding. Once we understand these mechanisms better, we can build human-level chess programs that do far less computation than do present programs.
Unfortunately, the competitive and commercial aspects of making computers play chess have taken precedence over using chess as a scientific domain. It is as if the geneticists after 1910 had organized fruit fly races and concentrated their efforts on breeding fruit flies that could win these races.
Q. What about Go?
A. The Chinese and Japanese game of Go is also a board game in which the players take turns moving. Go exposes the weakness of our present understanding of the intellectual mechanisms involved in human game playing. Go programs are very bad players, in spite of considerable effort (not as much as for chess). The problem seems to be that a position in Go has to be divided mentally into a collection of subpositions which are first analyzed separately followed by an analysis of their interaction. Humans use this in chess also, but chess programs consider the position as a whole. Chess programs compensate for the lack of this intellectual mechanism by doing thousands or, in the case of Deep Blue, many millions of times as much computation.
Sooner or later, AI research will overcome this scandalous weakness.
Q. Don't some people say that AI is a bad idea?
A. The philosopher John Searle says that the idea of a non-biological machine being intelligent is incoherent. He proposes the Chinese room argument www-formal.stanford.edu/jmc/chinese.html The philosopher Hubert Dreyfus says that AI is impossible. The computer scientist Joseph Weizenbaum says the idea is obscene, anti-human and immoral. Various people have said that since artificial intelligence hasn't reached human level by now, it must be impossible. Still other people are disappointed that companies they invested in went bankrupt.
Q. Aren't computability theory and computational complexity the keys to AI? [Note to the layman and beginners in computer science: These are quite technical branches of mathematical logic and computer science, and the answer to the question has to be somewhat technical.]
A. No. These theories are relevant but don't address the fundamental problems of AI.
In the 1930s mathematical logicians, especially Kurt Gödel and Alan Turing, established that there did not exist algorithms that were guaranteed to solve all problems in certain important mathematical domains. Whether a sentence of first order logic is a theorem is one example, and whether a polynomial equations in several variables has integer solutions is another. Humans solve problems in these domains all the time, and this has been offered as an argument (usually with some decorations) that computers are intrinsically incapable of doing what people do. Roger Penrose claims this. However, people can't guarantee to solve arbitrary problems in these domains either. See my Review of The Emperor's New Mind by Roger Penrose. More essays and reviews defending AI research are in [McC96a].
In the 1960s computer scientists, especially Steve Cook and Richard Karp developed the theory of NP-complete problem domains. Problems in these domains are solvable, but seem to take time exponential in the size of the problem. Which sentences of propositional calculus are satisfiable is a basic example of an NP-complete problem domain. Humans often solve problems in NP-complete domains in times much shorter than is guaranteed by the general algorithms, but can't solve them quickly in general.
What is important for AI is to have algorithms as capable as people at solving problems. The identification of subdomains for which good algorithms exist is important, but a lot of AI problem solvers are not associated with readily identified subdomains.
The theory of the difficulty of general classes of problems is called computational complexity. So far this theory hasn't interacted with AI as much as might have been hoped. Success in problem solving by humans and by AI programs seems to rely on properties of problems and problem solving methods that the neither the complexity researchers nor the AI community have been able to identify precisely.
Algorithmic complexity theory as developed by Solomonoff, Kolmogorov and Chaitin (independently of one another) is also relevant. It defines the complexity of a symbolic object as the length of the shortest program that will generate it. Proving that a candidate program is the shortest or close to the shortest is an unsolvable problem, but representing objects by short programs that generate them should sometimes be illuminating even when you can't prove that the program is the shortest.

Friday, January 11, 2008

eDirectory Authentication using LdapConnection and custom certificate validation

Introduction
This article explains how to authenticate a user over LDAPS using the System.DirectoryServices.Protocols.LdapConnection class, performing custom certificate validation.

Background
Recently, I ran into trouble using System.DirectoryServices.DirectoryEntry to connect to a Novell eDirectory server because the certificate was self-signed. When run in an ASP.NET application, the machine-level certificate store was not examined. So, even though the self-signed certificate was in the trusted store, DirectoryEntry was still refusing to establish a connection.

The LdapConnection class is a better choice for this situation, as it allows the user to validate the certificate manually. Note that the DirectoryEntry approach works fine with a trusted self-signed certificate when run in a Windows Forms application. A good example of using DirectoryEntry to connect to eDirectory can be found here.

Using the code
The example uses a Login control for simplicity. I recommend that in the "real world" you create a custom MembershipProvider. The following example code was used to connect to a Novell eDirectory server over secure LDAP. However, the code should work fine with other directory providers as long as the correct server/port/root DN is used.

Also remember to omit the con.SessionOptions.SecureSocketLayer = true line if you aren't using LDAPS. However, if you aren't using LDAPS you might as well use DirectoryEntry!

Connecting and authenticating
First, we set up our LdapConnection class. We specify the address and port of the server that we will be connecting over SSL, set up the certificate callback (more on that later) and provide the default credentials for authentication.

This example uses eDirectory's "contextless login" feature, so blank credentials are allowed. Depending on your LDAP server, you may need to specify credentials to search the directory. Also note that if no port is specified, then the default value of 389 will be used.

LdapConnection con = new LdapConnection(new LdapDirectoryIdentifier(
"EDIRECTORYSERVER:636"));
con.SessionOptions.SecureSocketLayer = true;
con.SessionOptions.VerifyServerCertificate =
new VerifyServerCertificateCallback(ServerCallback);
con.Credential = new NetworkCredential(String.Empty, String.Empty);
con.AuthType = AuthType.Basic;

Now we bind the initial connection. If con.Bind() executes without throwing an exception, the server and credentials specified are valid.

using (con)
{
con.Bind();

The next step is to search for the user's fully qualified, distinguished name. This is a necessary step because when users provide their usernames, they don't provide the full context of their names in the directory; i.e. jlennon is provided instead of cn=jlennon,ou=Beatles,ou=Artists,o=AppleRecordsLDAPDirectory.

First, we prepare the SearchRequest object. We specify the root DN, the search filter and the search scope. Then we send the request.

SearchRequest request = new SearchRequest(
"o=LDAPRoot",
"(&(objectClass=Person)(uid=" + Login1.UserName + "))",
SearchScope.Subtree);

SearchResponse response = (SearchResponse)con.SendRequest(request);

If we get this far without an exception being thrown, we know that the root DN and search filter specified are valid. If either is invalid, a DirectoryOperationException will be thrown.

Now we can extract the DN from the search result. If you want to provide a "no such username" message, you can check that response.Entries.Count > 0. An ArgumentOutOfRangeException will be thrown on the following line if the Username provided does not exist.

SearchResultEntry entry = response.Entries[0];
string dn = entry.DistinguishedName;

Now that we have the full DN for the user, we can check if the given password is valid. We set a new NetworkConnection on the LdapConnection object and re-bind. con.Bind() will throw LdapException if the password provided is invalid.

con.Credential = new NetworkCredential(dn, Login1.Password);
con.Bind();
}

If we get this far, we have successfully authenticated! We can now use a SearchRequest to search for group membership, etc. However, that's out of scope for this example.

VerifyServerCertificateCallback
This should need no explanation. We simply load the certificate file from disk and compare it to the certificate presented by the server. Production code should handle exceptions associated with reading the certificate: File not found, access denied, etc. If you really trust the server, you could omit all of this and just return true. ;)

public static bool ServerCallback(
LdapConnection connection, X509Certificate certificate)
{
try
{
X509Certificate expectedCert =
X509Certificate.CreateFromCertFile(
"C:\\certificates\\certificate.cer");

if (expectedCert.Equals(certificate))
{
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
return false;
}
}

Source Code Line Counter

Introduction
While updating my cv, I wanted a quick way of seeing how many lines of code my application contained (160,430 lines!). I haven't looked into Visual Studio Add-ins too much so I wrote a simple stand-alone application that has a few nice features.

Using the Application
Fire up the application and select a folder which contains your code or projects - that's it. The application then searches for all code files and counts every line of code. It displays the files included along the line count. It then generates a total at the bottom.

Checking the "Include subfolders" will instruct the software to search all the sub folders, and you also have the option to look for particular source files, *.cs for C# and *.vb for VB.net.

Nice Feature
Some of my projects are spread across two folders, (client / server) so I needed a way of adding these two totals together. After calculating the total for one folder, you can click on the Grouping > Add menu to add the total and directory to a list. As you tally up the folders a total is show in the menu.

Not a lot of Code
There isn't a lot of code to this but here's the important parts.

Using the DirectoryInfo.GetFiles function allows for easy use of searching just a folder or all folders contained within it with a search pattern, *.cs or *.vb. It's fast and saves time having to write your own recursive function.

DirectoryInfo dir = new DirectoryInfo(folderBrowserDialog1.SelectedPath);
FileInfo[] files = dir.GetFiles(cmbSearchPattern.SelectedItem.ToString(),
(chkIncludeSubfolders.Checked ? SearchOption.AllDirectories :
SearchOption.TopDirectoryOnly));

In case you aren't familiar with the inline if statement I hope this makes it a little clearer.

(true_or_false ? Do_this_if_true : Do_this_if_false)
(chkIncludeSubfolders.Checked ? SearchOption.AllDirectories :
SearchOption.TopDirectoryOnly));

Counting the lines
I figured that to count the lines I should open the text and use a RegEx "\n" to calculate the lines, but I guess that the RegEx is doing the same thing I would which is open the file and read each line. So I just use a loop to count the lines, which is just as quick I reckon.

reader = file.OpenText();
while (reader.ReadLine() != null)
{
fileCounter++;
}
reader.Close();

Collection.RemoveAt
Got stuck trying to remove items within a collection because I only need to remove a certain amount of items from a particular index. Items 0 to 3 contain the Add, Clear All, Total and a Separator. This solution below sorts this out.

// if there was two items or more in the collection then
// one item would always be left.
for (int i = 4; i < toolStripGrouping.DropDownItems.Count; i++)
{
toolStripGrouping.DropDownItems.RemoveAt(i);
}
// this fixes it
int count = toolStripGrouping.DropDownItems.Count;
for (int i = 4; i < count; i++)
{
toolStripGrouping.DropDownItems.RemoveAt(4);
}

Line Counter Code
The updated line counter code was changed to include or exclude certain lines. The easiest way for me to do this was to first remove the "\t" tabs from the line and then investigate the line of code.

Collapse // Replace the tabs
s = s.Replace("\t", "");

// Comment
if (s.StartsWith("//"))
{
dontCount = true;
}

// Blank line
if (s == "")
{
dontCount = true;
}

// This autoGenerateCode flag lets the software know that
// it should be looking for the first #endregion to close
// the starting one.
if (autoGeneratedCode)
{
if (s.Contains("#endregion"))
{
dontCount = true;
autoGeneratedCode = false;
}
}
else if (s == "#region Windows Form Designer generated code")
{
dontCount = true;
autoGeneratedCode = true;
}


History of Searches
I thought that re-browsing projects I have already counted was getting a bit repetitive so adding a history made sense.

Basically it works like this: if a search is performed it checks to see if it exists in the history stored in the ToolStripItems. If it does then it just moves that ToolStripItem to the top using the Insert function with a index set to 0. If it doesn't then it first checks to see if it has reached the maximum amount allowed (currently 5), removing the last one if it has and inserting the new search into index.

Collapseprivate void AddToFolderHistory(string FolderPath)
{
// Check if folder exists in the list
ToolStripItem[] searchItems = btnBrowse.DropDownItems.Find(FolderPath,
true);
if (searchItems.Length == 0)
{
// Doesn't exist
if (btnBrowse.DropDownItems.Count == maximumHistory)
{
btnBrowse.DropDownItems.RemoveAt(maximumHistory-1);
}

ToolStripMenuItem item = new ToolStripMenuItem();
item.Name = item.Text = FolderPath;
item.Click += new EventHandler(FolderHistory_Click);
this.btnBrowse.DropDownItems.Insert(0, item);
}
else
{
// Does exist so just move to the top
this.btnBrowse.DropDownItems.Insert(0, searchItems[0]);
}
}
private void FolderHistory_Click(object sender, EventArgs e)
{
// History item has been selected so move to the top
lblFolder.Text = ((ToolStripMenuItem)sender).Text;
this.btnBrowse.DropDownItems.Insert(0, ((ToolStripMenuItem)sender));
CountFiles(lblFolder.Text);
}


Conclusion
In my opinion, a neat little application that does exactly what it says on the tin. It's helped with my cv but no job offers just yet!

History
Added the ability to not count blank lines, comments, Visual Studio auto-generated code, or designer files. This is accessed through the File Options menu items.


Changed the search routine so that you can browse multiple extensions (*.cs|*.vb) at the same time.
Copy the current list of files and number of lines to the clipboard for email/printing
Redesigned the Browse feature with a Toolbar
Added a Recount button
A quick history list of the last 5 (configurable in code) searches - explained below.
Add the additional code to filter vb files.


If someone could send me the "#region" and "#endregion" in C++.net I can add this to the routine as well.

No ASPX just yet. I have a real software job to do.

License

Docking Toolbars in Plain C

First, we'll discuss the "floating" aspect of tool windows - i.e. how to get tool windows to stay floating on top of all other windows, how to get the window activation working correctly, etc. Later, we'll see how to get these floating tool windows to "dock" to a side of the owner window, and discuss various methods of window management.

The source code presented will culminate in a Win32 DLL called DockWnd.dll whose functions can be used by any Win32 program to easily support docking tool windows. There are many different ways to create a docking window. I imagine most of the code on the Internet uses well-designed C++ classes to hide the implementation. However, I still prefer to code in C, so the design of this library will be a non-object oriented approach and therefore easily useable by an application written in any language.

This code/article is based upon some original free code provided by James Brown. His website, contains an earlier, different version of this code (as well as numerous other free Win32 tutorials/examples).

Contents
DOCKINFO structure
How DockingCreateFrame creates a floating tool window
Prevent tool window deactivation
Show all tool windows as active
Sync the activation of all tool windows
Sync the enabled state of all tool windows
Docking a tool window
Floating versus docked size
Moving a window with a drag-rectangle
Drawing a drag-rectangle
Redrawing the docked windows
Enumerating tool windows
Various other features of the library
An application
Creating a tool window
Handling WM_SIZE message in the owner
Handling WM_NCACTIVATE message in the owner
Handling WM_ENABLE message in the owner
Handling messages sent to standard controls
Multiple child windows inside a tool window
Closing a tool window
Saving/Restoring a tool window size/position
Miscellaneous remarks
DOCKINFO structure
Because our docking library needs to maintain some information about each tool window it manages, we need some structure to store the information. We'll define a DOCKINFO structure (in dockwnd.h), and allocate a DOCKINFO for each tool window created. The application will call the DockingAlloc function in our docking library to allocate a DOCKINFO (initialized to default values), and then pass it to the DockingCreateFrame function which creates the actual tool window associated with that DOCKINFO. This struct will hold information such as the handle (HWND) to the tool window, the handle to the tool window's owner window, whether the tool window is currently docked to its owner or floating, and other information for our private use in managing docking tool windows. We store the handle to the tool window in the DOCKINFO's hwnd field. We store the handle to the owner window in the DOCKINFO's container field. And the value of the DOCKINFO's uDockedState field tells whether the tool window is floating or docked. This field is OR'ed with DWS_FLOATING (and therefore a negative value) when a tool window is floating. We'll discuss the other DOCKINFO fields later.

Note: It is the application's responsibility to create and manage the owner window. Our library deals only with creating/managing the tool windows.

We register our own window class (with the class name of "DockWnd32") for our tool windows. The window procedure (dockWndProc) for this class is inside of our library. We use the GWL_USERDATA field of the tool window to store a pointer to that tool window's DOCKINFO struct. In this way, we can easily and quickly fetch the appropriate DOCKINFO given only a handle (HWND) to a particular tool window.

We don't want to limit the application to only one owner window, and its set of docked windows. For example, perhaps an application will have two owner windows, each with its own set of docked windows. Nor, do we want to limit the application to a particular number of tool windows. So, we may be asked to create DOCKINFOs for numerous sets of tool windows and owners. By storing a pointer to a tool window's DOCKINFO in the tool window itself, and storing handles to the tool window and its owner window inside the DOCKINFO, we can easily get all of the information we need for our library to do what it needs to do, with minimal work on the part of the application.

There are times when our library needs to be able to enumerate all tool windows for a particular owner window. We'll get to the particulars of that later. In some of our example code below, we'll just refer to a placeholder function called DockingNextToolWindow which you should assume will fetch the next tool window (actually, that tool window's DOCKINFO) for a particular owner window. In the actual source code for the library, this is replaced by more complex code that we'll examine later.

How DockingCreateFrame creates a floating tool window
A floating tool window is just a standard window with the WS_POPUP style. When a popup window is created with an owner window, the popup is positioned so that it always stays on top of that owner window. This is how we can create and display a floating tool window:

// Create a floating (popup) tool window
HWND hwnd = CreateWindowEx(
WS_EX_TOOLWINDOW,
"ToolWindowClass", "ToolWindow",
WS_POPUP | WS_SYSMENU | WS_THICKFRAME | WS_CAPTION | WS_VISIBLE,
200, 200, 400, 64,
hwndOwner, NULL, GetModuleHandle(0), NULL
);

Note: In the above example, it is assumed that hwndOwner is a handle to some other window the application created to be the owner window for our tool window. The application must create this window, and then pass its handle to DockingCreateFrame. In other words, the application must create the owner window before any tool window can be created for it.

The WS_EX_TOOLWINDOW extra style doesn't do anything special, other than to make a window with a smaller titlebar. It doesn't make the window magically float - this is achieved automatically by specifying WS_POPUP style and an owner window. Here's what the above CreateWindowEx may display:



A tool window floating above its owner window ("Main window").

Prevent window deactivation
The image above shows the owner window ("Main window") with an inactive titlebar. This is entirely normal, because only one window at a time can have the input focus, and the operating system normally shows only that one window with an active titlebar. So, when we create our tool window, the operating system shows the tool window as active, and shows the owner window as deactivated.

But, it is normal practice for tool windows and their owner window to appear active at the same time. It looks more natural this way. So we need to devise a strategy to keep our tool window and owner window both appear active, even if only one technically has the input focus.

The solution involves the WM_NCACTIVATE message. The operating system sends this message when a window's non-client area (the titlebar and border) needs to be activated or deactivated. As with all window messages, WM_NCACTIVATE is sent with two parameters - wParam and lParam. When a window receives WM_NCACTIVATE with wParam=TRUE, this indicates that the titlebar and border should be shown as active. When wParam=FALSE, this indicates that the titlebar and border should be shown as inactive.

Note: MSDN states that WM_NCACTIVATE's lParam will always be 0. However, I have observed that lParam indicates the window handle of the window being deactivated. This appears to be true under Win95, 98 and NT, 2000, XP. Our solution relies upon this undocumented feature.

So, when we create our tool window, our owner window receives a WM_NCACTIVATE with wParam=FALSE and our tool window receives a WM_NCACTIVATE with wParam=TRUE.

When this message is passed to DefWindowProc(), the operating system does two things. First, the titlebar is drawn as either active or inactive, depending upon whether wParam is TRUE or FALSE respectively. Secondly, the operating system sets an internal flag for the window which remembers if the window was painted as active or inactive. This enables DefWindowProc() to process subsequent WM_NCPAINT messages to paint the titlebar with the proper activation. It is advisable to always pass WM_NCACTIVATE to DefWindowProc() so that this internal flag is set, even if you also do your own processing of WM_NCACTIVATE.

This WM_NCACTIVATE message provides us with a way to make all our tool windows, and owner window, look active, even if only one window technically has the focus. To do this, whenever our tool windows or owner window receive a WM_NCACTIVATE, we will always substitute TRUE for wParam when we pass the WM_NCACTIVATE to DefWindowProc(). The result is that the operating system always renders the titlebars of our tool windows and owner window as active.

Here is some code we could add to the window procedure of all our tool windows, and owner window, to show them all as simultaneously active:

case WM_NCACTIVATE:
return DefWindowProc(hwnd, msg, TRUE, lParam);



Both the tool window and its owner window are shown active.

Incidentally, MDI child windows also use this same technique to keep their titlebars active. The only difference is that MDI windows have the WS_CHILD style, instead of WS_POPUP.

Show all tool windows as active
The above method seems to do what we want, but there is a problem. Our owner window and tool windows will always appear active, even if our application is not in the foreground. For example, if the end user switches to some other application's window, our tool window and owner window still will look active, which can be a bit disconcerting to the end user.

Also, whenever we display a message box or a normal dialog box, the owner window and tool windows will still appear active, when in this scenario we ideally want to make them look inactive.

This calls for a more careful study of window activation messages. Here is a description of the series of window activation messages sent when one window becomes active, and another inactive:

WM_MOUSEACTIVATE is sent to the window about to become active, to ask it whether or not the activation request should be allowed. What your window procedure returns (i.e., MA_ACTIVATE or MA_NOACTIVATE) affects the subsequent activation messages.
WM_ACTIVATEAPP is sent if a window belonging to a different application is about to become active (or inactive). This message is sent both to the window that is currently active (to tell it that it is about to become inactive) as well as the window that is about to become active. The return value should always be zero, and never affects subsequent messages' behaviour.
As described above, WM_NCACTIVATE is sent when a window's non-client area needs to be shown activated or deactivated.
WM_ACTIVATE is sent last of all to the window becoming active. When this message is passed to DefWindowProc(), the operating system sets the input focus to that window.
With all of these activation messages, only two windows are actually involved - the window being deactivated, and the window being activated. So, even if we have many floating tool windows, not all of them will receive these messages. Only the one window being activated, and the one window being deactivated, receive the messages. But to make things look and feel right, we want the displayed state (i.e., whether a tool window's titlebar is shown active or inactive) of each tool window to be the same as all other tool windows. So, even though not all of our windows will receive the above messages, we still need to have all windows synced to the same state.

This same discussion applies whenever we need to disable or enable our owner window. If the owner window is to be disabled or enabled, we want to sync all the tool windows to that same state. (But, a different set of messages are sent for a window being disabled or enabled.)

So, our docking library has a bit of work to do in order to make things look and feel right:

When our application is activated / deactivated, we need to sync the active / inactive display of all tool windows with each other.
This also applies to activation within our own application. For example, if the user activates a window we create that isn't one of our tool windows, then we want to show all tool windows deactivated. And if the user switches back to a tool window from that window, we want all tool windows shown active.

When the owner window is disabled due to a modal dialog or message box being displayed, then we must disable all tool windows (and any modeless dialogs) to prevent the user from interacting with them while the modal dialog / message box is on screen.
A first try at a solution
Our first stab at a solution will be to concentrate on the WM_ACTIVATE message. This message is received whenever a window is activated or deactivated. The direction we will take will be to decide if the window receiving this message is active or inactive, and synchronise all other windows to the same state by manually sending them a "spoof" WM_NCACTIVATE message. This spoof message will force the other windows to update their titlebars to the same state as the window receiving the WM_ACTIVATE.

Here's a function that we could add to our docking library. Whenever one of our tool windows, or owner window, receives a WM_ACTIVATE message, it will call this function to sync the state of all tool windows:

Collapse/*********************** DockingActivate() **********************
* Sends WM_NCACTIVATE to all the owner's tool windows. A
* tool or owner window calls this in response to receiving
* a WM_ACTIVATE message.
*
* container = Handle to owner window.
* hwnd = Handle to window which received WM_ACTIVATE (can
* be the owner, or one of its tool windows).
* wParam = WPARAM of the WM_ACTIVATE message.
* lParam = LPARAM of the WM_ACTIVATE message.
*/

LRESULT WINAPI DockingActivate(HWND container,
HWND hwnd, WPARAM wParam, LPARAM lParam)
{
DOCKINFO * dwp;
BOOL fKeepActive;

fKeepActive = (wParam != WA_INACTIVE);

// Get the DOCKINFO of the next tool window for this owner window. when 0
// is returned, there are no more tool windows for this owner.
while ((dwp = DockingNextToolWindow(container)))
{
// Sync this tool window to the same state as the window that called
// DockingActivate.
SendMessage(dwp->hwnd, WM_NCACTIVATE, fKeepActive, 0);
}

// Allow the window that called DockingActivate to handle its WM_NCACTIVATE
// as normally it would.
return DefWindowProc(hwnd, WM_ACTIVATE, wParam, lParam);
}

It works, after a fashion. All tool windows activate and deactivate correctly, and all at the same time. This solution is not the best though.

The problem is that every tool window's titlebar flashes whenever the active window changes. This is because of the way the operating system sends the WM_ACTIVATE message. This message is first sent to the window that is being deactivated. If that happens to be a tool window or the owner window, it will call DockingActivate to deactivate all the tool windows. WM_ACTIVATE is then sent to the active window. If that window also happens to be a tool window or owner window, it will call DockingActivate to (correctly) activate all the tool windows. It is the fact that DockingActivate is quickly called twice (once to deactivate the tool windows, and then to activate them) that causes all the windows to flash.

A partial solution is to perform a check before deactivating the tool windows. We know that if a window is being deactivated, lParam identifies the (other) window about to be activated. And if this other window is one of our tool windows, we can skip deactivating the tool windows, because we know the other (tool) window is going to subsequently activate them anyway.

if (fKeepActive == FALSE)
{
while ((dwp = DockingNextToolWindow(container)))
{
if (dwp->hwnd == (HWND)lParam)
return DefWindowProc(hwnd, WM_ACTIVATE, wParam, lParam);
}
}

This prevents every tool window from briefly deactivating, then activating again. There is still a problem, albeit a minor one. The problem is, the single tool window that is being deactivated will still flicker briefly before being activated again. This is because it will already have received its WM_NCACTIVATE message, which caused the window to be redrawn deactivated. The window gets its activated look eventually, but this brief flicker is still visible.

Sync the activation of all tool windows
We need to take a step back and approach the problem from a slightly different direction. Instead of handling WM_ACTIVATE, which is called after a window's titlebar is redrawn, we'll go straight to the heart of the problem, and rewrite DockingActivate to be called whenever a window receives a WM_NCACTIVATE message. This will ensure that no unnecessary activation or deactivation will take place.

The function presented below performs several tasks on behalf of the tool (or owner) window that calls DockingActivate:

Search the list for the other window being activated/deactivated (the window specified by lParam, rather than the window receiving WM_NCACTIVATE). If this other window is a tool window, then we force all tool windows as activated.
Synchronize all current tool windows to our (possibly new) state.
Activate/deactivate the window that calls DockingActivate, depending on our new state.
The code looks like this:

CollapseLRESULT WINAPI DockingActivate(HWND container,
HWND hwnd, WPARAM wParam, LPARAM lParam)
{
DOCKINFO * dwp;
BOOL fKeepActive;
BOOL fSyncOthers;

// If this is a spoof'ed message we sent, then handle it
// normally (but reset LPARAM to 0).
if (lParam == -1)
return DefWindowProc(hwnd, WM_NCACTIVATE, wParam, 0);

fKeepActive = wParam;
fSyncOthers = TRUE;

while ((dwp = DockingNextToolWindow(container)))
{
// UNDOCUMENTED FEATURE:
// If the other window being activated/deactivated (i.e. not the one that
// called here) is one of our tool windows, then go (or stay) active.
if ((HWND)lParam == dwp->hwnd)
{
fKeepActive = TRUE;
fSyncOthers = FALSE;
break;
}
}

if (fSyncOthers == TRUE)
{
// Sync all other tool windows to the same state.
while ((dwp = DockingNextToolWindow(container)))
{
// Send a spoof'ed WM_NCACTIVATE message to this tool window,
// but not if it is the same window that called here. Note that
// we substitute a -1 for LPARAM to indicate that this is a
// spoof'ed message we sent. The operating system would never
// send a WM_NCACTIVATE with LPARAM = -1.
if (dwp->hwnd != hwnd && hwnd != (HWND)lParam)
SendMessage(dwp->hwnd, WM_NCACTIVATE, fKeepActive, -1);
}
}

return DefWindowProc(hwnd, WM_NCACTIVATE, fKeepActive, lParam);
}

The code above uses an undocumented feature of the WM_NCACTIVATE message which I observed while experimenting with these activation messages. The MSDN documentation states that lParam is unused (presumably zero), but this is not the case under Windows 95, 98, ME, and NT, 2000, XP.

Instead, lParam is a handle to the other window being activated/deactivated in our place (i.e., if we are being deactivated, lParam will be the handle to the window being activated). This is not always the case, specifically when the other window being activated/deactivated belongs to another process. In this case, lParam will be zero.

Sync the enabled state of all tool windows
Now, we need to tackle the other problem. When our owner window is disabled (perhaps because a modal dialog or message box has popped up), we need to disable all tool windows too. This feature prevents the user from clicking on and activating not only the main window, but also any tool window, while the modal dialog or message box is displayed.

The solution is similar to how we solved the activation problem, except that this time we write a function that a tool window or the owner window calls whenever it receives a WM_ENABLE message. DockingEnable simply enables/disables all the tool windows to the same state as the owner window.

Collapse/*********************** DockingEnable() **********************
* Sends WM_ENABLE to all the owner's tool windows.
* A window calls this in response to receiving a
* WM_ENABLE message.
*
* container = Handle to owner window.
* hwnd = Handle to window which received WM_ENABLE (can
* be the owner, or one of its tool windows).
* wParam = WPARAM of the WM_ENABLE message.
* lParam = LPARAM of the WM_ENABLE message.
*/

LRESULT WINAPI DockingEnable(HWND container,
HWND hwnd, WPARAM wParam, >LPARAM lParam)
{
DOCKINFO * dwp;

while ((dwp = DockingNextToolWindow(container)))
{
// Sync this tool window to the same state as the window that called
// DockingEnable (but not if it IS the window that called here).
if (dwp->hwnd != hwnd) EnableWindow(dwp->hwnd, wParam);
}

// Allow the window that called DockingEnable to handle its WM_ENABLE
// as normally it would.
return DefWindowProc(hwnd, WM_ENABLE, wParam, lParam);
}

Docking a tool window
The previous discussion took you through the steps necessary to create floating tool windows. Now, we'll discuss the techniques necessary to get these floating windows to "dock" with their owner window. I'm not going to reproduce all the library's source code in this tutorial, because quite a lot is involved. I'm instead going to give an overview of the approach taken, and you can study the profusely commented source code for details.

First of all, we need to define the terms "Docked" and "Undocked". A tool window is undocked when it is floating. And as we already know, in order to make that happen, the tool window must have the WS_POPUP style.

On the other hand, a tool window is docked when it is visually contained completely within its owner window, alongside one of the owner's borders. In order to make this happen, we must create the tool window with the WS_CHILD (not WS_POPUP) style (or change the style from WS_POPUP to WS_CHILD), and make its owner window also its parent window. When a tool window has the WS_CHILD style, the operating system restricts it to the area inside of its parent window, and the tool window is graphically "anchored" to its parent window (i.e., when the end user moves the parent window, the child window automatically moves with it).

But note that when the parent window is resized, the parent window will need to also move/resize the docked tool window so that the tool window remains "attached" to the border. (Of course, our library has functions the owner window can call to make this as easy as possible.)

A good docking library must allow the end user to be able to dock and undock any tool window by grabbing the tool window with the mouse and dragging it over to a dockable or undockable area. There are many different ways to implement docking windows. This is because there is no standard, built-in docking window support in Windows. Application developers have had to implement their own docking windows, or rely upon third party libraries to do the work for them (such as MFC).

There are two common types of docking window implementations. The most common (and intuitive, in my opinion) is the type where you grab the tool window (by a "gripper bar" or its title bar) with the mouse, and drag it around the screen. When you drag the tool window, instead of the window itself moving, a drag-rectangle (feedback rectangle) is XORed on the screen, showing the outline of where the window will move to when you release the mouse - like the way windows work when full-window dragging is turned off. With this method, when a window is dragged to / from a window, the feedback rectangle visibly changes to indicate that the window can be dropped. This is the docking implementation that our docking library uses.



A tool window being dragged. You can see the drag rectangle.

The second type of docking implementation can be found in some newer style applications (such as Microsoft Outlook). Instead of a feedback rectangle, windows can be directly "teared" or "snapped" on or off the owner window - i.e., they snap into place as soon as you manipulate them. Personally, I don't like this type of user-interface, and our docking library does not use it.

Our tool windows will have the following characteristics:

A docked tool window will have a "gripper bar" along its left side to allow the user to grab it and undock it.
A tool window will use a feedback (drag) rectangle as it's moved around the screen - even if the "full window drag" system setting is in effect. This is shown in the picture above.
While a drag-rectangle is dragged around the screen, at some point it will intersect one of the borders of its owner window. When this happens, the drag-rectangle will need to visibly change in order to reflect the fact that the tool window is now within a docking "region". Normal convention is for a wide (say three pixel) shaded rectangle to represent a floating position, and for a single-pixel rectangle to represent a docked position.
When the mouse is released after dragging a tool window, a test must be made to see if the window should be made to dock or float. (i.e., was the drag-rectangle ultimately moved to one of these docking "regions", or is it outside of any such region and therefore the tool window is floating?)
At the end user's discretion, a tool window can be forced to float, even when the drag-rectangle is released over a dockable area. This is usually achieved by the end user holding the key down.
When floating, a tool window can be resized just like any normal window. No special processing is required to do this - the standard Windows sizing behaviour can be used in this case.
When docked, a tool window can be resized either vertically or horizontally (but not both) to decrease or increase its size. A tool window docked to the top or bottom border of its owner can be resized horizontally. A tool window docked to the left or right border can be resized vertically.
When the user double-clicks a floating tool window's titlebar, or a docked window's gripper bar, the tool window is toggled from floating to docked, or vice versa.
Our docking library keeps track of whether a tool window is docked or floating. And if it is docked, we need to know to which of the owner's borders the tool window is docked. The uDockedState field of the DOCKINFO is used to store this state. As mentioned, if this field is OR'ed with DWS_FLOATING, then the tool window is floating. If not OR'ed with DWS_FLOATING, then the tool window is docked, and the remaining bits of the field are either DWS_DOCKED_LEFT, DWS_DOCKED_RIGHT, DWS_DOCKED_TOP, or DWS_DOCKED_BOTTOM depending upon to which border the tool window is docked.

We need to be able to toggle a tool window between being a child window (docked) and being a popup window (floating). This is basically accomplished with the code shown below.

// Assume "dwp" is a pointer to the tool window's DOCKINFO.

DWORD dwStyle = GetWindowLong(dwp->hwnd, GWL_STYLE);

// Is the window currently floating?
if (dwp->uDockedState & DWS_FLOATING)
{
// Toggle from WS_POPUP to WS_CHILD. We do this by altering
// the window's style flags to remove WS_POPUP, and add
// WS_CHILD. Then, we set the owner window as the parent.
SetWindowLong(dwp->hwnd, GWL_STYLE, (dwStyle & ~WS_POPUP) | WS_CHILD);
SetParent(dwp->hwnd, dwp->container);
}
else
{
// Toggle from WS_CHILD to WS_POPUP. We do this by altering
// the window's style flags to remove WS_CHILD, and add
// WS_POPUP. Then, we make sure it has no parent.
SetWindowLong(dwp->hwnd, GWL_STYLE, (dwStyle & ~WS_CHILD) | WS_POPUP);
SetParent(dwp->hwnd, NULL);
}

Look at the second SetParent API call in the code above. The only way to make a child (docked) window into a popup (floating) window is to set its parent window to zero (NULL). Because the tool window no longer has a parent, it is not visually confined to some other window. It can float freely around the desktop. But because it still has an owner window, the operating system keeps it floating above that owner window. In other words, when a window is docked, its owner window is also its parent window. When a window is floating, its owner window is no longer its parent as well.

Floating versus docked size
As mentioned, a tool window can be in one of two states: docked, or floating (undocked). We will remember the size of a tool window both in its floating state, and its docked state, and store this information in the DOCKINFO. In this way, the end user can give the tool window different sizes for its two states. Because we also allow the end user to quickly toggle between the two states by double-clicking on the gripper/titlebar, we need to remember where the tool window was last positioned in both states.

When a tool window is floating, it can be resized just like a normal window. This means that we will need to store both the width and height in the DOCKINFO. And of course, in order to remember its position, we need to store its X and Y position (in screen coordinates). These values are stored in the DOCKINFO's cxFloating, cyFloating, xpos, and ypos fields respectively.

Note: cxFloating and cyFloating are actually set to the size of the floating tool window's client (inner) area instead of the physical size of the tool window itself (including its titlebar and borders). This is because we always want the client area to remain the same size, even when the system settings change (i.e. the titlebar height is modified using the Control Panel).

When a tool window is docked, it can be resized in only one direction -- vertically or horizontally. This means that we need to remember only its width or height, but not both. If the tool window is docked to the top or bottom border of the owner, then we remember its height. If the tool window is docked to the left or right border of the owner, then we remember its width. Whichever value we remember, we store it in DOCKINFO's nDockedSize field. As far as its position is concerned, that is already remembered in the DOCKINFO's uDockedState field.

Moving a window with a drag-rectangle
The first obstacle we encounter is getting Windows to show a feedback rectangle when the end user moves a floating window around. Starting with Windows 95, a new user-interface feature was introduced. This feature is normally referred to as "Show window contents while dragging". When enabled, windows are no longer moved and sized using the standard feedback rectangle.

Unfortunately, there is no way to turn this feature off for specific windows. The SystemParametersInfo API call (with the SPI_GETDRAGFULLWINDOWS setting) can turn this feature on and off, but this is a system-wide setting, and is not really suitable. Of course, we could devise a method where we temporarily turn off the drag-window system setting just during the window movement (actually, this would be very straight-forward). The point is, it's a bit of a hack, and I prefer proper solutions to problems like this.

The only solution is to override the standard Windows behaviour and manually provide a feedback rectangle. This means processing a few mouse messages. Now, I don't want to show any code - again, the source code clearly demonstrates how to get this working (in the window procedure for a tool window, dockWndProc). What I will do is give a basic outline of the processing that is required.

The most important task is to stop the user from dragging the window around with the mouse. I know this sounds counter-productive, but we need to completely take over the standard window movement logic. This is actually quite simple - our docking window procedure just needs to handle WM_NCLBUTTONDOWN, and return 0 if the mouse is clicked in the caption area. By preventing the default window procedure from handling this message, window dragging is completely disabled.

In order to simulate the window being moved, we need to handle a few mouse messages. Only three need processing:

WM_NCLBUTTONDOWN - This message is received when the end user clicks on a tool window. In addition to returning 0 to prevent the operating system from doing normal window dragging, we draw the drag-rectangle at its initial position, and set the mouse capture using the SetCapture API call. We also install a keyboard hook so we can check if the end user presses the CTRL key (to force the tool window floating) or the ESC key (to abort the operation).
WM_MOUSEMOVE - This message is received whenever the mouse is moved. Our response is to redraw the drag-rectangle in the new position (erase it in the old position and draw it in the new position). In addition, we need to decide what type of rectangle to draw, depending on if the end user has moved the rectangle into a dockable region, or not.
WM_LBUTTONUP - This message is received when the mouse is released. We remove the drag-rectangle from the screen, release the mouse capture, and then take the appropriate action to physically reposition the tool window. This may mean docking / undocking, or simply moving the window if it was already floating.
As you can see, there's a little bit of work involved, but nothing particularly complicated. The big advantage of using this method is that the same mouse code can be used when the window is docked or floating. This keeps the code short and simple.

Drawing a drag-rectangle
A drag-rectangle is basically just a simple rectangle. This rectangle ideally needs to be drawn using XOR blitting logic, so that we can easily draw / erase the rectangle as it is moving around.



A tool window being dragged. You can see the drag rectangle.

The code below draws a shaded rectangle with the specified coordinates. The equivalent function in the source code does a little more than the code below (it draws both types of drag-rectangles), but I've stripped it down to keep it simple.

Collapsevoid DrawXorFrame(int x, int y, int width, int height)
{
// Raw bits for bitmap - enough for an 8x8 monochrome image
static WORD _dotPatternBmp1[] =
{
0x00aa, 0x0055, 0x00aa, 0x0055, 0x00aa, 0x0055, 0x00aa, 0x0055
};

HBITMAP hbm;
HBRUSH hbr;
HANDLE hbrushOld;
WORD *bitmap;

int border = 3;

HDC hdc = GetDC(0);

// Create a patterned bitmap to draw the borders
hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp1);
hbr = CreatePatternBrush(hbm);

hbrushOld = SelectObject(hdc, hbr);

// Draw the rectangle in four stages - top, right, bottom, left
PatBlt(hdc, x+border, y, width-border, border, PATINVERT);
PatBlt(hdc, x+width-border, y+border, border, height-border, PATINVERT);
PatBlt(hdc, x, y+height-border, width-border, border, PATINVERT);
PatBlt(hdc, x, y, border, height-border, PATINVERT);

// Clean up
SelectObject(hdc, hbrushOld);
DeleteObject(hbr);
DeleteObject(hbm);

ReleaseDC(0, hdc);
}

As you can see, we have the bitmap data for our rectangle as global data in our docking library. And we simply call some graphics functions to blit in onto the screen (in a rectangular shape) at the screen position where the end user has currently moved the mouse.

Redrawing the docked windows
When a tool window's state changes from docked to floating, or vice versa, this means that the layout of the owner window needs to be redrawn. For example, if a tool window was floating, and then is docked to the owner window, then other tool windows already docked may need to be resized/repositioned to accommodate the new docked tool window.

And if a tool window was docked to the owner window, but is torn off and left floating, that means the other, remaining docked windows may likewise need to be resized/repositioned to fill the "hole" left by the formerly docked window.

Whenever a tool window's state toggles between states, our docking library has a function named updateLayout that is called to send a spoofed WM_SIZE message to the owner window to inform it that it needs to redraw itself. The owner window then is expected to redraw its contents and call a docking library function named DockingArrangeWindows. DockingArrangeWindows does all the work of repositioning and redrawing the docked tool windows.

Enumerating tool windows
In the above code excerpts, we had a placeholder function named DockingNextToolWindow that enumerated the tool windows for a given owner window. We don't actually have such a function in the docking library. Let's examine how our docking library actually enumerates tool windows.

Unfortunately, the Windows operating system does not have a function to enumerate all the windows owned by a particular window. If it did, we could just pass our owner window to that function. What the operating system does have is a function called EnumChildWindows. This enumerates all of the child windows of a given parent window. Since a docked tool window has its owner window as its parent also, EnumChildWindows will enumerate all the docked tool windows for a given owner. But EnumChildWindows will not enumerate any of the floating tool windows, because the owner window is not also the parent of the floating tool windows.

There is another operating system function called EnumWindows. This enumerates all of the top-level (i.e., popup) windows on the desktop. Since our floating tool windows are WS_POPUP style, this works to enumerate them. (But, it will enumerate all windows on the desktop in addition to our tool windows, so we have a little extra work to do to isolate only the desired tool windows). EnumWindows does not enumerate any of the children (WS_CHILD windows) of those top-level windows. So, EnumWindows will not enumerate any docked tool windows.

Therefore, enumerating all the tool windows will be a two-step process. First, we'll call EnumChildWindows to enumerate the docked windows for a given parent window (which also happens to be the owner window). Then, we will call EnumWindows to enumerate the floating tool windows for a given owner window, and do some extra processing to make sure that the windows we isolate are for the desired owner window.

Let's examine a function that counts how many total tool windows a given owner window has, both floating and docked.

Collapsetypedef struct {
UINT count;
HWND container;
} DOCKCOUNTPARAMS;

/***************** DockingCountFrames() *****************
* Counts the number of tool windows for the specified
* owner window.
*
* container = Handle to owner window.
*/

UINT WINAPI DockingCountFrames(HWND container)
{
DOCKCOUNTPARAMS dockCount;

// Initialize count to 0, and store the desired owner window
dockCount.count = 0;
dockCount.container = container;

// Enumerate/count the floating tool windows
EnumWindows(countProc, (LPARAM)&dockCount);

// Enumerate/count the docked tool windows
EnumChildWindows(container, countProc, (LPARAM)&dockCount);

// Return the total count
return dockCount.count;
}

/******************* countProc() ********************
* This is called by EnumChildWindows or EnumWindows
* for each window.
*
* hwnd = Handle of a window.
* lParam = The LPARAM arg we passed to EnumChildWindows
* or EnumWindows. That would be our DOCKCOUNTPARAMS.
*/

static BOOL CALLBACK countProc(HWND hwnd, LPARAM lParam)
{
DOCKINFO * dwp;

// Is this one of the tool windows for the desired owner window?
if (GetClassWord(hwnd, GCW_ATOM) == DockingFrameAtom &&
(dwp = (DOCKINFO *)GetWindowLong(hwnd, GWL_USERDATA)) &&
dwp->container == ((DOCKCOUNTPARAMS *)lParam)->container)
{
// Yes it is. Increment count.
((DOCKCOUNTPARAMS *)lParam)->count += 1;
}

// Tell operating system to continue.
return TRUE;
}

Notice that we use countProc() as the callback for both EnumWindows and EnumChildWindows. And we pass our own initialized DOCKCOUNTPARAMS structure to our callback. First, we call EnumWindows to enumerate the floating windows. Then we call EnumChildWindows to enumerate the docked windows for our desired owner. So, let's examine countProc(). The entire key to making this work is to fetch and check the class ATOM for the window. If it matches the ATOM we got when we registered our own docking window class (returned by RegisterClassEx), then we know this is one of our tool windows. And if it is one of our tool windows, we know that its GWL_USERDATA field should contain its DOCKINFO. And note that the owner window handle has been stored in the DOCKINFO's container field. So we need only compare this handle with the owner handle passed to DockingCountFrames in order to determine if it is a tool window for the desired owner window.

Various other features of the library
The discussion above details all of the most important aspects of our docking library's features. But, there are some more, incidental features which are optional. You can enable any of these features for a given tool window just by setting the appropriate value into its DOCKINFO's dwStyle field. For example, you can force a tool window to always stay docked or floating. You can force it to keep its original size. You can restrict to which sides of the owner window the tool window may be docked.

When DockingAlloc creates a DOCKINFO, none of these extra features are enabled.


--------------------------------------------------------------------------------

An application
Up to this point, we've discussed only the code in the docking library. Since the whole intent of the library is to be used by an application, now we'll turn our attention to a sample application. There is a sample C application called DockTest included with the library. This example creates one owner window. The owner has a View -> Tool Window menu item you can select to create a tool window. You can then move the tool window around, docking and undocking it, to get a feel for how the implementation works. Each time you select this menu item, another tool window is created, so you can see how multiple tool windows can be floated and docked.

The owner window we create is an MDI window, and its window procedure is frameWndProc. You can open a document window with the File -> New menu item, and see how the docked tool windows interact with a document window. (But, as we'll see later, the application needs to do a little work to manage this interaction.)

Creating a tool window
Let's examine how the application creates a tool window. This happens when the View -> Tool Window menu item is selected, so the place where we create the tool window is in frameWndProc's handling of WM_COMMAND for menu ID IDM_VIEW_TOOLWINDOW. Below is a slightly simplified version of what needs to be done to create a tool window:

Collapsevoid createToolWindow(HWND owner)
{
DOCKINFO *dw;
HWND frame;

// Allocate a DOCKINFO structure.
if ((dw = DockingAlloc(DWS_DOCKED_BOTTOM)))
{
// Create a Docking Frame window (ie, the tool window).
if ((frame = DockingCreateFrame(dw, owner, "My title")))
{
// Create the child window that will be hosted inside of the Docking
// Frame window (ie, the contents of the tool window's client area)
// and save it in the DOCKINFO's focusWindow field. We'll create an
// EDIT control to be the contents, but you can utilize any standard
// control, or a child window of your own class.
if((dw->focusWindow = CreateWindow("EDIT", 0,
ES_MULTILINE|WS_VSCROLL|WS_CHILD|WS_VISIBLE,
0,0,0,0,
frame,
(HMENU)IDC_MYEDIT, GetModuleHandle(0), 0)))
{
// Show the Docking Frame.
DockingShowFrame(dw);

// Success!
return;
}

// Destroy the tool window if we can't create its contents.
// NOTE: The docking library will free the above DOCKINFO.
DestroyWindow(frame);
}
MessageBox(0, "Can't create tool window", "ERROR", MB_OK);
}
else
MessageBox(0, "No memory for a DOCKINFO", "ERROR", MB_OK);
}

First, we call DockingAlloc to get a DOCKINFO structure. We pass the desired initial state, which will be one of DWS_FLOATING, DWS_DOCKED_LEFT, DWS_DOCKED_RIGHT, DWS_DOCKED_TOP, or DWS_DOCKED_BOTTOM, depending upon whether we want the tool window initially created floating, or docked to one of the four borders. The docking library creates a DOCKINFO and initializes it to default values, returning a pointer.

At this point, we could modify the DOCKINFO if we want something other than the default features. In the above code, we simply go with the defaults.

Next, we call DockingCreateFrame to create the actual tool window. We pass the DOCKINFO we just got, the handle to our owner window, and the desired title for the tool window (which is shown only when the tool window is floating). DockingCreateFrame will create the tool window and return its handle. The tool window is not created visible, so nothing has yet shown up onscreen.

Now, a tool window with nothing inside of it would not be of much use. So we need to create something inside of the tool window that is of use to the end user. We can say that the tool window needs some "contents". Specifically, we need to create some WS_CHILD window which has the tool window as its parent. This can be any standard control, such as an Edit box, list box, tree-view control, etc. Or it could be a window of our own class. In the above code, we simply create a multi-line Edit control. Note that we have set the tool window to be this control's parent, and also specified the WS_CHILD style. This will cause the Edit control to be visually embedded inside of the tool window, and automatically move with the tool window. The size and position of the Edit control is not important now, because it will be sized and positioned later, before the tool window is finally made visible. We stuff the handle to this control into the DOCKINFO's focusWindow field. The docking library will automatically size this control to fill the client area of the tool window, and also give the control the focus whenever the user activates that tool window.

Finally, we call DockingShowFrame. This first sends a WM_SIZE message to our owner window (which is where we will do the final sizing/positioning of the tool window and its contents-window), and then makes the tool window visible.

That's all there is to creating a tool window. At this point, the docking library will manage the docking and undocking of this window.

Handling WM_SIZE message in the owner
The docking library transparently handles most aspects of the tool windows. But there are a couple times when it needs help from the application. One such time is whenever the owner window is resized. Given a new size for the owner window, it stands to reason that any docked tool window may also need to be resized and repositioned so that it stays docked to the desired side of the owner window. For this reason, the owner window will have to do the following when it receives a WM_SIZE:

Collapse case WM_SIZE:
{
HDWP hdwp;
RECT rect;

// Do the default handling of this message.
DefFrameProc(hwnd, MainWindow, msg, wParam, lParam);

// Set the area where tool windows are allowed.
// (Take into account any status bar, toolbar etc).
rect.left = rect.top = 0;
rect.right = LOWORD(lParam);
rect.bottom = HIWORD(lParam);

// Allocate enough space for all tool windows which are docked.
hdwp = BeginDeferWindowPos(DockingCountFrames(hwnd,
1) + 1); // + 1 for the MDI client

// Position the docked tool windows for this owner
// window. rect will be modified to contain the "inner" client
// rectangle, where we can position an MDI client.
DockingArrangeWindows(hwnd, hdwp, &rect);

// Here we resize our MDI client window so that it fits into the area
// described by "rect". Do not let it extend outside of this
// area or it (and the client windows inside of it) will be obscured
// by docked tool windows (or vice versa).
DeferWindowPos(hdwp, MainWindow, 0, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE|SWP_NOZORDER);

EndDeferWindowPos(hdwp);

return 0;
}

First, we pass the WM_SIZE to DefFrameProc to let the operating system do the default sizing of the owner window. We need to do this first so that the owner window's size is finalized before we go ahead and resize/reposition the docked tool windows.

The docking library has a function called DockingArrangeWindows that redraws all of the docked windows for a given owner. So to completely redraw its docked windows, all the owner needs to do is call this one function. But, there are a couple prerequisites. First, the owner must fill in a RECT with the dimensions of its client area, and pass this to DockingArrangeWindows. Secondly, the owner window must call the Windows API BeginDeferWindowPos to reserve enough space for all the docked windows. (The docking library has a function called DockingCountFrames which can be called to retrieve the total number of docked tool windows in an owner.) We use BeginDeferWindowPos so that, if there are many tool windows, we defer the final painting until after all of them are sized and positioned. This is more efficient and doesn't cause any unsightly visual artifacts for the end user to witness.

One very important aspect to note is that, after DockingArrangeWindows resizes and repositions all of the docked tool windows, it updates the RECT so that it encompasses only the owner client area not occupied by the tool windows. In other words, the RECT is the remaining, blank client area. We take this remaining area, and we resize/reposition our MDI child so that it fills only this remaining area. In this way, our document windows are not obscured by docked tool windows, and vice versa.

Handling WM_NCACTIVATE message in the owner
Another instance where our docking library needs help from the application is whenever the owner window receives a WM_NCACTIVATE message. Remember earlier we discussed how to keep all tool windows' titlebar activation in sync with the owner window. Now, we need the owner window to let the docking library know whenever it receives a WM_NCACTIVATE message. The owner window will need to do the following:

case WM_NCACTIVATE:
{
DOCKPARAMS dockParams;

dockParams.container = dockParams.hwnd = hwnd;
dockParams.wParam = wParam;
dockParams.lParam = lParam;
return(DockingActivate(&dockParams));
}

We simply fill in a DOCKPARAMS struct (defined in DockWnd.h) with the values we receive from the WM_ACTIVATE message, and also fill in the owner window handle (in DOCKPARAMS container field) and the handle of the window receiving the WM_ACTIVATE (which here, is the owner window). Then we call DockingActivate which takes care of syncing all tool windows' titlebar activation.

Handling WM_ENABLE message in the owner
Another instance where our docking library needs help from the application is whenever the owner window receives a WM_ENABLE message. Remember earlier we discussed how to keep all tool windows' enabled state in sync with the owner window. Now, we need the owner window to let the docking library know whenever it receives a WM_ENABLE message. The owner window will need to do the following:

case WM_ENABLE:
{
DOCKPARAMS dockParams;

dockParams.container = dockParams.hwnd = hwnd;
dockParams.wParam = wParam;
dockParams.lParam = lParam;
return(DockingEnable(&dockParams));
}

This is almost the same as the WM_NCACTIVATE handling, but it concerns the WM_ENABLE message, and we call a function named DockingEnable. DockingEnable takes care of syncing all tool windows' enabled state.

Handling messages sent to standard controls
You'll note that we used a standard Edit control as the content of our tool window. As you should know, an Edit window sends messages to its parent for certain actions. For example, when the end user alters the contents of the Edit control, a WM_COMMAND message is sent with a notification code of EN_CHANGE.

But remember that the parent window is the tool window, and our tool window procedure (dockWndProc) is in the docking library. So how does an application get a hold of that message?

There is a DockMsg field in the DOCKINFO. Into this field, we will stuff a pointer to a function in our application. We do this after we DockAlloc the DOCKINFO as so:

// Allocate a DOCKINFO structure.
if ((dw = DockingAlloc(DWS_DOCKED_BOTTOM)))
{
// Set our own function for the docking library to call.
dw->DockMsg = myMessages;

...

Whenever dockWndProc receives a message that it doesn't handle, such as a WM_COMMAND or WM_NOTIFY, it will call our application function, passing the DOCKINFO of the tool window, as well as the message, WPARAM, and LPARAM parameters.

Here is an example of the function we could add to handle a EN_CHANGE from our IDC_MYEDIT edit control:

LRESULT WINAPI myMessages(DOCKINFO * dwp, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
if (LOWORD(wParam) == IDC_MYEDIT)
{
if (HIWORD(wParam) == EN_CHANGE)
{
// Here we would handle the EN_CHANGE, and then return
// 0 to tell the docking library we handled it.
return 0;
}
}
}
}

// Return -1 if we want the docking library to do default handling.
return -1;
}

Note: Each DOCKINFO can have its own DockMsg function, so you do not need to worry about control ID conflicts between tool windows.

Multiple child windows inside a tool window
Above, we used a single Edit control to fill the client area of the tool window. But what if we would like several controls inside the tool window, for example, an Edit control as well as a push button labeled "Clear" which clears the text from the Edit control?

This is entirely possible, but there are a couple of prerequisites. First, when we create the Edit and button controls, we must make both of them children of the tool window. Secondly, we must provide a function that will resize and reposition the controls, and stuff a pointer to this in the DOCKINFO's DockResize field after we DockAlloc the DOCKINFO.

// Allocate a DOCKINFO structure.
if ((dw = DockingAlloc(DWS_DOCKED_BOTTOM)))
{
// Set our own functions for the docking library to call.
dw->DockMsg = myMessages;
dw->DockResize = myResize;

...

When the docking library calls our function, it passes the DOCKINFO for the tool window, as well as a RECT that encompasses the area that we need to fill. Here is an example of a function we could add to resize and reposition the edit and button controls so that the button stays near the bottom border of the tool window, and the edit control fills the rest of the area:

void WINAPI< myResize(DOCKINFO * dwp, RECT * area)
{
HWND child;

// Position the button above the bottom border
child = GetDlgItem(dw->hwnd, IDC_MYBUTTON);
SetWindowPos(child, 0, rect->left + 10,
rect->bottom - 20, 50, 18, SWP_NOZORDER|SWP_NOACTIVATE);

// Let the edit fill the remaining area
child = GetDlgItem(dw->hwnd, IDC_MYEDIT);
SetWindowPos(child, 0, rect->left, rect->top,
rect->right - rect->left, (rect->bottom - rect->top) - 22,

SWP_NOZORDER|SWP_NOACTIVATE);
}

Closing a tool window
When an owner window is destroyed, all of its tool windows are also automatically destroyed (except if you use the DWS_FREEFLOAT style. In that case, your owner window should handle WM_DESTROY and call DockingDestroyFreeFloat). The docking library will normally free the DOCKINFO for each tool window destroyed.

If you wish to manually close a tool window, you simply call the Windows API DestroyWindow, passing the handle to the desired tool window. Again, the docking library will normally free the DOCKINFO.

If you wish to override the docking library's default behavior of freeing the DOCKINFO, then you must write your own function, and stuff a pointer into the DOCKINFO's DockDestroy field. The docking library will call this function (passing it the DOCKINFO) whenever the tool window has been destroyed. It is your responsibility to eventually free the DOCKINFO by passing it to DockingFree. One use for this is to keep a DOCKINFO allocated (for a given tool window) throughout the lifetime of your application. You will reuse this same DOCKINFO with DockingCreateFrame each time the end user reopens that tool window. Because the DOCKINFO stores the last size and position of the tool window, this means that the tool window will reappear where it was located right before it was previously destroyed. You will then free the DOCKINFO only when your application is ready to terminate.

Saving/Restoring a tool window size/position
Each time that you run your application, you will normally want to restore the same sizes and positions for the tool windows that the end user set upon the last time your application was run. The docking library has two functions to help save and restore tool window positions. The data is saved to the Windows registry under some key of your choosing. The size/position of each tool window is saved separately as values under that key.

Before you free a tool window's DOCKINFO, you should first create/open some registry key of your choosing in order to save that tool window's settings. Then, you will pass the DOCKINFO, and a handle to this open key, to DockingSavePlacement. The docking library will save that tool window's settings. Here is an example of how we could save the settings of a tool window under the registry key "HKEY_CURRENT_USER\Software\MyKey\MyToolWindow":

HKEY hKey;
DWORD temp;

// Open/Create the "Software\MyKey\MyToolWindow" key under CURRENT_USER.
if (!RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\MyKey\\MyToolWindow",
0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, &temp))
{
// Let the docking library save this tool window's settings.
DockingSavePlacement(dw, hKey);

// Close registry key.
RegCloseKey(hKey);
}

Whenever your program runs, it should restore those settings by calling DockingLoadPlacement right after DockAlloc'ing the DOCKINFO for that tool window. Here is an example of restoring the previously saved settings:

// Allocate a DOCKINFO structure.
if ((dw = DockingAlloc(DWS_DOCKED_BOTTOM)))
{
HKEY hKey;

// Open the "Software\\MyKey\\MyToolWindow" key under CURRENT_USER.
if (!RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\MyKey\\MyToolWindow",
o, KEY_ALL_ACCESS, &hKey))
{
// Let the docking library restore this tool window's settings.
DockingLoadPlacement(dw, hKey);

// Close registry key.
RegCloseKey(hKey);
}

...

Note: If there are no previously saved settings for the tool window, then none of the DOCKINFO fields are altered