HOME > BCB

プログラミング:BCB

このページではBCBプログラミングで役立つ(と思われる)テクニックをメモの感覚でまとめていきます。 間違い等のご指摘をいただけると助かります。

アプリケーションの二重起動を防止するには

以下のようにMutexを設定します。ApplicationTitleはUniqueな名称を適当に設定。


HANDLE mx;
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    //2回起動防止
    mx = CreateMutex( NULL, true, "ApplicationTitle" );      
    if( GetLastError() ){
        return 1;
    }
    try
    {
        //省略
    }
    catch (Exception &exception)
    {
        Application->ShowException(&exception);
    }
    ReleaseMutex( mx );
    return 0;
}
//---------------------------------------------------------------------------

アプリケーションの二重起動をせずに、既存ウインドウに送られてきた引数を渡すには

ウインドウ名がApplicationTitleのようにわかっている場合はFindWindowを使ってそちらにデータを送ります。


//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    HWND hWnd=FindWindow(NULL,"ApplicationTitle");
    //引数が付いてきたとき、引数を見つけたウインドウへ送る
    if(hWnd && ParamCount()>0){
        for(int i=1;i<=ParamCount();i++){
            char msgbuf[256];
            strcpy(msgbuf,ParamStr(i).c_str());
            COPYDATASTRUCT cds;
            cds.dwData = i;
            cds.cbData = strlen(msgbuf);    
            cds.lpData = (LPVOID)msgbuf;    
            ::SendMessage(hWnd,WM_COPYDATA, NULL ,(LPARAM)&cds);
       }
       return 1;
    }
    try
    {
        //通常の処理
    }
    catch (Exception &exception)
    {
        Application->ShowException(&exception);
    }
    return 0;
}
//---------------------------------------------------------------------------
//MainUnit.h

Private:
    void __fastcall WMCopyData( TMessage &Message );

BEGIN_MESSAGE_MAP
    VCL_MESSAGE_HANDLER(WM_COPYDATA, TMessage, WMCopyData)
END_MESSAGE_MAP(TComponent)

//---------------------------------------------------------------------------
//MainUnit.cpp
void __fastcall TMainForm::WMCopyData( TMessage &Message ){
    COPYDATASTRUCT *cds=(PCOPYDATASTRUCT)Message.LParam;
    //送られてきた(char*)cds->lpDataを処理
    //アプリケーションのウインド状態変更
    Application->Restore();
    Message.Result=true;
}
//---------------------------------------------------------------------------


アプリケーションの初期状態にメインフォームを表示しないようにするには

以下のようにApplication->ShowMainForm=false;を追加します。


//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    try
    {
        Application->Initialize();
        Application->Title = "ApplicationTitle";
        Application->ShowMainForm=false;    //初期に非表示
        Application->CreateForm(__classid(TMainForm), &MainForm);
         Application->Run();
    }
    catch (Exception &exception)
    {
        Application->ShowException(&exception);
    }
    return 0;
}
//---------------------------------------------------------------------------

アプリケーションのアイコン上にドラッグ&ドロップされたファイルを開くには

たとえば以下のように記述します。


//---------------------------------------------------------------------------
void __fastcall TMainForm::FormCreate(TObject *Sender)
{
    if (ParamCount() > 0) {
        for(int iFile=1;iFile<=ParamCount();iFile++){     
           String OpenFileName=ExtractLongPathName(ParamStr(iFile));
           //ファイルを開く処理
           if(FileExists(OpenFileName))OpenDocument(OpenFileName);
        }
    }
}
//----------------------------------------------------------------------------
String __fastcall TMainForm::ExtractLongPathName(const String &ShortPath)
{
    String asTemp=ShortPath;
    String asReturn=ExtractLongName(asTemp);
    int iDelimiter=asTemp.LastDelimiter("\\");
    asTemp=ShortPath.SubString(1,iDelimiter-1);
    while((iDelimiter=asTemp.LastDelimiter("\\"))>0){
        asReturn=ExtractLongName(asTemp)+"\\"+asReturn;
        asTemp=ShortPath.SubString(1,iDelimiter-1);
    }
    asReturn=asTemp+"\\"+asReturn;
    return asReturn;
}
//----------------------------------------------------------------------------
String __fastcall TMainForm::ExtractLongName(const String &ShortPath)
{
    WIN32_FIND_DATA FindData;
    HANDLE hFindFile = FindFirstFile(ShortPath.c_str(), &FindData);

    String LongName = FindData.cFileName ;
    CloseHandle(hFindFile);

    return LongName;
}

