Wednesday, May 24, 2006
Monday, May 22, 2006
Destructor Bug
HEADTRL.OBJ : error LNK2001: unresolved external symbol "public: __thiscall TCIOControl::~TCIOControl(void)" (??1TCIOControl@@QAE@XZ)
So I look in the offending DLL with Depends.exe, and I find that the mangled name for the destructor differs by one character:
??1TCIOControl@@UAE@XZ
The U stands for virtual. The DLL that exports the symbol is a Debug DLL and the one importing is a Release DLL. So, why is the Release DLL looking for the non-vitual version of the exported class' destructor?
It turns out that whoever implemented the class pulled one of those #ifdef _DEBUG stunts. In Debug mode our imported class is given a base class, while in Release mode it's not.
By default, the VS6.0 compiler declares the destructor as virtual if the class has a base class, but does not otherwise.
So, by explicitly declaring the destructor as virtual (which is always a good idea) we restore Debug/Release binary compatibility.
Calling Conventions
- The order in which parameters are pushed onto stack
- Who is responsible for cleaning up stack (caller or callee)
- Other deatails about register usage during function call
Name Mangling
For example, operator overloading allows a developer to define a single function "foo" with multiple interfaces such as
void foo (int i);
void foo (char i);
While these two declarations have the same name, they are not the entity, and therefore should not be given the same symbolic name. The compiler must give each entity a unique name.
The name that a compiler chooses to give to a programming entity in order to preserve its symbolic uniqueness is called a mangled name or decorated name.
I use Microsoft's Visual Studio 6.0 compiler more often than any other. http://msdn2.microsoft.com/en-US/library/deaxefa7.aspx
Visual Studio 6.0 uses the following characteristics to create a mangled name for a programming entity:
Use Depends.exe to view mangled symbols.
Use Undname.exe to translate mangled symbols back into their human readable form.
@@Uxx - "U" indicates that the entity is declared virtual
@@Qxx - "Q" indicates that the entity is not virtual
"There is currently no standard for C++ naming between compiler vendors or even between different versions of a compiler."
References:
- Wikipedia Entry on Name Mangling (http://en.wikipedia.org/wiki/Name_decoration)
- MSDN Entries (http://msdn2.microsoft.com/en-US/library/deaxefa7.aspx)
IMPLEMENT_DYNAMIC / DECLARE_DYNAMIC
See this article on MSDN for an overview on adding run-time class information and other features to your CObject derived class.
DECLARE_DYNAMIC declares the appropriate methods, and thus should be used in your class definition:
class CPerson : public CObject
{
DECLARE_DYNAMIC( CPerson )
// rest of class declaration follows...
};
IMPLEMENT_DYNAMIC implements the methodes, and thus should be used outside of your class definition:
IMPLEMENT_DYNAMIC( CPerson, CObject )
IMPLEMENT_DYNCREATE enables objects of CObject-derived classes to be created dynamically at run time when used with the DECLARE_DYNCREATE macro.
Debugging Windows
Install VS 6.0 SP5
Install VS 6.0 SP6
Install WinNT Debug Symbols
MFC Symbols can now be debugged
Win32 Symbols are not working
Try debugging under VS 2005... nope
Install "Debugging Tools For Windows"... still no luck
Set _NT_SYMBOL_PATH to
SRV*e:\localsymbols*http://msdl.microsoft.com/download/symbols
Friday, May 19, 2006
Use the pre tag when displaying inline code snippets
Another little note is that when using the div tag, CSS margins and padding is not preserved. I probably just don't understand HTML. Anyway, here are some examples:
Using div:
{
int a = -1
return a;
}
Using pre:
void foo ()
{
int a = -1
return a;
}
Notice that when I use pre my indentation and margines are preserved, while they are discarded when using div.
Well Paired Operations
What's a paired operation? An operation is paired with another operation if invoking one operation requires the other operation to be invoked. Examples:
- malloc / free
- new / delete
- CreateDC / DeleteDC
- MyStartRoutine / MyStopRoutine
- They occur in the same routine (i.e. in the immediate execution scope, not in a child routine)
- They occur in two different routines who have the same degree of separation from a common ancestor
- Others?
void foo ()The malloc and free operations are well paired because they occur in the same immediate execution context.
{
void * someData = malloc (10);
// some code //
free (someData);
}
Let's look at an example of a set of poorly paired operations. Aaside from the fact that the following code breaks about a billion other best coding practices it is a surprisingly common anti-pattern!
void * formatSomeMemory()
{
void * tmp = malloc (10);
// do some stuff
return tmp;
}
void foo ()
{
void * someData = formatSomeMemory();
// some code //
free (someData);
}
In this context although the malloc and free operations share the same decendent routine - foo - they have differing degrees of separation and are therefore not well paired.
So, who's fault is this? it's the author of formatSomeMemory()'s fault.
Fundamentally, the problem of poorly paired operations is the result of poorly formed APIs. In this case, the author of the API that included the formatSomeMemory() routine did not provide an appropriate routine for freeing the data that formatSomeMemory allocated. The author of formatSomeMemory() is forcing the author of foo() to write poorly paired code!
In reality, the author of formatSomeMemory() and foo() are probably the same person! The reason that this person wrote a poorly formed API is that they inadvertantly broke an even more fundamental well formed coding principle: The user of an API should not be required to have intimate understandings of the internal framework of the API in order to properly use it.
In many cases, since the user is also the developer, this rule gets broken unconsiously. In our case, since the author of formatSomeMemory() knew that it malloc'ed some memory, he innocently added the free to foo and went on his merry way, not knowing he had just created a debugging pitfall for future developers.
So, what might be some alternatives to the malformed API.
Option 1 - Make user responsible for well pairing operations:
void * formatSomeMemory(void * mem, int size)
{
// do some stuff to the memory
// (perhaps even reallocing it)
return tmp;
}
void foo ()
{
void * someData = malloc(10);
someData = formatSomeMemory (someData, 10);
// do stuff
free (someData);
}
Option 2 - Handle well pairing behind the scenes:
void * formatSomeMemory()
{
void * tmp = malloc(10);
// do some stuff to the memory
return tmp;
}
void * freeFormattedMemory (void * tmp)
{
free (tmp);
}
void foo ()
{
void * someData = formatSomeMemory ();
// do stuff
freeFormattedMemory (someData);
}
In both these examples the paired operations malloc amd free free are now well paired because in both cases they share the same degree of separation from the root routine foo.
I personally prefer Option 1 to Option 2 simply because Option 2 requires good documentation to work properly. I hate documentation, don't you? :)
Thursday, May 18, 2006
Windows Device Contexts
- An application cannot use the ReleaseDC function to release a DC that was created by calling the CreateDC function; instead, it must use the DeleteDC function. ReleaseDC must be called from the same thread that called GetDC.
- The destructor of CDC will call DeleteDC on the device context associated with the CDC. In general, you don't need to call CDC::DeleteDC yourself.
NetBeans Isuues
The "/wizard.inf" issue is a known bug. It has to do with an old version of sax.jar residing in the /jre/lib/endorsed directory not being compatible with InstallShield. See the Netbeans.org bug reference.
I receive this error when reinstalling Netbeans 4.0 or installing Netbeans Plugins.
Temporarily renaming the entire endoresed directory to endoresed.bak during the install and then renaming fixed the problem.
Thursday, May 11, 2006
Wednesday, May 10, 2006
Tuesday, May 09, 2006
Spectrum Build Notes
Creating a Custom .NET Property Editor
This How To explains how to create a custom editor for a property who's class cannot be edited by one of the built in VisualStudio Property Page editors.
In general, a public property can be modified "at Design Time" by using Visual Stuio .NET's Property Page.
public class MyComponent : Component
{
private string myString;
public string MyString
{
get { return myString; }
set { myString = value; }
}
}
Concider also that this component MyComponent
were added to a user control, say MyControl
. Using the Visual Studio control designer an instance of the component would appear at the bottom of the designer window.
When designing a control, the user may manually set the values of properties of the components and controls contained in the control being designed. This is done by right clicking on a component or control, say MyComponent
, and and selecting Properties. The Property Page window will show all the public properties for the control.
In our case, the Property Page would show MyString
and the user could then proceed to edit the property's value simply by entering a value next to the entry int the Property Page.
Thursday, May 04, 2006
Windows Transparency
"GDI+ offers partial transparency and anti-aliasing for drawing operations, but only when performed within the confines of a single control." - MSDN
"With Win32, and the associated GDI and GDI+ drawing APIs, each control in the UI owns its part of the window exclusively. Windows are carved up by the controls into a set of disjointed regions that are usually, but not necessarily, rectangular. If you pick any single pixel on a window, there will be exactly one control responsible for drawing that pixel." - MSDN
"WPF breaks free from this limitation. No single control or UI element has exclusive ownership of any part of the window in a WPF application. You can create partially transparent UI elements, or controls that are not visually constrained to their logical rectangle. For example, it's possible for a control to cast a shadow that falls outside of its logical region." - MSDN
"In effect, the whole window becomes a single drawing surface that each of the parts of the UI contribute to." - MSDN
Coding Quotes
interfaces
in Java or .NET, in addition to being ABCs, interfaces
also have no data. I have come to appreciate that if you use abstract base classes and eliminate any data from them, then a lot of the difficulties of multiple inheritance that I wrote about just go away, even in C++." - Scott Meyers
Wednesday, May 03, 2006
Memory, memory
The reason for this is that if you do that, then the behavior is technically undefined because there is no guarantee that new would internally use malloc, or that delete would internally use free.
Also, never mix scalar new with vector delete. This will result in memory leaks.
delete t;
I'm not sure that's right. I think delete t is OK.
Or worse:
delete[] t;
Why? Because the pointer returned by the new[] operator is not the start of the allocated memory but instead points to Test[0]. Huh? Isn't Test[0] the beginning of the memory block? N0! When using scalar new the complier keeps track of the number of items that have been allocated. This makes complete sense. However, we forget that part of the allocated memory includes this additional word of information! So in short - Vector New'd memory does not look like Scalar New'd memory!
It's a little more clear when we take a look at the vector deleting destructor (pseudo-code):
void MyClass::vector deleting destructor (int flags)
{
if (flags & 2) // vector destruct
{
size_t* a = reinterpret_cast(this) - 1;
size_t howmany = *a;
vector destructor iterator (p,
sizeof(MyClass),
howmany,
MyClass::~MyClass);
if (flags & 1) // vector delete
{
operator delete(a);
}
}
else // scalar destruct
{
this->~MyClass(); // destruct one
if (flags & 1) // scalar delete
{
operator delete(this);
}
}
}
"If you have to use strcpy, use strncpy instead!"
char buf_b[256];
...
strcpy (buf_a, buf_b);// This is really bad!
The problem here is that the developer has forgotten that when using strcpy one must be aware of the size of the buffers being copied. By makeing a rule of always using strncpy instead of strcpy,