次の方法で共有


チュートリアル: WPF で Win32 コントロールをホストする

Windows Presentation Foundation (WPF) は、アプリケーションを作成するための豊富な環境を提供します。 ただし、Win32 コードに多額の投資がある場合は、そのコードを完全に書き換えるのではなく、少なくとも一部のコードを WPF アプリケーションで再利用する方が効果的な場合があります。 WPF には、WPF ページで Win32 ウィンドウをホストするための簡単なメカニズムが用意されています。

このトピックでは、Win32 リスト ボックス コントロールをホストするアプリケーションである WPF サンプルでの Win32 ListBox コントロールのホストについて説明します。 この一般的な手順は、任意の Win32 ウィンドウをホストするように拡張できます。

要求事項

このトピックでは、WPF と Windows API の両方のプログラミングに関する基本的な知識を前提としています。 WPF プログラミングの基本的な概要については、「 作業の開始」を参照してください。 Windows API プログラミングの概要については、このテーマに関する多数の書籍、特に Charles Petzold による プログラミング Windows を参照してください。

このトピックに付属するサンプルは C# で実装されているため、プラットフォーム呼び出しサービス (PInvoke) を使用して Windows API にアクセスします。 PInvoke に関する知識は役に立ちますが、必須ではありません。

このトピックには、関連するサンプルのコード例が多数含まれています。 ただし、読みやすくするために、完全なサンプル コードは含まれていません。 WPF サンプルでの Win32 ListBox コントロールのホストから完全なコードを取得または表示できます。

基本的な手順

このセクションでは、WPF ページで Win32 ウィンドウをホストするための基本的な手順について説明します。 残りのセクションでは、各手順の詳細を確認します。

基本的なホスティング手順は次のとおりです。

  1. ウィンドウをホストする WPF ページを実装します。 1 つの手法は、ホストされたウィンドウのページのセクションを予約する Border 要素を作成する方法です。

  2. HwndHostから継承するコントロールをホストするクラスを実装します。

  3. そのクラスで、 HwndHost クラス メンバー BuildWindowCoreをオーバーライドします。

  4. ホストされたウィンドウを、WPF ページを含むウィンドウの子として作成します。 従来の WPF プログラミングでは明示的に使用する必要はありませんが、ホスティング ページはハンドル (HWND) を持つウィンドウです。 hwndParent メソッドの BuildWindowCore パラメーターを通じて、ページ HWND を受け取ります。 ホストされるウィンドウは、この HWND の子として作成する必要があります。

  5. ホスト ウィンドウを作成したら、ホストされたウィンドウの HWND を返します。 1 つ以上の Win32 コントロールをホストする場合は、通常、HWND の子としてホスト ウィンドウを作成し、そのホスト ウィンドウのコントロールを子にします。 コントロールをホスト ウィンドウにラップすると、WPF ページがコントロールから通知を受け取る簡単な方法が提供されます。この方法では、HWND 境界を越えた通知に関する特定の Win32 の問題が処理されます。

  6. 子コントロールからの通知など、ホスト ウィンドウに送信された選択されたメッセージを処理します。 これを行うには 2 つの方法があります。

    • ホスティング クラスでメッセージを処理する場合は、WndProc クラスのHwndHost メソッドをオーバーライドします。

    • WPFにメッセージを処理させたい場合は、コードビハインドでイベントHwndHostクラスMessageHookを処理します。 このイベントは、ホストされたウィンドウによって受信されるすべてのメッセージに対して発生します。 このオプションを選択した場合でも、 WndProcをオーバーライドする必要がありますが、必要なのは最小限の実装だけです。

  7. DestroyWindowCoreWndProcメソッドとHwndHost メソッドをオーバーライドします。 HwndHost コントラクトを満たすためにこれらのメソッドをオーバーライドする必要がありますが、必要なのは最小限の実装のみである場合があります。

  8. 分離コード ファイルで、コントロール ホスティング クラスのインスタンスを作成し、ウィンドウをホストすることを目的とした Border 要素の子にします。

  9. ホストされているウィンドウと通信するには、Microsoft Windows メッセージを送信し、子ウィンドウからのメッセージ (コントロールによって送信された通知など) を処理します。

