frog.raindrop.jp.knowledge > Windows SDK, API

March 4, 2009

IPv4 でマルチキャストに送出するパケットの TTL を設定する。

IPv4 でマルチキャストのパケットを送出する際、デフォルトのままでは TTL が 1 になってしまう。ルータ越えの必要がある場合は以下のようにして、送信に使用するソケットのオプションを変更する。(下記は TTL を 32 に変更した例)

// #include <Winsock2.h>
int ttl = 32;
int result = setsockopt (socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof ttl);
if (SOCKET_ERROR == result)
{
    // エラー処理は省略...
}

C# の場合はこんなの。

// using System.Net.Sockets;
int ttl = 32;
socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, ttl);

February 24, 2006

ローカルネットワークを選択するコンボボックス

TCP サーバや UDP アプリなどの設定画面に登場する、バインドするネットワークアドレスを選択するコンボボックスです。はい、ものすごく限定的な Tips です。ネットワークインターフェースの列挙方法の一例、かな?

BOOL CXXXDialog::InitNetworkCombo (void)
{
    char szHostName [MAX_PATH];
    ZeroMemory (szHostName, sizeof szHostName);
    m_ComboNetwork.Clear ();
    
    // このコンピュータのホスト名を取得する
    int nRet = gethostname (szHostName, sizeof szHostName);
    if (SOCKET_ERROR == nRet)
    {
        // エラー処理
    }
    else
    {
        // ホスト名よりホストの情報を取得する
        struct hostent* pHostEnt = gethostbyname (szHostName);
        if (!pHostEnt)
        {
            // エラー処理
        }
        else
        {
            // INADDR_ANY を追加する
            int nIndex = m_ComboNetwork.AddString (_T ("すべてのネットワーク"));
            m_ComboNetwork.SetItemData (nIndex, INADDR_ANY);
            
            // 取得したアドレスのリストをコンボボックスに追加する
            for (int nCount = 0; pHostEnt->h_addr_list [nCount]; nCount ++)
            {
                struct in_addr Address;
                memcpy (&Address, pHostEnt->h_addr_list [nCount], sizeof (struct in_addr));
                nIndex = m_ComboNetwork.AddString (inet_ntoa (Address));
                m_ComboNetwork.SetItemData (nIndex, Address.S_un.S_addr);
            }
            
            // ループバックを追加する
            nIndex = m_ComboNetwork.AddString (_T ("127.0.0.1"));
            m_ComboNetwork.SetItemData (nIndex, INADDR_LOOPBACK);
            return TRUE;
        }
    }
    return FALSE;
}

January 21, 2006

アイコンの不透明部分のリージョンを作成する

アイコン型のウインドウを作成したかったのです。それには、アイコンのマスク部分を取得して、マスクの不透明部分の形のウインドウリージョンを作って設定します。

リージョン作成部分は続きの方にかいときますが、ウインドウプロシージャはこんな感じ。

LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_PAINT:
        PAINTSTRUCT PaintStruct;
        HDC hDC = BeginPaint (hWnd, &PaintStruct);
        DrawIcon (hDC, 0, 0, m_hIcon);
        EndPaint (hWnd, &PaintStruct);
        break;
    case WM_MOVE:
        SetWindowRgn (hWnd, m_hRgn, TRUE);
        break;
    default:
        break;
    }
    return DefWindowProc (hWnd, uMsg, wParam, lParam);
}

説明が前後しますが、ウインドウ作成時にウインドウに描画するアイコンを m_hIcon 仁読み込んであります。同時に、そのアイコンハンドルを渡して後述の CreateRgnFromIcon (HICON) を呼びだし、m_hRgn にアイコンのマスク部分の形のリージョンを得てあります。では、その CreateRgnFromIcon 関数です。

続きを読む...

タイトルバーのないウインドウのドラッグ

タイトルバーのないウインドウを、ウインドウ内の任意の位置をドラッグして移動できるようにする方法です。

以下のように、WM_LBUTTONDOWN メッセージをすり替えて、タイトルバー上でマウスボタンが押下されたようにだましてしまうとうまくいきます。

LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_LBUTTONDOWN:
        ReleaseCapture ();
        SendMessage (hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
        return 0;
    default:
        break;
    }
    return DefWindowProc (hWnd, uMsg, wParam, lParam);
}

