rulururu

post CompareNoCase does not work as expected

July 30th, 2008

Filed under: C++, MFC — Kai @ 11:18 am

Recently I found a program of mine doing all kind of unexpected things because of CString::CompareNoCase() not returning what I expected it to return.

My application usered names containing the characters: é, è, à. Somewhere in my app I used the given name to search the corresponding object in a list. Since the given names had to be (case insensitive) unique I stored those names in uppercase format.

Afterwards I compared (in a case insensitive way) the given name with the uppercase name of each object in the list.
And that went unexpectedly wrong! Look at the following code snippet:

CString strText("élève - à la façon - château");
CString strTextUpper(strText);
strTextUpper.MakeUpper(); // Upper case the original 
 
int match = strTextUpper.CompareNoCase(strText); //does not match

For CString::CompareNoCase I read:

Compares this CString object with another string using the generic-text function _tcsicmp. The generic-text function _tcsicmp, which is defined in TCHAR.H, maps to either _stricmp, _wcsicmp, _mbsicmp depending on the character set that is defined at compile time. Each of these functions performs a case-insensitive comparison of the strings, and is not affected by locale.

While digging deeper in MSDN I discovered the existence of CString::CollateNoCase for which I read:

Compares this CString object with another string using the generic-text function _tcscoll. The generic-text function _tcscoll, which is defined in TCHAR.H, maps to either stricoll, wcsicoll, or _mbsicoll depending on the character set that is defined at compile time. Each of these functions performs a case-insensitive comparison of the strings, according to the code page currently in use.

This knowledge led me to two workarounds:

  • Use CString::CollateNoCase instead of CompareNoCase.
  • Although the help says it works independent of the locale, before calling CString::CompareNoCase set (the LC_CTYPE part of the) the locale. e.g.:
    setlocale(LC_CTYPE, "french-belgian");

Finally my confidence in MFC’s CString comparison functions isn’t great after I discovered this. And that’s why I wrote my own ever-working workaround:

int MyCompareNoCase(LPCTSTR str1, LPCTSTR str2)
{
  CString strTmp1(str1);
  CString strTmp2(str2);
  strTmp1.MakeUpper();
  strTmp2.MakeUpper();
  return strTmp1.Compare(strTmp2);
}

post Starting an application minimized without flickering

June 20th, 2008

Filed under: C++, MFC — Kai @ 11:31 am

To start an application minimized (e.g. appearing directly in the tray area), the common way to do this is calling ShowWindow(SW_HIDE) as soon as possible, preferably directly after the call to CDialog::OnInitDialog().

This surely does the trick, but every now and then, you will see your main dialog window flickering for a fraction of a second before it is finally hidden or sent to the tray area.

This is because the basic window has already been created when CDialog::OnInitDialog() returns, and its default behaviour is to appear on screen (in fact, this is what windows were made for).

Maybe in a few years, when computers operate much more faster than they do now, you’ll get rid of this annoying flickering automatically, because your Operating System will then hide your window before you even start the corresponding application.

But for now, just add this OnWindowPosChanging() handler to your application:

void CTestDlg::OnWindowPosChanging(WINDOWPOS* lpwndpos) 
{
    if(m_bDlgVisible == false)
        lpwndpos->flags &= ~SWP_SHOWWINDOW;
 
    CDialog::OnWindowPosChanging(lpwndpos);
}

You can also declare an optional boolean member variable (m_bDlgVisible in this example), which is initialized to true in the constructor and gets toggled in the corresponding SendToTray() and RestoreFromTray() functions. This way, I can track the current window state (IsWindowVisible() would also work) for different purposes.

post Standard library conflicts with MFC

May 5th, 2008

Filed under: C++, MFC — Kai @ 9:56 am

Often includes from the standard library conflicts with the MFC library. Microsoft can’t do anything about it because of backward compatibility. The conflict are the functions min() and max(). The only thing that is done is to check for a preprocessor value NOMINMAX. If you define this for the whole project, conflicts should be avoided.

It is done in the Project | Settings, and the tab C/C++. Add the NOMINMAX definition to the “Preprocessor definitions” area.

The problem is caused by conflicting definitions of min and max. Min and max are defined as macros in Windef.h as follows:

#ifndef NOMINMAX
 #ifndef max 
 #define max(a,b)  (((a) > (b)) ? (a) : (b))
 #endif
 
 #ifndef min
 #define min(a,b)  (((a) < (b)) ? (a) : (b))
 #endif
#endif  /* NOMINMAX */

post Frequently done wrong - don’t also do it!

February 19th, 2008