ページ レイアウトを実装する

ListBox コントロールをホストする WPF ページのレイアウトは、2 つの領域で構成されます。 ページの左側には、Win32 コントロールを操作できるユーザー インターフェイス (UI) を提供するいくつかの WPF コントロールがホストされています。 ページの右上隅には、ホストされている ListBox コントロールの正方形の領域があります。

このレイアウトを実装するコードは非常に単純です。 ルート要素は、2 つの子要素を持つ DockPanel です。 1 つ目は、ListBox コントロールをホストする Border 要素です。 ページの右上隅にある 200 x 200 の正方形を占有します。 2 つ目は、情報を表示し、公開されている相互運用プロパティを設定して ListBox コントロールを操作できる WPF コントロールのセットを含む StackPanel 要素です。 StackPanelの子である各要素について、これらの要素が何であるか、何を行うかについて詳しく説明するために使用されるさまざまな要素の参照資料を参照してください。これらは以下のコード例に記載されていますが、ここでは説明しません (基本的な相互運用モデルではそれらを必要とせず、サンプルに対話機能を追加するために提供されています)。

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="WPF_Hosting_Win32_Control.HostWindow"
  Name="mainWindow"
  Loaded="On_UIReady">

  <DockPanel Background="LightGreen">
    <Border Name="ControlHostElement"
    Width="200"
    Height="200"
    HorizontalAlignment="Right"
    VerticalAlignment="Top"
    BorderBrush="LightGray"
    BorderThickness="3"
    DockPanel.Dock="Right"/>
    <StackPanel>
      <Label HorizontalAlignment="Center"
        Margin="0,10,0,0"
        FontSize="14"
        FontWeight="Bold">Control the Control</Label>
      <TextBlock Margin="10,10,10,10" >Selected Text: <TextBlock  Name="selectedText"/></TextBlock>
      <TextBlock Margin="10,10,10,10" >Number of Items: <TextBlock  Name="numItems"/></TextBlock>
  
      <Line X1="0" X2="200"
        Stroke="LightYellow"
        StrokeThickness="2"
        HorizontalAlignment="Center"
        Margin="0,20,0,0"/>
  
      <Label HorizontalAlignment="Center"
        Margin="10,10,10,10">Append an Item to the List</Label>
      <StackPanel Orientation="Horizontal">
        <Label HorizontalAlignment="Left"
          Margin="10,10,10,10">Item Text</Label>
        <TextBox HorizontalAlignment="Left"
          Name="txtAppend"
          Width="200"
          Margin="10,10,10,10"></TextBox>
      </StackPanel>
  
      <Button HorizontalAlignment="Left"
        Click="AppendText"
        Width="75"
        Margin="10,10,10,10">Append</Button>

      <Line X1="0" X2="200"
        Stroke="LightYellow"
        StrokeThickness="2"
        HorizontalAlignment="Center"
        Margin="0,20,0,0"/>
  
      <Label HorizontalAlignment="Center"
        Margin="10,10,10,10">Delete the Selected Item</Label>
  
      <Button Click="DeleteText"
        Width="125"
        Margin="10,10,10,10"
        HorizontalAlignment="Left">Delete</Button>
    </StackPanel>
  </DockPanel>
</Window>  

Microsoft Win32 コントロールをホストするクラスを実装する

このサンプルの中核となるのは、ControlHost.csコントロールを実際にホストするクラスです。 HwndHostから継承されます。 コンストラクターは、高さと幅の 2 つのパラメーターを受け取ります。これは、ListBox コントロールをホストする Border 要素の高さと幅に対応します。 これらの値は、コントロールのサイズが Border 要素と一致することを確認するために後で使用されます。

public class ControlHost : HwndHost
{
  IntPtr hwndControl;
  IntPtr hwndHost;
  int hostHeight, hostWidth;

  public ControlHost(double height, double width)
  {
    hostHeight = (int)height;
    hostWidth = (int)width;
  }
Public Class ControlHost
    Inherits HwndHost
  Private hwndControl As IntPtr
  Private hwndHost As IntPtr
  Private hostHeight, hostWidth As Integer