続きを読む...

December 18, 2005

API でちっちゃい文字をつぶれないように描くサンプル

HDD のお掃除をしていると、テストのために作ったソースとかごちゃごちゃ出てきます。これはその一つ。ちっちゃい字をスムージングをかけて縮小して出力します。

20051218stretchblt_font.png

続きを読む...

October 21, 2005

非表示のウインドウの最大化を解除する

ウインドウを最大化した後、非表示にする。

SendMessage (hWnd, WM_SYSCOMMAND, SC_MAXIMIZE);
ShowWindow (hWnd, SW_HIDE);

次に表示すると、ウインドウは最大化状態のままだ。

ShowWindow (hWnd, SW_SHOW);

ここで問題です。ウインドウを非表示のままでリストア(元のサイズに戻す、つまり最大化状態を解除)するにはどうしたらいいでしょう。

// 元のサイズに戻るが、同時にウインドウが表示されてしまう
ShowWindow (hWnd, SW_RESTORE);

// これも
SendMessage (hWnd, WM_SYSCOMMAND, SC_RESTORE);

// これも
WINDOWPLACEMENT Placement;
ZeroMemory (&Placement, sizeof (WINDOWPLACEMENT));
Placement.length = sizeof (WINDOWPLACEMENT);
GetWindowPlacement (hWnd, &Placement);
Placement.showCmd = SW_RESTORE;
SetWindowPlacement (hWnd, &Placement);

意外と難しい。最終的に、私が思いついたのはこんな方法。

DWORD dwStyle = GetWindowLong (hWnd, GWL_STYLE);
dwStyle &= ~WS_MAXIMIZE;
SetWindowLong (hWnd, GWL_STYLE, dwStyle);

この場合、最大化時のウインドウ位置とサイズがそのままでリストアされてしまう。非表示のままリストアを厳密にシミュレートするには、あらかじめ GetWindowPlacement で、リストア時のウインドウ矩形を取得しておき、リストア後に MoveWindow してあげないといけない。

October 7, 2005

ダイアログのデフォルトボタンを変更する

ダイアログのデフォルトボタンを動的に変更したい、あるいは、一時的にデフォルトでなくしたい、などを行うには、ダイアログに対して DM_SETDEFID メッセージを送出し、デフォルトにするボタンの ID を渡す。MFC だと、CDialog::SetDefID がそれにあたる。しかし、それだけではボタンの周囲の黒枠が更新されないんである。Enter キーを押下すると、確かにデフォルトボタンが変更されていることが分かるが、表示上は更新されていない。SendMessage で送出したあと Invalidate したり、UpdateWindow したりしてもだめだったので、メッセージ受信と非同期に「デフォルト」という属性を更新しているのかもしれない。

あれこれやってみたところ、以下のようにフォーカスを強制的にセットしてやる以外のうまい方法は見つけられなかった。

// フォーカスのあるコントロールを取得しておく
CWnd *pWndFocus = GetFocus ();

// ボタンをデフォルトに
SetDefID (IDC_BUTTON1);

// 一旦フォーカスをセットする
GotoDlgCtrl (&m_Button1);

// 以前にフォーカスのあったコントロールに戻る
if (pWndFocus)
{
    GotoDlgCtrl (pWndFocus);
}

もともと IDC_BUTTON1 にフォーカスがあったとしても、この方法でうまくいくようだ。ちなみに、単にデフォルト属性をはずす場合は、WPARAM を 0 にして DF_SETDEFID するのでいけるようだ。(SetDefID (0); ってこと)

October 5, 2005

PathAddExtension の挙動

PathAddExtension という関数がある。パスの最後に拡張子を追加するというもの。

BOOL PathAddExtension(
    LPTSTR pszPath,
    LPCTSTR pszExtension
);
pszPath
[in, out] 拡張子を追加する対象となる null 終端文字列を格納したバッファのポインタ。
pszExtension
[in] 拡張子を格納した文字列のポインタ。拡張子はピリオドから始まっている必要がある。

戻り値は、拡張子を追加した場合は TRUE、以外は FALSE が返る。

注意しなきゃなんないのは