//---------------------------------------------------------------------------

ヒントをステータスバーに表示するには

たとえば以下のように設定します。


//---------------------------------------------------------------------
void __fastcall TMainForm::ShowHint(TObject *Sender)
{
    StatusBar1->Panels->Items[1]->Text = Application->Hint;

}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormCreate(TObject *Sender)
{
    Application->OnHint = ShowHint;
}
//---------------------------------------------------------------------------

実行ファイルの存在するディレクトリを取得するには

たとえば以下のように記述


void __fastcall TMainForm::FormCreate(TObject *Sender)
{
    String DefaultDir=ExtractFilePath(ParamStr(0));
}

デスクトップウインドウの実効エリアを取得するには

たとえば以下のように記述します。


    RECT WinRect;
    SystemParametersInfo(SPI_GETWORKAREA,0,&WinRect,0);
    int iScreenTop=WinRect.top;
    int iScreenLeft=WinRect.left;
    int iScreenBottom=WinRect.bottom;
    int iScreenRight=WinRect.right;
    int iScreenHeight=GetSystemMetrics(SM_CYFULLSCREEN);
    int iScreenWidth=GetSystemMetrics(SM_CXFULLSCREEN);

Windowsの終了時に処理をするには

MainUnit.hに以下のように記述。


    void __fastcall WMEndSession(TMessage &Msg);

BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(WM_ENDSESSION,TMessage,WMEndSession)
END_MESSAGE_MAP(TComponent)

MainUnit.cppに処理を記述


//---------------------------------------------------------------------------
void __fastcall TMainForm::WMEndSession(TMessage &Msg)
{
    //処理内容
}
//---------------------------------------------------------------------------

リッチエディットのIME色を変更するには

たとえば以下のように設定します。


//---------------------------------------------------------------------------
void __fastcall TMainForm::FormCreate(TObject *Sender)
{
    COMPCOLOR imecolor[4];
    imecolor[0].crText=0x00ff0000;
    imecolor[0].crBackground=0x00ffffff;
    imecolor[0].dwEffects=CFE_AUTOCOLOR;
    imecolor[1].crText=0x00000000;
    imecolor[1].crBackground=0x00ffff00;
    imecolor[1].dwEffects=CFE_AUTOCOLOR;
    imecolor[2].crText=0x00ff0000;
    imecolor[2].crBackground=0x00ffffff;
    imecolor[2].dwEffects=CFE_AUTOCOLOR;
    imecolor[3].crText=0x00ffffff;
    imecolor[3].crBackground=0x00ff0000;
    imecolor[3].dwEffects=CFE_AUTOCOLOR;
    RichEdit1->Perform(EM_SETIMECOLOR,0,(LPARAM)&imecolor);
}
//---------------------------------------------------------------------------

詳しくはEM_SETIMECOLOR参照。

リッチエディットのカーソル位置の行数と桁数を取得するには

たとえば以下のように記述します。


    int iRow=SendMessage(RichEdit1->Handle,EM_LINEFROMCHAR,-1,0)+1;
    int iCol=RichEdit1->SelStart
            -SendMessage(RichEdit1->Handle,EM_LINEINDEX,-1,0)+1;

リッチエディットのカーソル位置を中央に移動するには

たとえば以下のように記述します。


    int iFirstVisibleLine=RichEdit1->Perform(EM_GETFIRSTVISIBLELINE,0,0);
    int iCurLine=RichEdit1->Perform(EM_LINEFROMCHAR, RichEdit1->SelStart, 0);
    int iVisibleLine=RichEdit1->ClientHeight/(RichEdit1->DefAttributes->Height);
    int iMoveLine=iCurLine-iVisibleLine/2-iFirstVisibleLine+1;
    if(iCurLine>0 || iMoveLine>0)
        RichEdit1->Perform(EM_LINESCROLL,0,MoveLine);

