Tuesday, July 31, 2007

MutiTask -- CreateMutex -- VC++中进程与多进程管理的实现方法

进程的互斥运行

  正常情况下,一个进程的运行一般是不会影响到其他正在运行的进程的。但是对于某些有特殊要求的如以独占方式使用串行口等硬件设备的程序就要求在其进程运行期间不允许其他试图使用此端口设备的程序运行的,而且此类程序通常也不允许运行同一个程序的多个实例。这就引出了进程互斥的问题。

  实现进程互斥的核心思想比较简单:进程在启动时首先检查当前系统是否已经存在有此进程的实例,如果没有,进程将成功创建并设置标识实例已经存在的标记。此后再创建进程时将会通过该标记而知晓其实例已经存在,从而保证进程在系统中只能存在一个实例。具体可以采取内存映射文件、有名事件量、有名互斥量以及全局共享变量等多种方法来实现。下面就分别对其中具有代表性的有名互斥量和全局共享变量这两种方法进行介绍:

// 创建互斥量
HANDLE m_hMutex = CreateMutex(NULL, FALSE, "Sample07");
// 检查错误代码
if (GetLastError() == ERROR_ALREADY_EXISTS) {
 // 如果已有互斥量存在则释放句柄并复位互斥量
 CloseHandle(m_hMutex);
 m_hMutex = NULL;
 // 程序退出
 return FALSE;
}

  上面这段代码演示了有名互斥量在进程互斥中的用法。代码的核心是CreateMutex()对有名互斥量的创建。CreateMutex()函数可用来创建一个有名或无名的互斥量对象,其函数原型为:

HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
 BOOL bInitialOwner, // 初始化互斥对象的所有者
 LPCTSTR lpName // 指向互斥对象名的指针
);

  如果函数成功执行,将返回一个互斥量对象的句柄。如果在CreateMutex()执行前已经存在有相同名字的互斥量,函数将返回这个已经存在互斥量的句柄,并且可以通过GetLastError()得到错误代码ERROR_ALREADY_EXIST。可见,通过对错误代码ERROR_ALREADY_EXIST的检测可以实现CreateMutex()对进程的互斥。

  使用全局共享变量的方法则主要是在MFC框架程序中通过编译器来实现的。通过#pragma data_seg预编译指令创建一个新节,在此节中可用volatile关键字定义一个变量,而且必须对其进行初始化。Volatile关键字指定了变量可以为外部进程访问。最后,为了使该变量能够在进程互斥过程中发挥作用,还要将其设置为共享变量,同时允许具有读、写访问权限。这可以通过#pragma comment预编译指令来通知编译器。下面给出使用了全局变量的进程互斥代码清单:

#pragma data_seg("Shared")
int volatile g_lAppInstance =0;
#pragma data_seg()
#pragma comment(linker,"/section:Shared,RWS")
……
if(++g_lAppInstance>1)
return FALSE;

  此段代码的作用是在进程启动时对全局共享变量g_nAppInstancd 加1 ,如果发现其值大于1,那么就返回FALSE以通知进程结束。这里需要特别指出的是,为了使以上两段代码能够真正起到对进程互斥的作用,必须将其放置在应用程序的入口代码处,即应用程序类的初始化实例函数InitInstance()的开始处。

  结束进程

  进程只是提供了一段地址空间和内核对象,其运行是通过在其地址空间内的主线程来体现的。当主线程的进入点函数返回时,进程也就随之结束。这种进程的终止方式是进程的正常退出,进程中的所有线程资源都能够得到正确的清除。除了这种进程的正常推出方式外,有时还需要在程序中通过代码来强制结束本进程或其他进程的运行。ExitProcess()函数即可在进程中的某个线程中使用,并将立即终止本进程的运行。ExitProcess()函数原型为:

VOID ExitProcess(UINT uExitCode);

  其参数uExitCode为进程设置了退出代码。该函数具有强制性,在执行完毕后进程即已经被结束,因此位于其后的任何代码将不能被执行。虽然ExitProcess()函数可以在结束进程的同时通知与其相关联的动态链接库,但是由于它的这种执行的强制性,使得ExitProcess()函数在使用上将存在有安全隐患。例如,如果在程序调用ExitProcess()函数之前曾用new操作符申请过一段内存,那么将会由于ExitProcess()函数的强制性而无法通过delete操作符将其释放,从而造成内存泄漏。有鉴于ExitProcess()函数的强制性和不安全性,在使用时一定要引起注意。

  ExitProcess()只能强制执行本进程的退出,如果要在一个进程中强制结束其他的进程就要用TerminateProcess()来实现。与ExitProcess()不同,TerminateProcess()函数执行后,被终止的进程是不会得到任何关于程序退出的通知的。也就是说,被终止的进程是无法在结束运行前进行退出前的收尾工作的。所以,通常只有在其他任何方法都无法迫使进程退出时才会考虑使用TerminateProcess()去强制结束进程的。下面给出TerminateProcess()的函数原型:

BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode);


  参数hProcess和uExitCode分别为进程句柄和退出代码。如果被结束的是本进程,可以通过GetCurrentProcess()获取到句柄。TerminateProcess()是异步执行的,在调用返回后并不能确定被终止进程是否已经真的退出,如果调用TerminateProcess()的进程对此细节关心,可以通过WaitForSingleObject()来等待进程的真正结束。

  小结

  多进程是多任务管理中的重要内容,文中上述部分对其基本概念和主要的技术如子进程的创建与结束、进程间的互斥运行等做了较详细的介绍。通过本文读者应能对多进程管理有一个初步的认识。

MutiTask -- CreateMutex

CreateMutex Creates or opens a named or unnamed mutex object.
 
同步問題 Mutex的使用
請先看 同步問題 Event的使用

如同使用Event的文章所述,如果兩個以上的Thread,同時想修改數個共用的記憶體,那
我們要保證同時之間,只有一個Thread可做Update的動作,而且要等該Thread完成所有
修改後,其他的Thread才可以讀取或修改;這便要使用Mutex物件的技術了。Mutex的作法
可以想像,大家都想做某件事前,要先取得一個許可金牌才能做,等做完了才將這金牌
Release出來,其他人只能一直等,直到取得金牌的擁有權後,方能做事。

Mutex物件的做法與特性如下:

使用CreateMutex()來產生一個Mutex物件,而傳入的Mutex名稱字串用以區別不同的Mutex
,也就是說,不管是哪個Process/Thread,只要傳入的名稱參數是相同的一個字串,那
CreateMutex()傳回值(hMutex, handle of Mutex)會指向相同的一個Mutex物件。這和
Event物件相同。然而Mutex和Event有很大的不同,Mutex有Owner的概念,如果Mutex為
ThreadA所擁有,那麼ThreadA執行WaitForSingleObject()時,並不會停下來,而會立即
傳回WAIT_OBJECT_0,而其他的Thread執行WaitForSingleObject()則會停下來,直到Mutex
的所有權被Release出來或Time Out。而Thread如何取得Mutex的所有權呢?主要如下:

  1.CreateMutex(Byval 0, 1, "MyMutex") 第二個參數傳1進去,則第一個呼叫CreateMutex
    且第二個參數傳1的Thread會擁有該Mutex。但如果第二個參數傳0進去,那代表
    CreateMutex時,沒有人擁有該Mutex。
  2.承上的說明,如果Mutex沒有擁有者,則第一個呼叫WaitForSingleObject的Thread
    會擁有該Mutex。

上面說過,只有擁有該Mutex的Thread在執行WaitForSingleObject()不會停下來,其他的
Thread則會停下來,那其他的Thread如何取得該Mutex的所有權呢?那必需是原先擁有該
Mutex的Thread以ReleaseMutex來放棄所有權,一旦所有權放出來,而有其他的Thread處
於WaitForSingleObject()的停留等待狀態,則有一個Thread會即時取得該Mutex的所有權
(上面第2點的說明),所以,若其他的Thread也執行WaitForSingleObject()時,就會處於
等待的狀態。正因WaitForSingleObject()會令Mutex處於UnSignal的狀態(和Event不同),
所以可以完成同一時問只有一個thread來UpDate共用記憶體的需求(當然大家都要使用Mutex
的規則則來做,即想Update時,要使用WaitForSingleObject()來看看是否可取得Mutex的所
有權。)

另有一件事要特別提出,如果一個Thread已取得Mutex的所有權,而它呼叫WaitForSingleObject()
n 次,則也要使用ReleaseMutex n次才能夠將Mutex的擁有權放棄,這和Event也不同,而
且,非Mutex擁有者呼叫ReleaseMutex也不會有任何作用。而每次以WaitForSingleObject
呼叫一次,Mutex會有一個計數器會加一,ReleaseMutex成功會減一,直到Mutex的計數
器為0之後,系統才會將之去除。
 

Saturday, July 28, 2007

text2icon function