Remarks
If there is already a file extension present, no extension will be added. If the pszPath points to a NULL string, the result will be the file extension only. If pszExtension points to a NULL string, an ".exe" extension will be added.

すでに拡張子が付いてる場合は、この関数はさらに拡張子を付けることをしない。つまり、ファイル名にピリオドを含みたい場合には不適だ("MyAppVer1.1.exe"とかさ)。ユーザに入力されたファイル名に拡張子を付けるような場合に使うのはやめた方がいい。

September 28, 2005

ファイル名に使用できない文字を判断する

Windows Xp になって、ファイル名に使用できない文字を入力したときのメッセージが修正されているのに気がついた。

pathgetchartype1.png

以前は、カンマとセミコロンが、実際にはファイル名に使用できるにもかかわらず、使用できない文字として表示されていたのは有名な話だ。

pathgetchartype2.png

ところで、shlwapi.dll には、ファイル名に使用できる文字かどうかを判断する関数がある。PathGetCharType だ。

続きを読む...

September 9, 2005

TransparentBlt の補間モード

Win32API の TransparentBlt 関数の解説に

SetStretchBltMode 関数で iStretchMode モードの値として設定される BLACKONWHITE と WHITEONBLACK は、TransparentBlt 関数では COLORONCOLOR に変換されます。

とある。が、じっさいには、 HALFTONE も COLORONCOLOR に変換されているように見える。そう書いてあるリソースがどこにもないのだけれど。カラーキーを指定したければ自分でマスクするか、GDI+ を使うのがよさげ。

August 15, 2005

複数行のツールチップ

TTM_SETMAXTIPWIDTH を送る。忘れないようにメモメモ。

lResult = SendMessage(          // returns LRESULT in lResult
    (HWND) hWndControl,         // handle to destination control
    (UINT) TTM_SETMAXTIPWIDTH,  // message ID
    (WPARAM) wParam,            // = 0; not used, must be zero
    (LPARAM) lParam             // = (LPARAM) (INT) iWidth;
);


April 7, 2005

お手軽矩形塗りつぶし

CDC のメンバ関数に、FillSolidRect ってのがある。私の知る限り、SDK には対応する関数がないのだが、引数が RECT と色だけで、ブラシを作成したりする必要がないので、ちょっと矩形領域を塗りつぶすのに重宝している。

ところで、この関数の実装は、普通に考えたら、こう?

void CDC::FillSolidRect( LPCRECT lpRect, COLORREF clr )
{
    ASSERT_VALID(this);
    ASSERT(m_hDC != NULL);
    
    CBrush  SolidBrush;
    if ( SolidBrush.CreateSolidBrush( clr ) )   // 指定色のブラシを作成
    {
        FillRect( lpRect, &SolidBrush );
        SolidBrush.DeleteObject();              // 作ったブラシは削除
    }
}

でも、デバッグのときにソースを見ると、実はこうなっている。
SDK で矩形塗りつぶしをするときにも、わざわざブラシを作らなくてもできるってこと。

続きを読む...

February 23, 2005

ウインドウを一時的にアクティブにして最前面に持ってくる

後ろで動いてるアプリを手前に持ってくる関数です。いつも作るうえに、毎回調べないと作れないので載せておきます。

ちなみに、ウインドウが「常に」最前面に表示されるようにするには、SetWindowPos 関数で HWND_TOPMOST を渡します。でもこれはググればごろごろサンプル出てくるんですけどね。

続きを読む...

February 10, 2005

デスクトップの作業領域

私は今まで、VBでウインドウの配置を決めるのに、デスクトップのサイズを下のようにして得ていた。

Dim lngRet          As Long
Dim udtRcDesktop    As RECT

'' デスクトップ全体の矩形を取得します
lngRet = GetWindowRect( GetDesktopWindow(), udtRcDesktop )

でも、これだとタスクバーを含んじゃうんだよね。で、SystemParametersInfo関数を使うと、タスクバーを含まない、デスクトップの作業領域の矩形を得ることができます。

'' デスクトップの作業領域の矩形を取得します
lngRet = SystemParametersInfo( SPI_GETWORKAREA, 0&, udtRcDesktop, 0& )

続きを読む...

ListBox に水平スクロールバーを表示する