  Public Sub New(ByVal height As Double, ByVal width As Double)
          hostHeight = CInt(height)
          hostWidth = CInt(width)
  End Sub

定数のセットもあります。 これらの定数は主に Winuser.h から取得され、Win32 関数を呼び出すときに従来の名前を使用できます。

internal const int
  WS_CHILD = 0x40000000,
  WS_VISIBLE = 0x10000000,
  LBS_NOTIFY = 0x00000001,
  HOST_ID = 0x00000002,
  LISTBOX_ID = 0x00000001,
  WS_VSCROLL = 0x00200000,
  WS_BORDER = 0x00800000;
Friend Const WS_CHILD As Integer = &H40000000, WS_VISIBLE As Integer = &H10000000, LBS_NOTIFY As Integer = &H00000001, HOST_ID As Integer = &H00000002, LISTBOX_ID As Integer = &H00000001, WS_VSCROLL As Integer = &H00200000, WS_BORDER As Integer = &H00800000

BuildWindowCore をオーバーライドして Microsoft Win32 ウィンドウを作成する

このメソッドをオーバーライドして、ページによってホストされる Win32 ウィンドウを作成し、ウィンドウとページ間の接続を確立します。 このサンプルには ListBox コントロールのホストが含まれているため、2 つのウィンドウが作成されます。 1 つ目は、WPF ページによって実際にホストされているウィンドウです。 ListBox コントロールは、そのウィンドウの子として作成されます。

この方法の理由は、コントロールから通知を受信するプロセスを簡略化するためです。 HwndHost クラスを使用すると、ホストしているウィンドウに送信されたメッセージを処理できます。 Win32 コントロールを直接ホストする場合は、コントロールの内部メッセージ ループに送信されたメッセージを受信します。 コントロールを表示してメッセージを送信することはできますが、コントロールが親ウィンドウに送信する通知は受け取りません。 つまり、特に、ユーザーがコントロールを操作するときに検出する方法はありません。 代わりに、ホスト ウィンドウを作成し、コントロールをそのウィンドウの子にします。 これにより、コントロールによって送信された通知を含むホスト ウィンドウのメッセージを処理できます。 便宜上、ホスト ウィンドウはコントロールの単純なラッパーに過しないので、パッケージは ListBox コントロールと呼ばれます。

ホスト ウィンドウと ListBox コントロールを作成する

PInvoke を使用して、ウィンドウ クラスを作成して登録することで、コントロールのホスト ウィンドウを作成できます。 ただし、より簡単な方法は、定義済みの "静的" ウィンドウ クラスを使用してウィンドウを作成することです。 これにより、コントロールから通知を受信するために必要なウィンドウ プロシージャが提供され、最小限のコーディングが必要になります。

コントロールの HWND は、読み取り専用プロパティを介して公開されます。これにより、ホスト ページはそれを使用してコントロールにメッセージを送信できます。

public IntPtr hwndListBox
{
  get { return hwndControl; }
}
Public ReadOnly Property hwndListBox() As IntPtr
  Get
      Return hwndControl
  End Get
End Property

ListBox コントロールは、ホスト ウィンドウの子として作成されます。 両方のウィンドウの高さと幅は、上で説明したコンストラクターに渡される値に設定されます。 これにより、ホスト ウィンドウとコントロールのサイズがページ上の予約領域と同じになります。 ウィンドウが作成されると、ホスト ウィンドウの HWND を含む HandleRef オブジェクトが返されます。

protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
  hwndControl = IntPtr.Zero;
  hwndHost = IntPtr.Zero;

  hwndHost = CreateWindowEx(0, "static", "",
                            WS_CHILD | WS_VISIBLE,
                            0, 0,
                            hostWidth, hostHeight,
                            hwndParent.Handle,
                            (IntPtr)HOST_ID,
                            IntPtr.Zero,
                            0);

  hwndControl = CreateWindowEx(0, "listbox", "",
                                WS_CHILD | WS_VISIBLE | LBS_NOTIFY
                                  | WS_VSCROLL | WS_BORDER,
                                0, 0,
                                hostWidth, hostHeight,
                                hwndHost,
                                (IntPtr) LISTBOX_ID,
                                IntPtr.Zero,
                                0);