Filed under: C++, MFC — Kai @ 4:39 pm

I’d like to tell you about tiny mistakes I often mention when reading code. All of them are not dramatic but they stay in your code for years and one time something goes wrong they’re hard to discover. There are many things that can quickly be done wrong - that’s the reason why I restrict my post to C++ / MFC related mistakes.

  • A widespread misbelief is that this usage of _tprintf and CString is correct:
  • CString strWord = _T("another");
    _tprintf(_T("One string placed in %s"), strWord);

    There’s only one reason why this doesn’t report an error:
    CString has size of 4 byte and points on a TCHAR array.

    This will fail when inheriting an own string class from CString class in which we override a virtual method from CString class. You’ll get problems when CString grows up from 4 to 8 bytes because it’s now holding two pointers: One to a vtable and one to the TCHAR array. _tprintf doesn’t know what to with the object on the stack and might crash or just report nonsense.

    As you can see a small change in your project can lead to lots of errors which are difficult to reproduce.

    How to do it right? There are three solutions - it’s just a manner of taste which one you prever:

    CString strWord = _T("another");
    _tprintf(_T("One string placed in %s"), static_cast<LPCTSTR>(strWord));

    or

    _tprintf(_T("One string placed in %s"), strWord.GetString());

    or

    _tprintf(_T("One string placed in %s"), strWord.GetBuffer(0));
    strWord.ReleaseBuffer();

    First solution is a static cast to an LPCTSTR (which is simply a char*).

    Second is the usage of CString::GetString() method which returns a PCTSTR.

    Third is save for unicode: CString::GetBuffer() returns a TCHAR* or for unicode WCHAR*
    Forgetting to call CString::ReleaseBuffer() can cause problems very difficult to debug as it releases the lock on CString’s inner buffer.

  • CString.Format & printf
  • When using CString.Format with Unicode strings, please be aware that if you are using %s the class expects the parameter to be UNICODE, this means this does not work -

    CString strString;
    strString.Format (_T("Output: %s"), myClass.GetOutput(var, VARIABLE)) ;

    For correct programming your options are:

    CString strString;
    strString.Format (_T("Output: %s"), CString(myClass.GetOutput(var, VARIABLE))) ;

    or for non-MFC usage of A2T makro:

    CString strString;
    USES_CONVERSION;
    strString.Format (_T("Output: %s"), A2T(myClass.GetOutput(var, VARIABLE))) ;

    Again be careful to use the correct character set with printf.

  • Wrong cast from void*
  • You often see a unsave cast from void* to another type that is often not just done that way because of lazyness but caused by nescience of probably occuring errors.

    char c = 0;
    char* p = &c;
    void* q = p;
    int* pp = q;

    This works but is dangerous because when decrementing *pp memory will get overriden even thougth pp is an integer.

    *pp--; //dangerous!

    Doing a c-style or C++ cast avoids big problems ’cause it explecit tells the compiler what type will be written into the other.

    int* pp = (int*)q;
    int* pp = static_cast<int*>(q);

    It’s very common to assign the result of a cast to a suitable pointer:
    In C++, use the typesafe new instead of malloc() operator because it can’t alloc the wrong size of memory:

    typedef std::complex<double> d;
     
    d* q = new d(1,2); // will throw bad_alloc if memory is exhausted
    if (*q == 1) { 
    // go on... 
    }

    new throws a bad_alloc exception if a memory error occurs.

  • Errors due gets()
  • Using the gets() function is bad! Nevertheless you often come accross it’s usage…Fortunatelly it just concerns console apps.

    You’d just use this function in exceptional cases - actually it should no more get used apart from having a really good reason to do the opposite. It does not know how many characters can be safely stored in the string passed to it. Thus, if too many are read, memory will be corrupted. Many security bugs that have been exploited on the Internet use this fact! Use the fgets() function instead (and read from stdin).

All those mistake are not just things beginners do. Also lots of other programmers, even experienced ones, make rather large error in their reasoning. Those mistakes are for the reason that a compiler can’t detect them difficult to detect. They may run in your project for years without complaining.

Of course there are many more things that can be do wrong without any knowledge of it but having heard of some frequently done wrong things might help to prevent them. At least I hope so!

post Data binding with WPF

January 28th, 2008

Filed under: .NET, C++, General Programming, MFC, WPF — Kai @ 2:50 pm

Today I had a close look at the Data binding possibilities provided by the Windows Presentation Foundation.

Before telling you more about that I’d like to show you good-old know alternatives and then compare them to WPF’s solution.

MFC with C++