VB6のリストボックスは、項目が長くても横スクロールできない。横スクロールバーを出すには、LB_SETHORIZONTALEXTENT メッセージを送る必要がある。

項目追加するたびに横スクロールバーを調整するのはこんな感じ

Option Explicit
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Const LB_GETHORIZONTALEXTENT    As Long = &H193
Private Const LB_SETHORIZONTALEXTENT    As Long = &H194&

'------------------------------------------------------------------
'プロシージャ名:  :AddItemAndSetScrollBar
'説明:            :リストボックスに項目を追加し、必要に応じて横スクロールバーを表示する
'引数:            :pListBox           I   リストボックス
'                 :pItem              I   追加する文字列
'                 :pIndex             I   追加位置
'------------------------------------------------------------------
Private Sub AddItemAndSetScrollBar(pListBox As VB.ListBox, pItem As String, Optional pIndex As Variant)
Dim sngTextWidth        As Single
Dim lngCurPixels        As Long
Dim lngNewPixels        As Long
    
    
    '' ここは説明は不要でしょう
    Call pListBox.AddItem(pItem, pIndex)
    
    '' 現在の幅を取得
    lngCurPixels = SendMessage(pListBox.hWnd, LB_GETHORIZONTALEXTENT, 0&, ByVal 0&)
    
    '' テキストの幅から必要なスクロール幅を計算
    '' 計算方法は適宜修正のこと
    '' リストボックスのフォントとフォームのフォントが同じならこんな感じで求められる
    With pListBox.Parent
        sngTextWidth = .TextWidth(pItem)                            '' 文字列の幅
        lngNewPixels = .ScaleX(sngTextWidth, .ScaleMode, vbPixels)  '' ピクセルに変換
    End With
    
    '' 余白分を足す
    '' ここでは適当に4ピクセル足してるが、システム標準の値とかあるのかしら
    '' GetSystemMetrics( SM_CXEDGE ) * 2 あたりがに合わしておくといいかも
    lngNewPixels = lngNewPixels + 4&
    
    '' 現在の幅がちっちゃければ設定します。
    If (lngNewPixels > lngCurPixels) Then
        Call SendMessage(pListBox.hWnd, LB_SETHORIZONTALEXTENT, lngNewPixels, ByVal 0&)
    End If
End Sub
続きを読む...

January 31, 2005

MakeSureDirectoryPathExists を VB6 でシミュレートする

VB6 で、MakeSureDirectoryPathExists 関数の動きをシミュレートしたものを作ったので、おいときます。

続きを読む...

December 8, 2004

VB6 API ビューアの ENUMLOGFONTEX 構造体宣言の誤り

VB6 より Windows API を利用する場合に、APIビューアは便利ですが、しばしば宣言等に誤りがあります。

今回は、フォントファミリの列挙を行う EnumFontFamiliesEx 関数を使おうとして、ENUMLOGFONTEX 構造体の宣言をコピーしたのですが、

' API ビューアの宣言
Private Type ENUMLOGFONTEX
        elfLogFont As LOGFONT
        elfFullName(LF_FULLFACESIZE) As Byte
        elfStyle(LF_FACESIZE) As Byte
        elfScript(LF_FACESIZE) As Byte
End Type

のようになっていました。WinGDI.h には

// WinGDI.h (Microsoft Platform SDK February 2003) Line:1220
#if(WINVER >= 0x0400)
typedef struct tagENUMLOGFONTEXA
{
    LOGFONTA    elfLogFont;
    BYTE        elfFullName[LF_FULLFACESIZE];
    BYTE        elfStyle[LF_FACESIZE];
    BYTE        elfScript[LF_FACESIZE];
} ENUMLOGFONTEXA, FAR *LPENUMLOGFONTEXA;
typedef struct tagENUMLOGFONTEXW
{
    LOGFONTW    elfLogFont;
    WCHAR       elfFullName[LF_FULLFACESIZE];
    WCHAR       elfStyle[LF_FACESIZE];
    WCHAR       elfScript[LF_FACESIZE];
} ENUMLOGFONTEXW, FAR *LPENUMLOGFONTEXW;
#ifdef UNICODE
typedef ENUMLOGFONTEXW ENUMLOGFONTEX;
typedef LPENUMLOGFONTEXW LPENUMLOGFONTEX;
#else
typedef ENUMLOGFONTEXA ENUMLOGFONTEX;
typedef LPENUMLOGFONTEXA LPENUMLOGFONTEX;
#endif // UNICODE
#endif /* WINVER >= 0x0400 */