HICON CTrayIcon::Text2Icon(const char* const text,COLORREF color)
{
    int cxIcon = GetSystemMetrics(SM_CXSMICON);
    int cyIcon = GetSystemMetrics(SM_CYSMICON);

    HDC hdcDisplay = GetDC(NULL);
    if (!hdcDisplay){
        return NULL;
    }

    HDC hdcXOR = CreateCompatibleDC(hdcDisplay);
    if (!hdcXOR){
        ReleaseDC(NULL, hdcDisplay);
        return NULL;
    }

    HDC hdcAND = CreateCompatibleDC(hdcDisplay);
    if (!hdcAND){
        DeleteDC(hdcXOR);
        ReleaseDC(NULL, hdcDisplay);
        return NULL;
    }

    ICONINFO iconinfo;
    ZeroMemory(&iconinfo,sizeof(ICONINFO));
    iconinfo.fIcon = TRUE;

    iconinfo.hbmMask = CreateCompatibleBitmap(hdcDisplay,cxIcon, cyIcon);
    if (!iconinfo.hbmMask){
        DeleteDC(hdcAND);
        DeleteDC(hdcXOR);
        ReleaseDC(NULL, hdcDisplay);
        return NULL;
    }

    iconinfo.hbmColor = CreateCompatibleBitmap(hdcDisplay, cxIcon, cyIcon);
    if (!iconinfo.hbmColor){
        DeleteObject(iconinfo.hbmMask);
        DeleteDC(hdcAND);
        DeleteDC(hdcXOR);
        ReleaseDC(NULL, hdcDisplay);
        return NULL;
    }

    ReleaseDC(NULL, hdcDisplay);

    RECT rc; rc.left = 0; rc.top = 0; rc.right = cxIcon; rc.bottom = cyIcon;

    LOGFONT lf;
    ZeroMemory(&lf,sizeof(LOGFONT));
    lf.lfCharSet            = ANSI_CHARSET;
    lf.lfOutPrecision       = OUT_DEFAULT_PRECIS;
    lf.lfClipPrecision      = CLIP_DEFAULT_PRECIS;
    lf.lfQuality            = ANTIALIASED_QUALITY; //DEFAULT_QUALITY;
    lf.lfPitchAndFamily     = DEFAULT_PITCH;
    lf.lfHeight                = cyIcon;
    lf.lfWidth                 = (cxIcon / 3)-1;
    lf.lfWeight                = FW_NORMAL;
    strcpy_s(lf.lfFaceName ,_countof(lf.lfFaceName),TEXT("TAHOMA"));

    HFONT hfontANDOld  = NULL;
    HFONT hfontXOROld  = NULL;
    HFONT hfont = CreateFontIndirect(&lf);
    if (hfont){
        hfontXOROld  = (HFONT)SelectObject(hdcAND, hfont);
        hfontANDOld  = (HFONT)SelectObject(hdcXOR, hfont);
    }

    HBITMAP hbmMaskOld  = (HBITMAP)SelectObject(hdcAND, iconinfo.hbmMask);
    PatBlt(hdcAND, 0, 0, cxIcon, cyIcon, WHITENESS);
    int nBkModeANDOld   = SetBkMode(hdcAND, TRANSPARENT);
    DrawText(hdcAND,  text, -1, &rc,  DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    SetBkMode(hdcAND, nBkModeANDOld);
    if (hfontANDOld)
        SelectObject(hdcAND, hfontANDOld);

    HBITMAP hbmColorOld = (HBITMAP)SelectObject(hdcXOR, iconinfo.hbmColor);
    PatBlt(hdcXOR, 0, 0, cxIcon, cyIcon, BLACKNESS);
    int nBkModeXOROld   = SetBkMode(hdcXOR,  TRANSPARENT);
    COLORREF rgbOld        = SetTextColor(hdcXOR, color);
    DrawText(hdcXOR, text, -1, &rc,  DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    SetBkMode(hdcXOR,  nBkModeXOROld);
    SetTextColor(hdcXOR, rgbOld);
    if (hfontXOROld)
        SelectObject(hdcXOR, hfontXOROld);

    if (hbmMaskOld)
        SelectObject(hdcAND, hbmMaskOld);
    if (hbmColorOld)
        SelectObject(hdcXOR, hbmColorOld);

    if (hfont)
        DeleteObject(hfont);
    DeleteDC(hdcAND);
    DeleteDC(hdcXOR);

    HICON hIcon = CreateIconIndirect(&iconinfo);

    DeleteObject(iconinfo.hbmColor);
    DeleteObject(iconinfo.hbmMask);

    return hIcon;
}

燃料電池 加水 轉換 電力

 

超真實Vista XP外觀改裝

 

Sunday, July 22, 2007

CE -- Windows CE Shortcuts (.bat on PDA ?)

Shortcuts have the file extension ".lnk" - let's take a simple example to start, an application that no self respecting operating system should be without... Solitare! - The shortcut file is going to be Solitare.lnk, and would contain the following :

21#\windows\solitare.exe

So, how does this work, 21# means that the command line contains 21 characters, and the command line is "\Windows\Solitare.exe" (which is 21 characters, count them!)

wait a second, on the desktop the shortcut appears as "Internet Explorer", not as a shortcut to "Iesample" - when the Windows CE operating system gets built we can examine the contents of the overall O/S .DAT file ( initobj.dat) [the .dat file contains the creation of folders and maps files to folders] - here's the interesting line from the .dat file.

Directory("\Windows\Desktop"):-File("Internet Explorer.lnk", "\Windows\iesample.lnk")

Notice how the actual file \Windows\iesample.lnk maps to the \Windows\Desktop folder and appears as "Internet Explorer.lnk" on the desktop, .DAT file entries can be used to map, and rename files - pretty cool, eh !

 

CE / WM -- wceload.exe -- cab installation

 

Monday, July 16, 2007

cygwin - bash - print last 10 lines

#!/bin/sh
#print last 10 lines of file
# First argument is the filename
lines=`wc -l $1 | awk '{print $1}' `

#echo start=expr $lines - $2
start=`expr $lines - $2`

#echo sed "1,$start d" $1
sed "1,$start d" $1

Sunday, July 15, 2007

Robotics -- 支援機器人運動微軟推出升級版機器人平台

支援機器人運動微軟推出升級版機器人平台
臺灣新浪網 - Taiwan
微軟在周二推出了升級版的Robotics Studio平台,新增了對Windows Embeded CE 6.0和Windows Mobile的支援,此外還有若干編程方面的改進。微軟在去年12月引入了這個機器人平台,同時宣布將會支援RoboCup2007;RoboCup是一個旨在組建一直類人機器人並能夠到2050年時能擊敗 ...
  新版的Robotics Studio 1.5改進了編程語言和顯示世界的模擬引擎。增加了視覺與語音識別,更好的使用文檔與優化的代碼編輯器。
 

Thursday, July 12, 2007

MutiTask - Begin/EndCriticalRegion

http://groups.google.com/group/microsoft.public.dotnet.framework.compactframework/browse_thread/thread/1e6d0a04fd391a12?hl=en

MutiTask -- CreateProcess() and WaitForSingleObject()


        STARTUPINFO startupInfo;
        PROCESS_INFORMATION processInfo;
        DWORD result = 0;
        int error = 0;

        ZeroMemory(&startupInfo, sizeof(startupInfo));
        ZeroMemory(&processInfo, sizeof(processInfo));
        startupInfo.cb = sizeof(startupInfo);

        if (CreateProcess(
                        _T("\\FlashDisk\\Executable.exe"),
                        NULL,
                        NULL,
                        NULL,
                        FALSE,
                        0,
                        NULL,
                        NULL,
                        &startupInfo,
                        &processInfo))
        {
                result = WaitForSingleObject( ProcessInfo.hProcess, INFINITE);
                CloseHandle(processInfo.hProcess);
                CloseHandle(processInfo.hThread);
        }
        else
        {
                error = GetLastError();
        }

Sunday, July 08, 2007

TeraTerm 虛擬終端機程式介紹

TeraTerm 是一套虛擬終端機程式,所謂的終端機的來由是因為古早時代的電腦時在太貴又太稀少
所以那時省成本的方式就是一台主機大家共用,以遠端連線方式連接到主機上,以主機的角度來看
那些連上主機的設備…不見得是電腦可能只是螢幕與鍵盤…所以通稱遠端的設備叫終端機…
那時因為要達成不同的設備連接都能夠有一致的畫面顯示,所以規範了VT100,VT220...之類的VT系列指令
而現在電腦普及…連接主機的需求還是存在,特別是在UNIX/Linux系列的平台
所以虛擬終端機程式還是有許多人在使用,而TeraTerm 是一套很好用的虛擬終端機程式

它好用之處在於它可以執行MACRO 進行批次自動化的工作,對於自動化測試而言有很高的便利性
古早的版本是 http://hp.vector.co.jp/authors/VA002416/teraterm.html 下載,最後的版本是1998/3/10
而最近發現…有一群人在維護它TeraTerm Support Forums,讓這個好用的軟體可以延續下去,
最新的版本是 4.51版,支援UTF8 及SSH2和一些奇奇怪怪的新功能