致开

2009年1月4日

C++ Common knowledge base

1. Derived classes use the function name to hide the function with the same names in Parent class. This is declared by C++ Language Specification. Actually this is not a good design, so we should try to prevent this happen in our design.

2. Override your "new" operator for your class if necessary. And keep in mind that the overrided "new" operation applies for whole inheritance tree.
class X
{
public:
  
void* operator new(size_t sz); //allocate sz bytes
  void  operator delete(void* p) //free p;
};

3. Inheritance talk.
As we know, you can call the child method through your base class or reference. But you could not call the child method through your base object even this method is overridden in your child class.
Say we have such a piece of code:
class Base
{
public:
    
virtual void Display(){
    cout
<<"base"<<endl;}
};

class Derived : public Base
{
public:
    
virtual void Display(){
    cout
<<"derived"<<endl;};
};
int _tmain(int argc, _TCHAR* argv[])
{
    Base ba;
    Derived de;
    ba 
= de;
    ba.Display();


    Base 
* pba = &de;
    pba
->Display();
    
return 0;
}
Many persons wonder this phenomenon and has a lot of questions. But if you take a look at the assembly code, you can get it clear.
//Base::Display actually is a global function. 
//In Compiling phase, this piece of calling will be regarded as calling the global function.
ba.Display();
0041155A  lea         ecx,[ba] 
0041155D  call        Base::Display (411140h) 

//As we know, the virtual methods in C++ are implemented by virtual table. 
//So this calling will be happen in execution time, which finds the correct method.
pba->Display();
00411568  mov         eax,dword ptr [pba] 
0041156B  mov         edx,dword ptr [eax] 
0041156D  mov         esi,esp 
0041156F  mov         ecx,dword ptr [pba] 
00411572  mov         eax,dword ptr [edx] 
00411574  call        eax  
00411576  cmp         esi,esp 
00411578  call        @ILT+455(__RTC_CheckEsp) (4111CCh) 


posted @ 2009-01-04 16:42 Xiaxi 阅读(471) | 评论 (1)编辑 收藏

2008年12月30日

Concurrency Programming - Write your own ReaderLock and WriterLock

This article is based on the previous articles, since we'll use the mutex and semaphore in our class. This artical will mainly be consisted by the C++ code, not too many words.

#include <windows.h>

class Lock
{
public:
    Lock()
    {
        hMutex 
= CreateMutex(NULL, false"OWNLOCK");
        hSemaphore 
= CreateSemaphore(NULL, 11"OWNSEMAPHORE");
        iReaderCount 
= 0;
    }

    
void AcquireReadLock()
    {
        WaitForSingleObject(hMutex, INFINITE);

        
if(++iReaderCount == 1)
            WaitForSingleObject(hSemaphore, INFINITE);
        ReleaseMutex(hMutex);
    }

    
void ReleaseReadLock()
    {
        WaitForSingleObject(hMutex, INFINITE);
        
if(--iReaderCount == 0)
            ReleaseSemaphore(hSemaphore, 
1, NULL);
        ReleaseMutex(hMutex);
    }

    
void AcquireWriteLock()
    {
        WaitForSingleObject(hSemaphore, INFINITE);
    }

    
void ReleaseWriteLock()
    {
        ReleaseSemaphore(hSemaphore, 
1, NULL);
    }
    
private:
    HANDLE hMutex;
    HANDLE hSemophore;
    
int iReaderCount;
};

Regarding when to use the lock, I'm saying that if the data is shared between threads, any access to this piece of data should be protected, locked. Also using the right type of lock is also necessary, otherwise it will be the bottleneck of your application.

Another issue is that at which granularity to add the lock. My suggestion is that don't add too many locks at one time, which will cause hard to locate the problems if it happens. You should at as few locks as possiable at one time.

posted @ 2008-12-30 21:34 Xiaxi 阅读(212) | 评论 (0)编辑 收藏

2008年12月28日

Win 32 Mulit-Thread Synchronous

At current few applications still use the Single-Thread model. Every application benefits a lot from the multi-threads model: quick UI response, concurrent workflow, etc. But there is a concern that if you could not master the multi-thread well, it would be your nightmare.

In this article, I'll give some introductions to the common sychronous skills that we used in Win32.

1. CRITICAL_SECTION
CRITICAL_SECTION is used to protect your resources. Here the resource includes the memory blocks, files, data structures, etc.
One mind is that CRITICAL_SECTION is not the kernel object. So the cost of using it is less that the mutex kernel object. I think at most cases we should prefer to this choice if you don't want to syschrous between the multiple processes.
So here is the steps that you can use it.
//1. declare a critical_section object
CRITICAL_SECTION cs;