  return new HandleRef(this, hwndHost);
}
Protected Overrides Function BuildWindowCore(ByVal hwndParent As HandleRef) As HandleRef
  hwndControl = IntPtr.Zero
  hwndHost = IntPtr.Zero

  hwndHost = CreateWindowEx(0, "static", "", WS_CHILD Or WS_VISIBLE, 0, 0, hostWidth, hostHeight, hwndParent.Handle, New IntPtr(HOST_ID), IntPtr.Zero, 0)

  hwndControl = CreateWindowEx(0, "listbox", "", WS_CHILD Or WS_VISIBLE Or LBS_NOTIFY Or WS_VSCROLL Or WS_BORDER, 0, 0, hostWidth, hostHeight, hwndHost, New IntPtr(LISTBOX_ID), IntPtr.Zero, 0)

  Return New HandleRef(Me, hwndHost)
End Function
//PInvoke declarations
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
                                              string lpszClassName,
                                              string lpszWindowName,
                                              int style,
                                              int x, int y,
                                              int width, int height,
                                              IntPtr hwndParent,
                                              IntPtr hMenu,
                                              IntPtr hInst,
                                              [MarshalAs(UnmanagedType.AsAny)] object pvParam);
'PInvoke declarations
<DllImport("user32.dll", EntryPoint := "CreateWindowEx", CharSet := CharSet.Unicode)>
Friend Shared Function CreateWindowEx(ByVal dwExStyle As Integer, ByVal lpszClassName As String, ByVal lpszWindowName As String, ByVal style As Integer, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal hwndParent As IntPtr, ByVal hMenu As IntPtr, ByVal hInst As IntPtr, <MarshalAs(UnmanagedType.AsAny)> ByVal pvParam As Object) As IntPtr
End Function

DestroyWindow と WndProc を実装する

BuildWindowCoreに加えて、WndProcDestroyWindowCoreメソッドとHwndHost メソッドもオーバーライドする必要があります。 この例では、コントロールのメッセージは MessageHook ハンドラーによって処理されるため、 WndProcDestroyWindowCore の実装は最小限です。 WndProcの場合は、handledfalse に設定して、メッセージが処理されていないことを示し、0 を返します。 DestroyWindowCoreの場合は、ウィンドウを破棄するだけです。

protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  handled = false;
  return IntPtr.Zero;
}

protected override void DestroyWindowCore(HandleRef hwnd)
{
  DestroyWindow(hwnd.Handle);
}
Protected Overrides Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
  handled = False
  Return IntPtr.Zero
End Function

Protected Overrides Sub DestroyWindowCore(ByVal hwnd As HandleRef)
  DestroyWindow(hwnd.Handle)
End Sub
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
<DllImport("user32.dll", EntryPoint := "DestroyWindow", CharSet := CharSet.Unicode)>
Friend Shared Function DestroyWindow(ByVal hwnd As IntPtr) As Boolean
End Function

ページでコントロールをホストする

ページでコントロールをホストするには、まず、 ControlHost クラスの新しいインスタンスを作成します。 コントロール (ControlHostElement) を含む border 要素の高さと幅を、 ControlHost コンストラクターに渡します。 これにより、ListBox のサイズが正しく設定されます。 次に、ControlHost オブジェクトをホスト ChildBorder プロパティに割り当てることで、ページ上でコントロールをホストします。

このサンプルでは、MessageHookControlHost イベントにハンドラーをアタッチして、コントロールからメッセージを受信します。 このイベントは、ホストされたウィンドウに送信されるすべてのメッセージに対して発生します。 この場合、これらは、コントロールからの通知を含む、実際の ListBox コントロールをラップするウィンドウに送信されるメッセージです。 このサンプルでは、SendMessage を呼び出してコントロールから情報を取得し、その内容を変更します。 ページがコントロールと通信する方法の詳細については、次のセクションで説明します。