リッチエディットの処理速度を多少なりとも速くするには

たとえば以下のように記述します。これはTListView等でも同様です。


    RichEdit1->Lines->BeginUpdate();
    //時間のかかる処理
    RichEdit1->Lines->EndUpdate();

あるいは、


    long mask = SendMessage(RichEdit1->Handle, EM_SETEVENTMASK, 0, 0);
    //時間のかかる処理
    SendMessage(RichEdit1->Handle, EM_SETEVENTMASK, 0, mask);

大文字小文字の変換

AnsiStringの関数を使えば


    String asSource;
    String asDest=AnsiLowerCase(asSource);

で簡単ですが、地道にやると次のようになります。


String __fastcall TMainForm::ToUpperLowerCase(const String &Str, bool bUpperCase)
{
    char* source=Str.c_str();
    char* dest=new char[s.Length()+1];
    //sourceのポインタ
    char* Psource=source;
    //destのポインタ
    char* Pdest=dest;
    while (*P!=&#39;\0&#39;){
        //一応ダブルバイト文字をチェック
        if((*P>=char(0x81) && *P<=char(0x9f)) 
            || (*P>=char(0xe0) && *P<=char(0xfc))){
            *Pdest++=*Psource++;
            *Pdest++=*Psource++;
        }else if(bUpperCase && *P>=&#39;a&#39; && *P<=&#39;z&#39;){
            *Pdest++=*Psource++ -32;
        }else if(!bUpperCase && *P>=&#39;A&#39; && *P<=&#39;Z&#39;){
            *Pdest++=*Psource++ +32;
        }else{
            *Pdest++=*Psource++;
        }
    }
    *Pdest=&#39;\0&#39;;
    String Str1=String(dest);
    delete[] dest;
    return Str1;
}

スクリーンセーバーを起動するには


    PostMessage(GetDesktopWindow(),WM_SYSCOMMAND,SC_SCREENSAVE,0L);

スクリーンセーバーのオンオフ制御は


    //オン
    SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,TRUE,0,SPIF_SENDWININICHANGE);
    //オフ
    SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,FALSE,0,SPIF_SENDWININICHANGE);

Internet ExplorerにURLやファイル名を渡して表示するには


#include <comobj.hpp>

void __fastcall TMainForm::OpenIE(String asURL)
{
    const String asOLEObjName = "InternetExplorer.Application";
    static Variant IE;
    HWND hWnd;
    if( VarIsEmpty( IE ) ){
        IE = CreateOleObject( asOLEObjName );
    }
    if(!(hWnd = FindWindow( "IEFrame", NULL ))){
        IE = CreateOleObject( asOLEObjName );
        hWnd = FindWindow( "IEFrame", NULL );
    }
    if(hWnd){
        IE.OlePropertySet("Visible",true);
        ::SetForegroundWindow( hWnd );
        IE.OleProcedure("Navigate",asURL);
    }
}

NetscapeにURLやファイル名を渡して表示するには

BCB4だと問題なく動きますが、BCB3はDdeMan.pasのバグのため、DdeMan.pasのRequestData関数の

        Move(pData, Result, len);    
     を
        Move(pData^, Result^, len); 

に書き換えて再コンパイルする必要があります。(あるかりさん、ありがとうございました。)


//#include "Ddeman.hpp"    //BCB3のとき        
#include <Ddeman.hpp>

void __fastcall TMainForm::OpenNN(String asURL)
{
    char* dummy;
    bool bSuccess=false;
    TDdeClientConv *dde = new TDdeClientConv(this);
    dde->ServiceApplication =NNFullPath;    //netscapeのパス
    dde->SetLink("netscape", "WWW_Activate");
    dummy=dde->RequestData("0xFFFFFFFF");
    StrDispose(dummy);
    bSuccess=dde->SetLink("netscape", "WWW_OpenURL");
    dummy=dde->RequestData(asURL+",,0xFFFFFFFF,0x3,,,");
    StrDispose(dummy);
    dde->CloseLink();
    delete dde;
    if(!bSuccess)WinExec(NNFullPath.c_str(),SW_SHOW);
}

HOME > BCB