ページ

2007-10-04

非表示のウィンドウからダイアログを表示させる

電波状態監視のアプリをウィンドウを非表示で作成しようとしています。
 
ウィンドウは ShowWindowに SW_HIDEを指定して非表示にしました。
こうすることでタスクの一覧からも消えてくれます。
//ShowWindow(g_hWnd, nCmdShow);
ShowWindow(g_hWnd, SW_HIDE);
UpdateWindow(g_hWnd);
 
ところが、この状態からダイアログを表示できずにしばらく悩んでしまいました。
case WM_TRAY_NOTIFYICON:
    if (WM_LBUTTONDOWN == lParam) {
        DialogBox(g_hInst, (LPCTSTR)IDD_CONFIG_DIALOG, g_hWnd, ConfigDialogProc);
    }
    break;
 
DialogBoxは正常に終るのですが、画面には何も表示されません。
 
その後 色々と試行錯誤した結果、単に親ウィンドウが非表示だから表示できてなかっただけという事に気付きました。
 
ダイアログを表示する直前にウィンドウを表示(ついでに最前面に移動)することで無事にダイアログを表示させることができました。
case WM_TRAY_NOTIFYICON:
    if (WM_LBUTTONDOWN == lParam) {
        ShowWindow(g_hWnd, SW_NORMAL);
        SetForegroundWindow((HWND)((ULONG) g_hWnd | 0x00000001));
        DialogBox(g_hInst, (LPCTSTR)IDD_CONFIG_DIALOG, g_hWnd, ConfigDialogProc);
        ShowWindow(g_hWnd, SW_HIDE);
    }
    break;
 
わかってしまえば何という事も無いのですが、随分時間を取られてしまいました。
という訳で今日はこの辺で...。
 

2007-10-03

レジストリの状態を監視する方法

これまでレジストリの監視をするときはタイマーイベントで一定時間毎に RegQueryValueExしていたのですが、 実はレジストリの内容を監視するためのAPIが用意されていました。  
日本語版のページにも少しだけ紹介されていたので、知らなかったのは自分だけなのかもしれませんが...。  
上記のページによると「状態および通知ブローカーAPI」と言うらしいです。
素直に「状態通知API」くらいにしておいてくれた方が目にとまりやすいと思いませんか?
 
ともかく昨日までの受信信号強度の取得部分を書き直してみました。
 
まず、WM_CREATEあたりでレジストリの監視を開始します。
    HREGNOTIFY    hRegNotify;
    
    RegistryNotifyCallback(
        HKEY_CURRENT_USER                   ,
        _T("Software\\Sharp\\ShPhoneLib")   ,
        _T("RFStrength")                    ,
        RegNotifyCallbackFunc               ,
        0                                   ,
        NULL                                ,
        &hRegNotify
    );
 
今回はレジストリの値がそのまま受信信号強度なので問題ないのですが、 レジストリの特定のビットだけを監視したいときはサンプルにあるように NOTIFICATIONCONDITION構造体を使用します。
 
 
続いてレジストリの値が変化したときに呼び出されるコールバック関数。
レジストリの値は pDataに入っています。
VOID RegNotifyCallbackFunc(
        HREGNOTIFY  hNotify     , 
        DWORD       dwUserData  , 
        const PBYTE pData       , 
        const UINT  cbData      )
{
    DWORD   dwRfStrength;

    dwRfStrength = (*(DWORD *)pData);

    if (dwRfStrength == 0) {
        // 圏外
    } else {
        // 圏内
    }
}
 
 
最後、アプリケーションを終了するときにハンドルを閉じます。
    // 終了時に閉じる
    RegistryCloseNotification(hRegNotify);
 
 
タイマーイベントと比べて負荷が軽減したのかは不明ですが、コードはすっきりとしました。
やはりドキュメントは一通り目を通しておかないといけませんね。
 

2007-10-02

受信信号強度の取得 その3

昨日 CString::Midがうまく動かないと悩んでいた件は単なる勘違いでした。
 