SendMessage には 2 つの PInvoke 宣言があることに注意してください。 これは、 wParam パラメーターを使用して文字列を渡し、もう 1 つは整数を渡すために使用するためです。 データが正しくマーシャリングされるようにするには、署名ごとに個別の宣言が必要です。

public partial class HostWindow : Window
{
int selectedItem;
IntPtr hwndListBox;
ControlHost listControl;
Application app;
Window myWindow;
int itemCount;

private void On_UIReady(object sender, EventArgs e)
{
  app = System.Windows.Application.Current;
  myWindow = app.MainWindow;
  myWindow.SizeToContent = SizeToContent.WidthAndHeight;
  listControl = new ControlHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth);
  ControlHostElement.Child = listControl;
  listControl.MessageHook += new HwndSourceHook(ControlMsgFilter);
  hwndListBox = listControl.hwndListBox;
  for (int i = 0; i < 15; i++) //populate listbox
  {
    string itemText = "Item" + i.ToString();
    SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, itemText);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" +  itemCount.ToString();
}
Partial Public Class HostWindow
    Inherits Window
    Private selectedItem As Integer
    Private hwndListBox As IntPtr
    Private listControl As ControlHost
    Private app As Application
    Private myWindow As Window
    Private itemCount As Integer

    Private Sub On_UIReady(ByVal sender As Object, ByVal e As EventArgs)
        app = System.Windows.Application.Current
        myWindow = app.MainWindow
        myWindow.SizeToContent = SizeToContent.WidthAndHeight
        listControl = New ControlHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth)
        ControlHostElement.Child = listControl
        AddHandler listControl.MessageHook, AddressOf ControlMsgFilter
        hwndListBox = listControl.hwndListBox
        For i As Integer = 0 To 14 'populate listbox
            Dim itemText As String = "Item" & i.ToString()
            SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, itemText)
        Next i
        itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
        numItems.Text = "" & itemCount.ToString()
    End Sub

private IntPtr ControlMsgFilter(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  int textLength;

  handled = false;
  if (msg == WM_COMMAND)
  {
    switch ((uint)wParam.ToInt32() >> 16 & 0xFFFF) //extract the HIWORD
    {
      case LBN_SELCHANGE : //Get the item text and display it
        selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero);
        textLength = SendMessage(listControl.hwndListBox, LB_GETTEXTLEN, IntPtr.Zero, IntPtr.Zero);
        StringBuilder itemText = new StringBuilder();
        SendMessage(hwndListBox, LB_GETTEXT, selectedItem, itemText);
        selectedText.Text = itemText.ToString();
        handled = true;
        break;
    }
  }
  return IntPtr.Zero;
}
internal const int
  LBN_SELCHANGE = 0x00000001,
  WM_COMMAND = 0x00000111,
  LB_GETCURSEL = 0x00000188,
  LB_GETTEXTLEN = 0x0000018A,
  LB_ADDSTRING = 0x00000180,
  LB_GETTEXT = 0x00000189,
  LB_DELETESTRING = 0x00000182,
  LB_GETCOUNT = 0x0000018B;

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
                                       int msg,
                                       IntPtr wParam,
                                       IntPtr lParam);

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
                                       int msg,
                                       int wParam,
                                       [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam);

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hwnd,
                                          int msg,
                                          IntPtr wParam,
                                          String lParam);

Private Function ControlMsgFilter(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
    Dim textLength As Integer

    handled = False
    If msg = WM_COMMAND Then
        Select Case CUInt(wParam.ToInt32()) >> 16 And &HFFFF 'extract the HIWORD
            Case LBN_SELCHANGE 'Get the item text and display it
                selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero)
                textLength = SendMessage(listControl.hwndListBox, LB_GETTEXTLEN, IntPtr.Zero, IntPtr.Zero)
                Dim itemText As New StringBuilder()
                SendMessage(hwndListBox, LB_GETTEXT, selectedItem, itemText)
                selectedText.Text = itemText.ToString()
                handled = True
        End Select
    End If
    Return IntPtr.Zero