//2. initialize it before use.
InitializeCriticalSection(&cs);

//3. try to enter into the critical section
//if success, this function will return. Otherwise, the calling thread will be forced to sleep.
EnterCriticalSection(&cs);

//do some stuff

//4. leave the critical_section
LeaveCriticalSection(&cs);

//5. if not used any more, delete it.
DeleteCriticalSection(&cs);

One point is that the thread can enter into the critical_section several times after it already enter into it. The only thing that you should be aware of is that you should call the LeaveCriticalSection() same times as calling the EnterCriticalSection.

Here are some suggestions about how to use the critical_section.
   - Do not lock one resource for long time. This may cause other threads waiting and may lead to UI hang.
   - Do not call the Sleep() or Wait...()  during the critical_section.

2. Mutex  - Mutual Exclusion
Mutex is a kernel object and need the user-mode and kernel-mode switch if using it, which means a lot of cost are necessary. But there is a good poing is that you can use the Mutex between sereval processes, which the CRITICAL_SECTION could not provide this funcnality.
//1. Create a mutex using the Win32 API
//   you can also call OpenMutex() to open an existing mutex.
HANDLE hMutex = CreateMutex(NULL, true"MSCAR");

//2. Lock the mutex
//you can call WaitForMultipleObjects() if you want to lock several mutex
HRESULT hr = WaitForSingleObject(hMutex, INFINITE);

//3. got the lock and do something
if(hr == WAIT_OBJECT_0)
{
     
//
}
//4. release the mutex. this can lead to other threads getting the mutex
ReleaseMutex(hMutex)

//5. Close the handle. This should not be forgotten otherwise lead to handle leak.
CloseHandle(hMutex);
 

3. Semaphores
A lot of articles introduces several scenarios about how to use the semaphore, such like a buffer. So here I don't want to take too much. Just show the steps.
//create a semaphore, with the max_count set.
HANDLE hSemaphore = CreateSemaphore(NULL, 05"COUNT");

//the following is two threads running.
//thread 1
{
    
//add some resource
    long lCount;
    ReleaseSemaphore(hSemaphore, 
1&lCount);
}


//thread 2
{
    
//wait for resources. If resource is more than 1, this thread will be wake up.
    WaitForSingleThread(hSemaphore, INFINITE);
   
   
//do some handling
}
You may notice from the demo code that the thread 1 doesn't get the ownership of the semaphore, but calls the ReleaseSemaphore(). This is the difference betwen the mutex and semaphore. For semaphore, threads doesn't need to get the ownership to call the ReleaseSemaphore().

4. Events
Event is the most flexiable to use. Its only purpose is to become the singled or unsignled. For mutex kernel objects, it will become singled after the thread calls the Wait...() function and shortly return back to unsigned status. But for evetns, these two status will be controled by application. Application can set its status perfectly as it hopes.
There are also two types of events: Auto and Munual. Auto event means that this event will become unsignled automatically after this event becomes signled, which always some thread wakes up from sleep. Munual event means that the application needs to call ResetEvent() to set the event in unsigled status. I'll list the API here.
//Create the event 
HANDLE CreateEvent();

//Set the event in singled status
BOOL SetEvent();

//set the event in unsingled status
BOOL ResetEvent();






posted @ 2008-12-28 22:27 Xiaxi 阅读(538) | 评论 (0)编辑 收藏

2008年12月16日

Apartments in COM

     摘要: Will COM die? At least I don't think so. Its idea is still widely used in software.

This article is going to explain a bit about the confusing concept about the Apartment, mainly focusing at the MTA and Neutral. Hope you can learn some from it.  阅读全文

posted @ 2008-12-16 21:55 Xiaxi 阅读(1255) | 评论 (0)编辑 收藏

STA in COM

COM now has five types of Apartment:
1. Legacy
2. STA
3. Free
4. Both
5. Neutral

I'll discuss the Apartment in this article.

COM Objects living in Apartment will be protected by COM, any other calls for this object will be serialized by the COM. If there are concurrent threading in your application and you also don't want to the complicated multi-thread concurrency, you may get to love the Apartment model.

Here are some points of the Apartment modle:
1. Each Single Apartment only contains one thread.
2. Each thread should call ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); to join the STA.
3. If  you want to call the STA COM object living in other STA, you need to marshal your pointer.
4. Each STA may contain more than one COM objects.
5. At one time a STA allows only one thread running.

The code for COM object is simple. Here is I just list the code snippet for the client.
#include <iostream>
using namespace std;

#import 
"CarDemo.dll" no_namespace