DDX (Dialog Data Exchange) and DDV (Dialog Data Validation) are two great tools that allow us to easily set and access the values of certain MFC controls.

DDX allows us to link the value of a variable to a control. To use some DDX functions, you should override the DoDataExchange function of a dialog:

afx_msg void DoDataExchange(CDataExchange*);

The function itself can look like this, it just binds the “resource” to a member variable.

afx_msg void DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, TXT_NAME, m_strName);
}

The first line calls the base class, the seconds calls that function:

void DDX_Text(CDataExchange* pDX, int nIDC, CString& value);

If we want to set the value of the controls, we can call the UpdateData passing FALSE to the UpdateData function. On the other hand, if we want to retrieve the value of the control into our CString variable, we would pass TRUE to the UpdateData function.

BOOL CWnd::UpdateData(BOOL bSaveAndValidate)
{
	CDataExchange dx(this, bSaveAndValidate);
	try
	{
		DoDataExchange(&dx);
	}
	catch(CUserException e)
	{
		return FALSE;
	}
	return TRUE;
}

For other controls such as the list box, radio button and check box, we would use the similar DDX functions DDX_CBString, DDX_Radio and DDX_Check respectively.

When using DDX for the exchange of data between controls and variables we easily can validate them.

void CSample::DoDataExchange(CDataExchange* dx)
{
	CDialog::DoDataExchange(dx);
	//{{AFX_DATA_MAP(CDlg)
	...
	DDX_Text (dx, TXT_AGE, m_iAge);
	DDV_MinMaxInt(dx, m_iAge, 0, 150);  // <- validation
	. . .
	//}}AFX_DATA_MAP
}

The DDX_Text() call transfers the contents of the edit control into the m_age data member, converting it into an integer. After that is has to pass validation.

DDV_MinMaxInt looks as follows:

void DDV_MinMaxInt(CDataExchange* dx, int value, int min, int max)
{
	if (value < min || value > max)
	{
		AfxMessageBox("Please enter a value between ...");
		dx->Fail();
	}
}

If the value doesn’t yield the condtions of the validation method a error message is shown and Fail() is invoked on the data exchange object.

Windows Forms with C#

Data binding in C# provides a more advanced way for developers to create a read/write link between the controls on a form. It’s often used for binding forms to complex data structures such as SQL databases. Windows Forms data binding allows you to access data from databases as well as data in other structures, such as arrays and collections.

Each Windows Form has at least one BindingContext object that manages the access to and from the controls. For example if you add use a textbox control to a form and bind it to a column of a table (e.g. “tblAddress.Name”) in a dataset(e.g. called “dsAddr”), the control communicates with the BindingContext object for that form.

txtname.DataBindings.Add("Tom Morello",Adresses,"tblAddress.Name");
CurrencyManager cm = (CurrencyManager)this.BindingContext[dsAddr,"tblAddress"];
foreach(object s in cm)
{
Console.WriteLine(s.ToString());
}

The CurrencyManager is used to keep data-bound controls synchronized with each other.

That way of data binding is very useful when dealing with collection controls such as ListView or ListBox.
It’s common practice to bind them to any kind of Array, Collection or List.

A quick and clever way is the usage of data bindings in C# with ADO.NET Data Objects because those data structures are suitable for binding to.

DataTables such as GridView provide DataSource and DataMember properties:

gv1.DataSource = dsAddr;
gv1.DataMember = "tlbAddresses";

other appropriate objects are:

  • DataColumn
  • DataView
  • DataSet
  • DataViewManager

You also can bind controls to other controls. Binding data of a ComboBox to a ListBox follows the same model and is no problem.

DataBinding with WPF

Now we come to the point the article originally was about.
When working with collection-like data sources in WPF, you can read properties of the selected item by writing your binding expressions directly into the XAML (eXtensible Application Markup Language) code that defines the structure of every WPF-GUI.

<DockPanel DataContext="{x:Static Persons}">
  <TextBlock DockPanel.Dock="Top"
             Text="{Binding Lastname}" >
  <ListBox ItemsSource="{Binding}"
           IsSynchronizedWithCurrentItem="True" />
</DockPanel>

In that example the source is an array of persons names and the ListBox should show the lastname of every person the array contains.
When WPF encounters a binding to a non-existent property on a collection, WPF has a fallback strategy: it looks for the named property on the current item.

If source and destination are both collections you can do

Property="{Binding /}"

to bind them to each other.