End Function
Friend Const LBN_SELCHANGE As Integer = &H1, WM_COMMAND As Integer = &H111, LB_GETCURSEL As Integer = &H188, LB_GETTEXTLEN As Integer = &H18A, LB_ADDSTRING As Integer = &H180, LB_GETTEXT As Integer = &H189, LB_DELETESTRING As Integer = &H182, LB_GETCOUNT As Integer = &H18B

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Function

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As StringBuilder) As Integer
End Function

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
End Function

コントロールとページ間の通信を実装する

コントロールを操作する場合は、Windows メッセージを送信します。 コントロールは、ユーザーが操作を行うと、ホストウィンドウに通知を送って通知します。 WPF での Win32 ListBox コントロールのホストのサンプルには、このしくみのいくつかの例を示す UI が含まれています。

  • リストに項目を追加します。

  • 選択した項目を一覧から削除する

  • 現在選択されている項目のテキストを表示します。

  • リスト内の項目数を表示します。

ユーザーは、従来の Win32 アプリケーションの場合と同様に、リスト ボックス内の項目をクリックして選択することもできます。 ユーザーが項目を選択、追加、または挿入してリストボックスの状態を変更するたびに、表示されるデータは更新されます。

項目を追加するには、リスト ボックスに LB_ADDSTRING メッセージを送信します。 アイテムを削除するには、 LB_GETCURSEL を送信して現在の選択範囲のインデックスを取得し、 LB_DELETESTRING してアイテムを削除します。 また、サンプルは LB_GETCOUNTを送信し、返された値を使用して項目の数を示す表示を更新します。 SendMessageのこれらの両方のインスタンスでは、前のセクションで説明した PInvoke 宣言のいずれかを使用します。

private void AppendText(object sender, EventArgs args)
{
  if (!string.IsNullOrEmpty(txtAppend.Text))
  {
    SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, txtAppend.Text);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" + itemCount.ToString();
}
private void DeleteText(object sender, EventArgs args)
{
  selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero);
  if (selectedItem != -1) //check for selected item
  {
    SendMessage(hwndListBox, LB_DELETESTRING, (IntPtr)selectedItem, IntPtr.Zero);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" + itemCount.ToString();
}
Private Sub AppendText(ByVal sender As Object, ByVal args As EventArgs)
    If txtAppend.Text <> String.Empty Then
        SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, txtAppend.Text)
    End If
    itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
    numItems.Text = "" & itemCount.ToString()
End Sub
Private Sub DeleteText(ByVal sender As Object, ByVal args As EventArgs)
    selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero)
    If selectedItem <> -1 Then 'check for selected item
        SendMessage(hwndListBox, LB_DELETESTRING, New IntPtr(selectedItem), IntPtr.Zero)
    End If
    itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
    numItems.Text = "" & itemCount.ToString()
End Sub

ユーザーが項目を選択するか、選択内容を変更すると、コントロールはホスト ウィンドウに WM_COMMAND メッセージを送信して通知し、ページの MessageHook イベントを発生させます。 ハンドラーは、ホスト ウィンドウのメイン ウィンドウ プロシージャと同じ情報を受け取ります。 また、ブール値 ( handled) への参照も渡します。 handledtrueに設定して、メッセージを処理し、それ以上の処理は必要ないことを示します。

WM_COMMAND はさまざまな理由で送信されるため、通知 ID を調べて、それが処理するイベントであるかどうかを判断する必要があります。 ID は、 wParam パラメーターの上位ワードに含まれています。 このサンプルでは、ビット演算子を使用して ID を抽出します。 ユーザーが選択を行ったり変更したりした場合、ID は LBN_SELCHANGEされます。

LBN_SELCHANGEを受信すると、サンプルは、コントロールにLB_GETCURSELメッセージを送信することによって、選択した項目のインデックスを取得します。 テキストを取得するには、最初に StringBuilderを作成します。 次に、コントロールに LB_GETTEXT メッセージを送信します。 空の StringBuilder オブジェクトを wParam パラメーターとして渡します。 SendMessageが返されると、StringBuilderには選択した項目のテキストが含まれます。 この SendMessage の使用には、さらに別の PInvoke 宣言が必要です。

最後に、 handledtrue に設定して、メッセージが処理されたことを示します。

こちらも参照ください