となっていますので、VB6 では、

' 正解
Private Type ENUMLOGFONTEX
        elfLogFont As LOGFONT
        elfFullName(LF_FULLFACESIZE - 1&) As Byte
        elfStyle(LF_FACESIZE - 1&) As Byte
        elfScript(LF_FACESIZE - 1&) As Byte
End Type

のようにするのが正しいです。

続きを読む...

November 9, 2004

Service のデバッグ

サービスプログラムのデバッグがしたくて、 ServiceMain と、Handler 関数をエクスポートしてみた。
// MyService.def
EXPORTS
	ServiceMain
	Handler
さらに、テストプロを書いてみる
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>


#define MODULE_NAME         "MyService.exe"
#define FUNCNAME_MAIN       "ServiceMain"
#define FUNCNAME_HANDLER    "Handler"
int    main (int argc, char **argv)
{
    HMODULE     hModule    = NULL;
    char        szCurrent[MAX_PATH];
    char        szModule[MAX_PATH];
    DWORD       dwRet    = 0;

    LPSERVICE_MAIN_FUNCTION    lpfnServiceMain    = NULL;
    LPHANDLER_FUNCTION         lpfnHandler        = NULL;



    ZeroMemory (szCurrent, sizeof szCurrent);
    dwRet = GetCurrentDirectory (sizeof szCurrent, szCurrent);
    if (!dwRet)
    {
        fprintf (stderr, "GetCurrentDirectory err -->(%d)\n", GetLastError ());
    }
    else
    {
        ZeroMemory (szModule, sizeof szModule);
        wsprintf (szModule, "%s\\%s", szCurrent, MODULE_NAME);

        hModule = LoadLibrary (szModule);
        if (!hModule)
        {
            fprintf (stderr, "LoadLibrary err -->(%d)\n", GetLastError ());
        }
        else
        {
            lpfnServiceMain = (LPSERVICE_MAIN_FUNCTION) GetProcAddress (hModule, FUNCNAME_MAIN);
            if (!lpfnServiceMain)
            {
                fprintf (stderr, "GetProcAddress(ServiceMain) err -->(%d)\n", GetLastError ());
            }
            else
            {
                lpfnHandler = (LPHANDLER_FUNCTION) GetProcAddress (hModule, FUNCNAME_HANDLER);
                if (!lpfnHandler)
                {
                    fprintf (stderr, "GetProcAddress(Handler) err -->(%d)\n", GetLastError ());
                }
                else
                {
                    // とりあえず Handler をコールしてみた
                    lpfnHandler (SERVICE_CONTROL_CONTINUE);
                }
            }
            FreeLibrary (hModule);
        }
    }
    return 0;
}
結果は・・・呼び出せた。そんで動けば面白かったんだけど、サービスモジュール側でログ出力の処理がアクセスヴァイオレーションになってしまった。まあ、そううまくはいかないよね。一応、失敗談として。

November 5, 2004

FindFirstFile, FindNextFile, FindClose に関しての覚書

FindFirstFile
条件に合うファイルが見つからない場合も含め、有効なファイルを返せない場合は、INVALID_HANDLE_VALUE を返す。
条件に合うファイルが見つからない場合は GetLastError() は ERROR_FILE_NOT_FOUND が返る。
FindNextFile
条件に合うファイルが見つからない場合も含め、有効なファイルを返せない場合は、FALSE を返す。
条件に合うファイルが見つからない場合は GetLastError() は ERROR_NO_MORE_FILES が返る。
FindClose
FindFirstFile が成功しなかった場合は、ハンドルを渡してはいけない。ERROR_INVALID_HANDLE エラーを返したりなんていう、親切な実装はされていない。渡すとスレッドが死んでしまう。
CloseHandle と異なり、NULL ハンドルを渡しても死んでしまうようだ。もちろん、INVALID_HANDLE_VALUE もだめである。

October 22, 2004

NTサービスの作り方