Tuesday, December 11, 2007

What's wrong with Monitor::Enter()

Suppose we have the following variable we'd like to set as a state and we'd like to make it thread safe (at most one thread is allowed to access the variable):

bool workQueued;

Now if we write something like this, it's not gonna work:

Monitor::Enter(__box(workQueued));
try
{
workQueued = true;
}
__finally
{
Monitor::Exit(__box(workQueued));
}

Why it will not work? The problem is, after workQueued is boxed, the value is not store anywhere. So the second thread reaches here will execute Enter statement with another object value.

To make it work as desired, we need to create a object whose only purpose is to act like a lock:

Object* lock;

Then, we feed this object to Enter and Exit method:

Monitor::Enter(lock);
try
{
workQueued = true;
}
__finally
{
Monitor::Exit(lock);
}

Wednesday, December 5, 2007

LINK : warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/INCREMENTAL:NO' specification

What the heck?! If you've spent more than ten minutes just trying to find where to set EDITANDCONTINUE property in VC++ project property page, you are not alone. It can be set through: (Under Project Property Pages) Configuration Properties -> C/C++ -> General -> Debug Information Format.

OK. Now I've set it NOT to be /ZI and disabled incremental link, why the darn VS still complain the same warning. Remember that no matter how bizarre a problem seems to be, computer always works as expected. Why don't you check the project properties of other projects that this project depends on?

Monday, December 3, 2007

Visual Studio got stuck when opening sln file

Sometimes VS may get stuck when we double-click a .sln file. We can see the file name on which it got stuck. But even if we try to rename that file or replace with another one with the same name, it may still not work. In this case, try to delete the corresponding .suo file.

Friday, November 30, 2007

Problem caused by dll loading mistake

It is very common that we have two versions of dll: somedll.dll(for Release build) and somedll_d.dll(for Debug build). Because of the dll dependancy complications, if these two versions were load at the same time, it may not cause build errors. However, something wrong can happen during the run time.

For example, if there is a global variable in the source file whose output is somedll.dll/somedll_d.dll, that global variable may be initialized twice (once during the load time of somedll.dll and once during the load time of somedll_d.dll). The client code then will get confused on which of the two references to use. But the fact is, it should be only one instance of that global variable existing.

Thursday, November 29, 2007

Time-saving Tips

1. Sometimes we may have weird link errors. Some of the errors will be gone if we try to delete the debug/release folder (clean) and rebuild the project.

2. If an environment involves several executables and dlls working together, their build version should match exactly. Otherwise, some bizarre behaviors may happen.

Mixed DLL Loading Problem

When using managed code inside unmanaged dll entry point, it may cause dll loading problem. For more information, follow the link below.

http://msdn2.microsoft.com/en-us/library/aa290048(vs.71).aspx

Monday, November 26, 2007

Be Careful of Naming

Sometimes bad naming may cause compile errors.

I named a managed enum "Event" (__value enum Event) inside a managed class "SopExt" (__gc class SopExt). In this class, I had a private field whose type is the managed enum "Event" and a pair of getter/setter of it. Later, I found that the compiler sometimes had trouble to recognize the expression like this: "sopExt->Event". The compiler sometimes also didn't recognize the type "SopExt::Event".

Obviously, neither expression is wrong. But apperently, the name of the managed enum "Event" is not a good name. Just by changing "Event" to "StreamEvent" (without even changing the name of getter/setter), the compiler error is gone. The reason that caused the compiler error is not clear. It might be that the name "Event" is too general so it confused the compiler? Anyway, good naming is a very important element in programming.

Friday, November 16, 2007

Examine std::vector in VS watch window

vector->_Myfirst[offset] or vector->_Myfirst, count

Thursday, November 15, 2007

Why using managed extension is a pain in the ***?

1. We simply cannot include unmanaged objects in a managed class. We can only include pointers to unmanaged objects.

Tuesday, November 13, 2007