W-SIMから"at@@lvl00OK"のようなレスポンスが返ってきたときに、
if (strResponse.Mid(7, 2) == _T("00")) {
    // 圏外
}
としていたのですが、 よく見るとレスポンスは "at@@lvl00OK"ではなく "at@@lvl\r00\r\n\r\nOK\r\n" となっています。
 
CR/LFを含めることで無事に動作するようになりました。
if (strResponse.Mid(8, 2) == _T("00")) {
    // 圏外
}
 
ところがアプリに組み込むと本来のアンテナ表示が頻繁に圏外になります。
どうやら W-SIMの取り合いになっているみたいです。
考えてみたらその通りですよね...。
 
 
その後、レジストリの HKCU\Software\Sharp\ShPhoneLib\RFStrengthというキーに気付きました。
結果的にこのレジストリを監視することで 他に影響を出さずに電波状態を取得できるようになったのですが、 今度は本体が節電状態になると動作しないようです。
 
節電状態でもアプリを動作させるというのは可能なのでしょうか?
相変わらず先は長そうです。
 

2007-10-01

受信信号強度の取得 その2

昨日の続きで W-SIMから信号強度を取得してみます。
 
NSDNを見ながらシリアル通信部分を書いてみました。
細かい部分を端折っていますが、一応レスポンスを取得できています。
 
最初、コマンドに\rを付け忘れていたためにレスポンスが帰って来なくて 1時間くらい悩んでいました。
 
#define WSIM_PORT   _T("COM1:")
#define LVL_COMMAND _T("at@@lvl\r")


HANDLE  g_hWSim;

// ポート開く
BOOL OpenWSim()
{
    DWORD dwErr;
    CloseWSim();

    g_hWSim = CreateFile(
        WSIM_PORT                       ,
        GENERIC_READ | GENERIC_WRITE    ,
        0                               ,
        NULL                            ,
        OPEN_EXISTING                   ,
        0                               ,
        NULL                            );
    if (INVALID_HANDLE_VALUE == g_hWSim) {
        dwErr = GetLastError();
        return FALSE;
    }

    return TRUE;
}


// ポート閉じる
BOOL CloseWSim()
{
    if (INVALID_HANDLE_VALUE != g_hWSim) {
        CloseHandle(g_hWSim);
        g_hWSim = INVALID_HANDLE_VALUE;
    }

    return TRUE;
}


// コマンドを送る
INT SendCommand(CString strCommand)
{
    CHAR    cBuffer[256];
    DWORD   dwNumberOfBytesWritten = 0;

    WideCharToMultiByte(CP_ACP, 0, strCommand, strCommand.GetLength(), cBuffer, 256, NULL, FALSE);
    WriteFile(g_hWSim, cBuffer, strCommand.GetLength(), &dwNumberOfBytesWritten, NULL);

    return dwNumberOfBytesWritten;
}


// レスポンス取得
INT ReceiveResponse(CString *strResponse)
{
    INT     iLen;
    CHAR    cBuffer[256];
    DWORD   dwNumberOfBytesRead = 0;

    ZeroMemory(cBuffer, 256);
    ReadFile(g_hWSim, cBuffer, 256, &dwNumberOfBytesRead, NULL);
    iLen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)cBuffer, -1, NULL, 0);
    if (iLen > 0) {
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)cBuffer, -1, strResponse->GetBuffer(iLen), iLen);
        strResponse->ReleaseBuffer();
    }
    return dwNumberOfBytesRead;
}


// 圏外の判定
BOOL IsOutOfService()
{
    BOOL    bRet = FALSE;
    CString strResponse;

    CString str;

    if (!OpenWSim()) {
        return bRet;
    }

    SendCommand(LVL_COMMAND);
    Sleep(1000);
    ReceiveResponse(&strResponse);
    
    // レスポンスが 00から始まっていれば圏外?

    CloseWSim();

    return bRet;
}
 
後はレスポンスの文字列を解析して圏外かどうかを判定するだけなのですが、 何故か CString::Midがうまく動いてくれません。
まだまだ先は長そうですね。