That’s nice behavoir but just the beginning of some great ideas.
There are several things you can define in the binding object:

  • OneWay binding causes changes to the source property to automatically update the target property, but changes to the target property are not propagated back to the source property.
  • TwoWay binding causes changes to either the source property or the target property to automatically update the other. This type of binding is appropriate for editable forms or other fully-interactive UI scenarios.
  • OneWayToSource is the reverse of OneWay binding; it updates the source property when the target property changes.
  • OneTime binding causes the source property to initialize the target property, but subsequent changes do not propagate. This means that if the data context undergoes a change or the object in the data context changes, then the change is reflected in the target property. This type of binding is appropriate if you are using data where either a snapshot of the current state is appropriate to use or the data is truly static.

The UpdateSourceTrigger property of the binding determines when binding should appear.

  • LostFocus (default e.g. for TextBox)
  • PropertyChanged
  • Explecit (when calling UpdateSource)

Due data binding you could for example bind a textbox value to its background color. The property of type string in the binding source object is connected to a Color property of type Color.

IValueConverter makes it possible:

public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

To ensure that the user has entered the expected information we user input validate it with some logic.
At first we add a validation in description section of the GUI.

<TextBox Name="Birthday" 
    Style="{StaticResource textStyleTextBox}">
  <TextBox.Text>
    <Binding Path="BDay" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
        <ExceptionValidationRule />
      </Binding.ValidationRules>
    </Binding&gt;
 </TextBox.Text>
</TextBox>

The ValidationRules property takes a collection of ValidationRule objects. When the user enters a value that cannot be converted to an the expected type, an exception is thrown, causing the binding to be marked as invalid.

You also can define your own rule:

<Binding.ValidationRules>
                <src:MyRule />
            </Binding.ValidationRules>
class MyRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        DateTime date;
        try
        {
            date = DateTime.Parse(value.ToString());
        }
        catch (FormatException)
        {
            return new ValidationResult(false, "Invalid date!");
        }
        if (DateTime.Now.Date < date)
        {
            return new ValidationResult(false, "You cannot be born in the future.");
        }
        else
        {
            return ValidationResult.ValidResult;
        }
    }
}

You also might visualize a wrong input for the user.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
  <Setter Property="Foreground" Value="#fff000" />
  <Setter Property="MaxLength" Value="10" />
  <Style.Triggers>
    <Trigger Property="Validation.HasError" Value="true">
      <Setter Property="ToolTip"
        Value="{Binding RelativeSource={RelativeSource Self},
                        Path=(Validation.Errors)[0].ErrorContent}"/>
    </Trigger>
  </Style.Triggers>
</Style>

Now a tooltip appears when the HasError property of Validation is true. You can define a so called Error Template that is used for invalid inputs, if you don’t the default is used.

There are enough and to spare features you can use when working with bindings in WPF, but telling about all of them would carry things too far.

Comparison and forecast

The way MFC programmers use is clear and easy to use. It’s not very casual to handle but it can be extented unique for the project’s necessities.

Win Forms are mostly the same - also the spreading of Win Forms applications on the software market seems to be similar to MFC apps.

From my point of view software development can be really enhanced by technolgies like those provided by the WPF. Also for non-WPF applications its always worth to think about those concepts.

post BOOL and bool

January 18th, 2008

Filed under: C++, MFC — Kai @ 2:49 pm

When using MFC I often wondered about the difference between ‘BOOL’ and ‘bool’.
Especially warnings like that confused me:

warning C4800: 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)

Some time ago I detected that the size of ‘BOOL’ is 4, that’s why I found out the following:

‘bool’ is a built-in C++ type while ‘BOOL’ is a Microsoft specific type that is defined as an ‘int’. You can find it in ‘windef.h’:

typedef int                 BOOL;
#ifndef FALSE
#define FALSE               0
#endif
#ifndef TRUE
#define TRUE                1
#endif

The only possible values for a ‘bool’ are ‘true’ and ‘false’, whereas for ‘BOOL’ you can use any ‘int’ value, though ‘TRUE’ and ‘FALSE’ macros are defined in ‘windef.h’ header.

If you use the ’sizeof’ operator, it will yield 1 for ‘bool’, though according to the standard the size of’ bool’ is implementation defined, and 4 for ‘BOOL’, on 32-bits platform, where ’sizeof(int)’ is 4 bytes. If the size of ‘int’ changes to 8 bytes on 64-bits platforms, ’sizeof(BOOL)’ will yield 8 instead.

‘BOOL’ was used by Microsoft long before ‘bool’ was actually added to the C++ language, but it has nothing to do with MFC. Many Windows API returns a ‘BOOL’ to indicate success or failure.

ruldrurd
Powered by WordPress, Content and Design by Kai Bellmann
Entries (RSS) and Comments (RSS)