void ThreadFunc(void *);
//IFreeCar * pCar;
//IFreeCar * pCar;
ICar * pCar;
int _tmain(int argc, _TCHAR* argv[])
{
    ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
    {
        ICar 
* pCar;
        ::CoCreateInstance(__uuidof(Car) , NULL, CLSCTX_INPROC_SERVER  , __uuidof(ICar) , (
void**)&pCar);
        IStream
* interfaceStream1;
        IStream
* interfaceStream2;
        CoMarshalInterThreadInterfaceInStream(__uuidof(ICar), pCar, 
&interfaceStream1);
        CoMarshalInterThreadInterfaceInStream(__uuidof(ICar), pCar, 
&interfaceStream2);
        HANDLE hThread[
2];
        hThread[
0= (HANDLE)::_beginthread(ThreadFunc, 0, (void*)interfaceStream1);
        hThread[
1= (HANDLE)::_beginthread(ThreadFunc, 0, (void*)interfaceStream2);
        ::WaitForMultipleObjects(
2, hThread, true, INFINITE);

        pCar
->Release();

    }
    
int iVal;
    cin
>>iVal;

    ::CoUninitialize();
    
return 0;
}

void ThreadFunc(LPVOID lpParameter)
{
    ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    {
        IStream
* pStream = reinterpret_cast<IStream*>(lpParameter);
        ICar 
* pCar;
        CoGetInterfaceAndReleaseStream(pStream, __uuidof(ICar), reinterpret_cast
<void**>(&pCar));
        pCar
->Drive(3);
        pCar
->Release();
    }
    ::CoUninitialize();
}

Here is some helpful links for you to understand the STA better:
http://support.microsoft.com/default.aspx?scid=kb;en-us;172314
http://support.microsoft.com/kb/206076



posted @ 2008-12-16 11:57 Xiaxi 阅读(270) | 评论 (0)编辑 收藏

2008年12月15日

COM Stub and Proxy

In the following cases leads to the usage of the Stub and Proxy:
1. Call COM Interfaces living in other Apartments.
2. Call COM Interfaces living in an EXE Server.

Definition for Stub and Proxy:
 - Proxy : A COM object loaded into the client's process, which packages up client requests for any out-of-process ( or out of apartment ) method invocation.
 - Stub : A COM object loaded into the server's process, which receives the client's requests, unpackages them, and handles them off to the "real COM object."

Under the hoold, a procedure named "Marshaling" works between process, which includes the process of packing, sending, receiving and unpacking data between clients and servers, via Stubs and Proxies.

posted @ 2008-12-15 22:28 Xiaxi 阅读(568) | 评论 (0)编辑 收藏

A simple C++/CLI code snippet

In my current project, i need to search out the GUIDs from some certain strings. This is a difficult job if you want to written with the tradition C++. But with C++/CLI, we can benefit from the powerful .NET library. I have to admit that mixing the C++/CLI code with the C++ code is a bit confusing.
#include "stdafx.h"

#
using <system.dll>
#
using <system.data.dll>
using namespace System;
using namespace System::Text::RegularExpressions;
using namespace System::Runtime::InteropServices;

int _tmain(int argc, _TCHAR* argv[])
{
    String 
^ sDelivDataXML = "gp:GUID=\"7F57069D-DD5C-48E9-B746-6FAFF0271B9A\" gp:Included=\"true\"gp:Component gp:GUID=\"02023E2A-A4ED-45B8-9900-D3A144F09C82\" ";
    String
^ pat = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}";
    Regex
^ r = gcnew Regex( pat,RegexOptions::Compiled);
    Match
^ m = r->Match(sDelivDataXML);
    
int matchCount = 0;
    
while ( m->Success )
    {
        Console::WriteLine( 
"{0}", m->Value );
        m 
= m->NextMatch();
    }
    
return 0;
}

I will not cover the grama or any other knowledge of C++/CLI or the regular expression in this article. If you have any questions about the String^ or how to write the regular expression, just google it.

The Output :
7F57069D-DD5C-48E9-B746-6FAFF0271B9A
02023E2A-A4ED-45B8-9900-D3A144F09C82

ok, enjoy it!


posted @ 2008-12-15 16:31 Xiaxi 阅读(362) | 评论 (0)编辑 收藏

Add C++/CLI support in your legacy code

Just now I'm maintaining an old piece of code, written in C++. And now i  just want to search out some special strings following special pattern, and it seems that there is no good way that the tradition c++ way. So since now the .net is growing almost 7 years old and the c++/cli is coming mature, i decide to take advantage of the .net powerful functionality.

Steps are simple, not very complicated.
Here is the steps.
1. Add the header file (.h).
2. Add the body file (.cpp). Include the header file in the body file.
3. Pop-up the Property page for the body file. Property->C/C++->General->Compile with common language support : common language runtime support
4. If your project is vc++ and uses the precompiled header, go to Property->C/C++->Precompiled Headers->Create/Use Precompiled Header : Not using precompiled header.
5. You also needs to set Property->C/C++->Enable C++ Exception : Yes with SEH Exceptions(/EHa)

ok, now go to rebuild your project and it will works!

Enjoy the power of the .NET!

posted @ 2008-12-15 11:50 Xiaxi 阅读(318) | 评论 (0)编辑 收藏

2008年7月17日

正确的使用CComSafeArray,CComVariant和CComBSTR

如果你用C++来编写COM,那么你将必不可少的使用这三个类型。使用这三种wrapper class毫无疑问会简化我们的编程,使得使用SAFEARRAY, VARIANT和BSTR简单。但是,使用这三个类型依然需要小心,因为使用不当的话,就会造成内存泄漏,或效率降低。

1. 如果拷贝两个BSTR
假如我们一个BSTR,这个时候我希望复制一份BSTR,并丢弃之前的BSTR。通常我们会这么写:
CComBSTR StringToBSTR(const string & sVal)
{
     CComBSTR bstrValue 
= sVal.data();
     
return bstrValue;
}

int main()
{
     CComBSTR vValue 
= StringToBSTR("value");

     
return 0;
}

当然,上面这个程序没有任何问题,不会有任何内存泄漏的可能。但是,你有没有上面代码里都发生了什么了?
答案很简单,在函数StringToBSTR里面,讲bstrValue返回的时候,会调用CComBSTR::Copy(),在Copy()里面将会调用
 ::SysAllocStringByteLen()
这个函数。而后在给vValue赋值的时候,又 会调用一次
::SysAllocString()
显而易见,开销很大。

那么,我们将怎么改进这段代码了?
BSTR StringToBSTR(const string & sVal)
{
     CComBSTR bstrValue 
= sVal.data();
     
return bstrValue.Detach();
}

int main()
{
     CComBSTR vValue.Attach(StringToBSTR(
"value"));

     
return 0;
}
这样,通过CComBSTR::Detach(),我们将BSTR返回回来,通过CComBSTR::Attach(),我们将BSTR指针存储起来。这样,就减小了两次开销,大大提高了效率,也不会造成内存效率。

2. 如何使用CComSafeArray
使用CComSafeArray的一个最大的好处,就是它会自动释放元素是VARIANT和BSTR。也就是说,如果你的类型是VARIANT,它会自动调用::VariantClear()。如果你的类型是BSTR,他会自动调用::SysStringFree()方法。但是使用它的时候,同样要小心。
2.1 成对使用::SafeArrayAccessData()和::SafeArrayUnaccessData()
我们有时候会这样使用CComSafeArray的元素:
void DoSomething()
{
     CComSafeArray
<double> pSafeArray(3);
     
double * pVal = NULL;
     ::SafeArrayAccessData(pSafeArray
.m_psa, (void**)&pVal);

     
//handle the elements through the pVal;
}
因为::SafeArrayAccessData 方法会在SFAEARRAY上给lock加1. 如果上面程序显示调用CComSafeArray::Destroy()函数,你检查它返回来的HRESULT的时候,应该是下面的值:
        hr    0x8002000d 内存已锁定。     HRESULT
如果你不仔细检查,那么将造成CComSafeArray没有释放。
2.2 从CComSafeArray转为成CComVariant
有时候我们使用CComVariant包装SAFEARRY。你会这样写代码:
void DoSomething()
{
     CComSafeArray
<double> pSafeArray(3);
    
     
//fill the safearray

     CComVariant v 
= pSafeArray.Detach();
}
你可能会任务CComVariant会存储pSafeArray的指针。可惜,你错了。
CComVariant会调用::SafeArrayCopy 来完成赋值操作。而你的pSafeArray已经调用了Detach()操作,那么它里面的SAFEARRAY就变成了孤儿,没有人去释放它了。
那么你应该怎么写了?
你可以这么写:
void DoSomething()
{
     CComSafeArray
<double> pSafeArray(3);
    
     
//fill the safearray

     CComVariant v 
= pSafeArray.m_psa;
}
这样,CComVariant会调用::SafeArrayCopy 来完成复制操作,而CComSafeArray也会保证在析构的时候释放里面的SAFEARRAY。

使用上面三个wrapper类,确实可以很方便我们编程,也能避免很多memory leak。但是,使用他们同样要小心,不然,同样会造成性能损失,或者,更糟糕的,内存泄漏。


posted @ 2008-07-17 21:10 Xiaxi 阅读(5141) | 评论 (2)编辑 收藏

仅列出标题  
<2008年12月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

导航

统计

常用链接

留言簿(1)

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