__gc Array Aggregate Initialization

__gc Array Aggregate Initialization doesn't work inside a namespace (except that the method is a member of a gc class). We have to initialize each element one by one.

namespace Sample
{
void ParseString(String* str)
{
...
Char splitter[] = { ',' }; // This will cause link error
// Use the following instead
// Char splitter[] = new Char[1];
// splitter[0] = ',';
...
}
}

Wednesday, October 17, 2007

Switch between Windows debuggers

The key "Debugger" under registry:
\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
specifies which debugger will be invoked in case of application unhandled exceptions are thrown.

Usually, if Visual Studio is installed, the default debugger will be Visual Studio JIT Debugger("C:\WINDOWS\system32\vsjitdebugger.exe" -p %ld -e %ld); But if we want to use Dr. Watson(drwtsn32 -p %ld -e %ld -g) to debug (creating log and crash dump), we can set the key name of Dr. Watson to "Debugger" and VS JIT Debugger to something else.

Wednesday, September 12, 2007

About Copy Constructor

In the example below, push_back will call the copy constructor of Family implicitly. In this case, it's a deep copy. If we comment out the copy constructor, a default one will gets called, which is a shallow copy.

If we have at least one complex object in a class, we should think about writing our own copy constructor. (choose between deep copy and shallow copy) If there's no complex object in the class, a default copy constructor will do since there's no difference between deep and shallow copy in this case. Remember that if we provide our own copy constructor, we also need to write the destructor so the complex objects can be deallocated.

class Person
{
public:
int age;
Person()
{
age = 0;
}
};

class Family
{
public:
Person* houseHold;
int aptNum;
Family()
{
houseHold = 0;
aptNum = 0;
}
Family(const Family& family)
{
this->houseHold = new Person(*family.houseHold);
this->aptNum = family.aptNum;
}
};

main()
{
vector vecF;
Person* p = new Person();
p->age = 30;
Family* f = new Family();
f->houseHold = p;
f->aptNum = 100;
vecF.push_back(*f);
delete f;
delete p;
cout <<
vecF.at(0).houseHold->age << endl << vecF.at(0).aptNum << endl;
getchar();
}

Tuesday, August 21, 2007

Tuesday, August 14, 2007

Get used to managed C++ style definition

Sometimes the ugly managed c++ grammar can cause us much pain.

For example, what is the correct way of defining a __wchar_t gc array?

Wrong answers:
__wchar_t charArr[] = {':', '\\'};
__wchar_t[] charArr = {':', '\\'};
__wchar_t __gc[] charArr = {':', '\\'};
__wchar_t __gc charArr[] = {':', '\\'};
__wchar_t __gc[] charArr[] = {':', '\\'};
JUST_LET_ME_DEFINE_A_DAMN __wchar_t __gc charArr[] = {':', '\\'};

Only this one is right:
__wchar_t charArr __gc[] = {':', '\\'};

Friday, July 20, 2007

What I've learn from COOP

1. Enterprise-scaled project. More stuff to concern: multi-threading, better use on resources(memory etc.), structure flexibility, code reusability, refactoring.

2. Testing. Software engineering knowledge and experience.

3. Documenting code and project. Also improved writting skills.

4. Interpersonal skills. Working with peers and supervisors. Presentation. Book club.

Thursday, July 12, 2007

Avoid Pitfalls

The following code snippet is error-prone:

for (int i = 0; i <>SelectedIndices->Count; i++)
{
/* do something */
}

Why? listView->SelectedIndices->Count may change in the loop. For example, if we delete one item in listView->SelectedIndices collection a time, listView->SelectedIndices->Count will be 1 less than the the value in last loop.

Another common pitfall is, when deleting items in a collection, items after the deleted item are shifted automatically. Therefore, we are to delete some items one by one in a collection according to their initial indices, we need to delete them backwards.

Friday, July 6, 2007

Making Textbox control autoscrollable

If text is appended every time, we can do this way:

txtBx->AppendText(strToAppend);
txtBx->Focus();
txtBx->ScrollToCaret();

If text is completely loaded from the beginning to the end every time, it can be done this way:

txtBx->Text = strContent;
txtBx->Focus();
txtBx->SelectionLength = 0;
txtBx->SelectionStart = txtBx->Text->Length;
txtBx->ScrollToCaret();

In either case, Focus method is indispensable.

Thursday, July 5, 2007

Implementing indexers in VC++

Unlike C#, C++ doesn't support indexers directly. However, we can achieve the same goal through a different way:

[System::Reflection::DefaultMemberAttribute(S"Item")]
public __gc class PlayoutLists : public ICollection
{
public:
ArrayList* listArray;
public:
PlayoutLists()
{
listArray = new ArrayList();
}
public:
__property PlayoutList* get_Item(int index)
{
return dynamic_cast(listArray->get_Item(index));
}
public: void CopyTo(Array* a, int index)
{
listArray->CopyTo(a, index);
}
public: __property int get_Count()
{
return listArray->get_Count();
}
public: __property Object* get_SyncRoot()
{
return listArray->get_SyncRoot();
}
public: __property bool get_IsSynchronized()
{
return false;
}
public: IEnumerator* GetEnumerator()
{
return listArray->GetEnumerator();
}
public: void Add(PlayoutList* newList)
{
listArray->Add(newList);
}
};

get_Item (and set_Item if you want to be able to set the value) property will work the same way as indexers in C#.

Thursday, June 28, 2007

Beware of "==" in VC++

"==" operator of String class may not return desired result. For example, when checking the equality of two Strings:

if (cmbBx_Pos->Text == S"BOL")

Even if the two Strings are the same, it may have a "false" result. To make sure it return the correct result, use this instead:

if (cmbBx_Pos->Text->Equals(S"BOL"))

It doesn't make much sense why the "==" operator failed. Perhaps it was the operator of Object class, so it was comparing two references instead of two Strings.

Updated: when using a .net string literal in VC, always write as: S"hello". "==" operator will return false when comparing "hello" and S"hello". If a project has only .net strings as S"xxx", using "==" operator probably has no problem. If a project deals with different kinds of string, we better off use String::Equals to check string equality.

Thursday, June 21, 2007

Rules That Make Your Programming Life Easier

Rule 1: Life is like an onion: you peel it one layer at a time, and sometimes you weep. So is programming.

Rule 2: Internet is one of your most reliable friends.

Rule 3: Whenever encounter a problem, search on Internet, refer to books and think. Until you know exactly what the problem is, don't ask for help.

Rule 4: Don't bother (ask tech questions etc.) colleagues too often. People are all busy!

Rule 5: Taking notes is very very important. If you have a doubt, read Rule 1 five times.

Tuesday, June 12, 2007

Access form controls freely

Sometimes we want to access form controls outside the normal scope. For example, a callback function in another file need to notify some control so it can update its display. Simply change the control from private to public is not enough, we also need to get the current form instance. According to the article How to get current ApplicationContext instance at run time, we can create a new class MyForm which inherits Form class and set a similar static field _myForm. The static field will always store the first instance of that class, even if that class is initialized multiple times later.

We then use this new class to run the application:

Application::Run(new MyForm());

Later on, whenever we want to get the instance of MyForm, we can do this:

MyForm* myForm = (new MyForm())->_myForm;

Although this method may not deserve the word "pattern", it is interesting to compare it to the Singleton Pattern. Different from Singleton Pattern, we can have more than one instance in this case, but we can freely get the first instance.

Convert String* to char* and backwards

To convert String* to char*, use the following:

using namespace System::Runtime::InteropServices;
char* temp;
temp = (char*)(void*)Marshal::StringToHGlobalAnsi(txtBx_MgmtName->Text);

To convert char* to String*, just use the constructor of System::String.

BTW, most data types in .NET has the ToString method.