リストビューのソートの方法
リストビューのソートの方法
こんにちは、早速ですが、以下の問題の解決方法がわかりません。どうすればいいのでしょうか?
ListViewにSendMessage(hList,LVM_SORTITEMS,0,0)を送ると、一番左のカラムの部分でソートが行われると思うのですが、これを2番目以降のカラムの部分でソートを行いたいのですが、0の部分を1とかに変更しても"変わらない・エラーが発生してしまい"、方法が分からず困っています。
一応質問は順番(確か"記号→012…→abc…→あいう…→漢字")ですが、もしよければ逆の方法も教えてくれるとありがたいです。(別に教えてもらえなくても結構です。)
ListViewにSendMessage(hList,LVM_SORTITEMS,0,0)を送ると、一番左のカラムの部分でソートが行われると思うのですが、これを2番目以降のカラムの部分でソートを行いたいのですが、0の部分を1とかに変更しても"変わらない・エラーが発生してしまい"、方法が分からず困っています。
一応質問は順番(確か"記号→012…→abc…→あいう…→漢字")ですが、もしよければ逆の方法も教えてくれるとありがたいです。(別に教えてもらえなくても結構です。)
Re: リストビューのソートの方法
ソート用の関数を自前で定義する必要があるみたいですね。ListViewにSendMessage(hList,LVM_SORTITEMS,0,0)を送ると、一番左のカラムの部分でソートが行われると思うのですが、これを2番目以降のカラムの部分でソートを行いたいのですが、0の部分を1とかに変更しても"変わらない・エラーが発生してしまい"、方法が分からず困っています。
私もよく知らない&現在AB環境が無いのでサンプルを作れないですが、
http://www.kumei.ne.jp/c_lang/sdk2/sdk_110.htm
が参考になるかと思います。C/C++言語の解説ページ(「猫でも分かる~」)ですが、
使っている構造体はABでも大抵定義されていると思いますので、
なんとか移植できるかと…。
で、リストビューの任意カラムでのソートは私も知りたいので、
AB用のリストビューソートライブラリにまとめてもらえると嬉しいです♪(マテ。
ははは、自分でやれって罠?(爆)。
まぁせめても、ってことで、
たぶんABには定義されて無いだろうListView_SortItemsマクロを書いてみました。
コード: 全て選択
'// commctrl.hより抜粋
'#define ListView_SortItems(hwndLV, _pfnCompare, _lPrm) \
' (BOOL)SNDMSG((hwndLV), LVM_SORTITEMS, (WPARAM)(LPARAM)(_lPrm), \
' (LPARAM)(PFNLVCOMPARE)(_pfnCompare))
Sub ListView_SortItems( hWnd As DWord, pfnCompare As DWord, lParamSort As Long )
SendMessage( hWnd, LVM_SORTITEMS, lParamSort, pfnCompare )
EndSub
AddressOfで得る必要があります、たぶん。
その関数の組み方しだいで、昇順やら降順やら好きなようにソートできそうですね。
もう一つ。
たぶんABでは定義されていない(少なくともVer.3.13では)リストビュー関連の構造体やらマクロなど。
こちらをクリック [ここをクリックすると内容が表示されます]
コード: 全て選択
'参考
'http://hira.hopto.org/absample1.htm#ListView
'http://www.kumei.ne.jp/c_lang/sdk2/sdk_198.htm
'拡張リストビューのチェックボックスの有無を調べるのに必要
Const LVIS_STATEIMAGEMASK = &hF000
Function ListView_GetCheckState( hListV As Long, nIndex As Long )
ListView_GetCheckState = ( SendMessage( hListV, LVM_GETITEMSTATE, nIndex, LVIS_STATEIMAGEMASK) >> 12) - 1
EndFunction
'上記とは逆に、チェックをつける/外すを受け持つ。
Sub ListView_SetCheckState( hListV As DWord, nIndex As Long, fCheck As Long )
Dim lvitem As LVITEM
With lvitem
.stateMask = LVIS_STATEIMAGEMASK
.state = (fCheck + 1) << 12
End with
SendMessage( hListV, LVM_SETITEMSTATE, nIndex, VarPtr(lvitem) )
EndSub
'リストビュー用LV_COLUMN構造体
Type LV_COLUMN
mask As Long
fmt As Long
cx As Long
pszText As String
cchTextMax As Long
iSubItem As Long
End Type
'リストビュー用LV_ITEM構造体
Type LV_ITEM
mask As Long
iItem As Long
iSubItem As Long
state As Long
stateMask As Long
pszText As BytePtr
cchTextMax As Long
iImage As Long
lParam As Long
End Type
'リストビュー用LV_DISPINFO構造体
Type LV_DISPINFO
hdr As NMHDR
item As LV_ITEM
End Type
'アイテムを追加。
'Text=一番左の列に表示する文字列、hWnd=ListViewコントロールのハンドル、
'Index=アイテムの番号(通常は上から0,1,2…と通し番号にすればOK)
Sub AddListVItem( hWnd As Long, nIndex As Long, pText As BytePtr )
Dim lvitem As LV_ITEM
lvitem.mask = LVIF_TEXT
lvitem.iItem = nIndex
lvitem.pszText = pText
lvitem.iSubItem = 0
SendMessage( hWnd , LVM_INSERTITEM, NULL, VarPtr( lvitem ) )
End Sub
'サブアイテムを設定。
'Text=設定する文字列、hWnd=ListViewコントロールのハンドル、Index=アイテムの番号、IndexSub=列番号(一番左の列が0、2列目が1…)
Sub SetListVItem( hWnd As Long, nIndex As Long, nIndexSub As Long, pText As BytePtr )
Dim lvitem As LV_ITEM
lvitem.mask = LVIF_TEXT
lvitem.iItem = nIndex
lvitem.pszText = pText
lvitem.iSubItem = nIndexSub
SendMessage( hWnd , LVM_SETITEM, NULL, VarPtr( lvitem ) )
End Sub
'アイテムを追加する(サブもメインも関係なくw)
Sub AddListViewItem( hWnd As Long, nIndex As Long, nIndexSub As Long, pText As BytePtr )
If( nIndexSub=0 )Then
AddListVItem( hWnd, nIndex, pText )
Else
SetListVItem( hWnd, nIndex, nIndexSub, pText )
EndIf
EndSub
'アイテムを削除。
'hWnd=ListViewコントロールのハンドル、Index=アイテムの番号
Sub DelListItem( hWnd As Long, nIndex As Long )
SendMessage( hWnd, LVM_DELETEITEM, nIndex, 0 )
End Sub
'全アイテムを削除。
'hWnd=ListViewコントロールのハンドル
Sub DelAllListItem( hWnd As Long )
SendMessage( hWnd, LVM_DELETEALLITEMS, NULL, NULL )
End Sub
'カラムを削除。
'hWnd=ListViewコントロールのハンドル、Index=カラムの番号。但し0(一番左のカラム)を指定することはできない。
Sub DelColumn( hWnd As Long, nIndex As Long)
SendMessage( hWnd, LVM_DELETECOLUMN, nIndex, NULL )
End Sub
'選択されているアイテムの番号を取得。
'hWnd=ListViewコントロールのハンドル
Function FindSelListItem( hWnd As Long ) As Long
FindSelListItem=SendMessage( hWnd, LVM_GETITEMCOUNT, -1, 2 )
'&H100C
End Function
Re: リストビューのソートの方法
淡幻星さん、回答ありがとうございます。
URL参考にさせてもらいますが、自分では解読が困難なので、もう少し他の方の回答を待ってみます。
(実はサンプルが欲しかったりするだけ)(←こういうユーザは嫌われそうな・・・(^^;;;;))
URL参考にさせてもらいますが、自分では解読が困難なので、もう少し他の方の回答を待ってみます。
(実はサンプルが欲しかったりするだけ)(←こういうユーザは嫌われそうな・・・(^^;;;;))
Re: リストビューのソートの方法
少なくとも、好まれはしないと思います。(実はサンプルが欲しかったりするだけ)(←こういうユーザは嫌われそうな・・・(^^;;;;))
・・・が、「サンプルが欲しいんです!」って気持ちはよ~く分かります^^;
なので、私は悪いとは思いません。
やっぱ、サンプルがなくちゃ分かりませんよね~┐(´ー`)┌
さて。
リストビューの任意のカラムを昇順/降順に並び替える関数を作ってみました。
ListView_SortItems_UpDown()です。
ただし、テストしてませんので(現在AB環境にありません故^^;)
エラーしたらごめんなさい。
なお、これを利用するためには、アイテムをリストボックスに追加する際に、
LV_COLUMN構造体のmaskメンバにLVIF_PARAMを指定しておく必要があるみたいです。
コードはこちらをクリック。 [ここをクリックすると内容が表示されます]
BackSearchABのysamaさんのログ(No.8905)を参考にさせていただきました。コード: 全て選択
Dim g_hListView As DWord
Dim g_fListViewSort As Long
Const MaxPath_LV = 260
'リストビュー(hWnd)のサブインデックス(nSubIndexSort)のカラムを
'昇順(fUpDown=+1)/降順(fUpDown=-1)に並び替える。
Sub ListView_SortItems_UpDown( hWnd As DWord, nSubIndexSort As Long, fUpDown As Long )
g_hListView = hWnd
If( fUpDown>0 )Then
g_fListViewSort = +1
Else
g_fListViewSort = -1
EndIf
ListView_SortItems( hWnd, AddressOf( CompareProc ), nSubIndexSort )
EndSub
'指定されたサブアイテムインデックスのカラムを並び替える
Sub ListView_SortItems( hWnd As DWord, pfnCompare As DWord, lParamSort As Long )
SendMessage( hWnd, LVM_SORTITEMS, lParamSort, pfnCompare )
EndSub
'アイテムのインデックスを得る+使用する構造体
Type LV_FINDINFO
flags As DWord
psz As BytePtr
lParam As Long
pt As POINTAPI
vkDirection As DWord
EndType
Sub ListView_FindItem( hWnd As DWord, iStart As Long, ByRef plvfi As LV_FINDINFO )
SendMessage( hWnd, LVM_FINDITEM, iStart, plvfi )
EndSub
'#define ListView_FindItem(hwnd, iStart, plvfi) \
' (int)SNDMSG((hwnd), LVM_FINDITEM, (WPARAM)(int)(iStart), (LPARAM)(const LV_FINDINFO *)(plvfi))
'アイテムインデックスから内容を得る
Sub ListView_GetItemText( hWnd As DWord, iItem As Long, iSubItem As Long, pszText As BytePtr, cchTextMax As Long )
Dim _ms_lvi As LV_ITEM
_ms_lvi.iSubItem = iSubItem
_ms_lvi.cchTextMax = cchTextMax
_ms_lvi.pszText = pszText
SendMessage( hWnd, LVM_GETITEMTEXT, iItem, _ms_lvi )
EndSub
'#define ListView_GetItemText(hwndLV, i, iSubItem_, pszText_, cchTextMax_) \
'{ LV_ITEM _ms_lvi;\
' _ms_lvi.iSubItem = iSubItem_;\
' _ms_lvi.cchTextMax = cchTextMax_;\
' _ms_lvi.pszText = pszText_;\
' SNDMSG((hwndLV), LVM_GETITEMTEXT, (WPARAM)(i), (LPARAM)(LV_ITEM *)&_ms_lvi);\
'}
'比較のためのコールバック関数
Function CompareProc( lp1 As Long, lp2 As Long, lpSort As Long ) As Long
Dim nItem1 As Long
Dim nItem2 As Long
Dim bBuf1[ MaxPath_LV ] As Byte
Dim bBuf2[ MaxPath_LV ] As Byte
Dim lvf As LV_FINDINFO
lvf.flags = LVFI_PARAM
lvf.lParam = lp1
nItem1 = ListView_FindItem( g_hListView, -1, lvf )
lvf.lParam = lp2
nItem2 = ListView_FindItem( g_hListView, -1, lvf )
ListView_GetItemText( g_hListView, nItem1, lpSort, bBuf1, MaxPath_LV )
ListView_GetItemText( g_hListView, nItem2, lpSort, bBuf2, MaxPath_LV )
If( g_fListViewSort>0 )Then
'昇順
CompareProc = lstrcmp( bBuf1, bBuf2 )
Else
'降順
CompareProc = lstrcmp( bBuf1, bBuf2 ) * -1
EndIf
End Function
グローバル変数を二つほど使ってます。
「猫でも分かる~」なども見るところでは、リストビューのソートでは、
リストビューのハンドルと、昇順/降順/ソートされてないの管理を
グローバル変数で渡すのが慣例みたいですね。
たまたまなのか、何か意味があるのかは分かりません。
個人的に、グローバルは極力使いたくないのですが・・・クラス化するしかないのかな?
それから、LV_FINDINFO構造体の定義はあっているか自信がありません^^;
→修正→追記:合ってました。アンダーバー除いた形で、ABでも定義されてました(爆)。
ふぃ。いつものように蛇足。
コールバック関数って要するに何なのか未だに分からない~┐(´ー`)┌
Re: リストビューのソートの方法
> 少なくとも、好まれはしないと思います。(実はサンプルが欲しかったりするだけ)(←こういうユーザは嫌われそうな・・・(^^;;;;))
> ・・・が、「サンプルが欲しいんです!」って気持ちはよ~く分かります^^;
> なので、私は悪いとは思いません。
> やっぱ、サンプルがなくちゃ分かりませんよね~┐(´ー`)┌
やっぱりですよね。でも、そこからの改変は自分でやらないと、自分の力にはなりませんからね。あくまで動作するサンプルが欲しいのですよ。
> ふぃ。いつものように蛇足。
> コールバック関数って要するに何なのか未だに分からない~┐(´ー`)┌
英語にするとCall Back、呼び戻す?ってこのなのかな?自分もコールバック関数は良く分かりません。(^^;;
Re: リストビューのソートの方法
淡幻星さん、どうもありがとうございます。
提供していただいたサンプルを元に、一部を修正したら動作しました。
どうもありがとうございました。
提供していただいたサンプルを元に、一部を修正したら動作しました。
どうもありがとうございました。
Re: リストビューのソートの方法
> 英語にするとCall Back、呼び戻す?ってこのなのかな?
そのとおりです。WinAPIにおけるWindowsのような,
機能を提供する側が利用する側を呼び出すことをコールバックと呼び,
コールバックされる関数をコールバック関数というのです。
そのとおりです。WinAPIにおけるWindowsのような,
機能を提供する側が利用する側を呼び出すことをコールバックと呼び,
コールバックされる関数をコールバック関数というのです。
Re: リストビューのソートの方法
> > 英語にするとCall Back、呼び戻す?ってこのなのかな?
>
> そのとおりです。WinAPIにおけるWindowsのような,
> 機能を提供する側が利用する側を呼び出すことをコールバックと呼び,
> コールバックされる関数をコールバック関数というのです。
そういう意味だったんですか。イグトランスさん、回答ありがとうございます。
(あと、"このなのかな?"ってなってますが、もちろん"ことなのかな?"の間違いです(汗爆))
>
> そのとおりです。WinAPIにおけるWindowsのような,
> 機能を提供する側が利用する側を呼び出すことをコールバックと呼び,
> コールバックされる関数をコールバック関数というのです。
そういう意味だったんですか。イグトランスさん、回答ありがとうございます。
(あと、"このなのかな?"ってなってますが、もちろん"ことなのかな?"の間違いです(汗爆))
コールバック関数とは?
???イグトランスさん さんが書きました:そのとおりです。WinAPIにおけるWindowsのような,
機能を提供する側が利用する側を呼び出すことをコールバックと呼び,
コールバックされる関数をコールバック関数というのです。
つまり、コード上で明示的に呼び出される関数ではなくて、
いったんWindowsのような機能提供側(「カーネル」で良いんでしょうか?)に
処理を投げた(DispatchMessageとか)後に、提供側から決まった形(引数etc)で
呼び出される関数のことを、「制御が呼び出し側に戻された=コールバック」という意味で
コールバック関数と呼ぶ、という理解で合ってますでしょうか?
関連として、以下の理解は合っておりますでしょうか?
1) コールバック関数は、仕様としてコンパイル時点で関数アドレスが
存在していなければならない(提供側が呼び出す故?)。
2) 呼び出しの時点までアドレスが存在しない関数を仮想関数と呼ぶ。
3) クラスはオブジェクトが生成されるまでは実体が無い。
したがって、そのメソッドはコンパイル時は仮想関数である。
(故に、メソッドにstatic以外ではコールバック関数を持てない。)
4) C/C++でいうところの WINAPI 識別子(で良いのかな?)が
付いているものは、コールバック関数である。
5) 現在、ABにはstatic識別子はない。
話が脱線してしまいすいませんが、答えていただければ幸いです。
Re: コールバック関数とは?
OSから送られてきたメッセージをGetMessage関数で捕まえて、DispatchMeesage関数を呼び出した時に、適切なウィンドウにメッセージが送信されます。これを受けてウィンドウ(OS)はコールバック関数を呼び出します。> つまり、コード上で明示的に呼び出される関数ではなくて、
> いったんWindowsのような機能提供側(「カーネル」で良いんでしょうか?)に
> 処理を投げた(DispatchMessageとか)後に、提供側から決まった形(引数etc)で
> 呼び出される関数のことを、「制御が呼び出し側に戻された=コールバック」という意味で
> コールバック関数と呼ぶ、という理解で合ってますでしょうか?
ようするに、OSさんがプログラマに
「こんなメッセージが送られてきたんですけど、このウィンドウはどんなふうに動くんですか?」
と、コールバック関数を介して、聞きに来ているんですね。
コンパイル時でなくても実行時にアドレスが存在していれば問題ないです。> 関連として、以下の理解は合っておりますでしょうか?
> 1) コールバック関数は、仕様としてコンパイル時点で関数アドレスが
> 存在していなければならない(提供側が呼び出す故?)。
例えば、DLL内にコールバック関数を収めておき、呼び出す関数をGetProcAddress関数なんぞで切り替えてやれば、プラグインなんかも作れます。
なんともいえません。> 2) 呼び出しの時点までアドレスが存在しない関数を仮想関数と呼ぶ。
開発環境が違ってくると仮想関数も違ってきます。
今の現在(AB4)のところABでは厳密な意味での仮想関数は使えません。
(所謂、VCでいうところのvirtual指定は使えない)
基本的にはそうです。> 3) クラスはオブジェクトが生成されるまでは実体が無い。
> したがって、そのメソッドはコンパイル時は仮想関数である。
> (故に、メソッドにstatic以外ではコールバック関数を持てない。)
SetProp関数だったかなんかで、クラスのメソッドにコールバック関数を含めることが出来たような気がします。
追記2005/12/22/23:10ごろ
SetProp関数が使えるのはウィンドウプロシージャだけのようです。
でも、グローバルアトムとか使ってデータの共有化を行えばVatPtr(This)ポインタとかをコールバック関数に引き渡すことが出来そうです。
とくにそう決まっているわけではありません。> 4) C/C++でいうところの WINAPI 識別子(で良いのかな?)が
> 付いているものは、コールバック関数である。
WINAPIというのは関数がstdcall形式で定義されていることを表しています。
標準APIではありませんがコールバック関数の中にはごくまれにcdecl形式のものも存在します。
そうです。ぜひ欲しいですよね。> 5) 現在、ABにはstatic識別子はない。
私も(笑
話はそれますが、
コールバック関数は自分で作成した関数でも使えますよ。
下はファイル名を列挙するサンプルです。
コード: 全て選択
TypeDef ENUMFILEPROC = *Function(ByRef lpwfd As WIN32_FIND_DATA) As Long
Function EnumFile(lpFileName As *Char, lpfnCallback As ENUMFILEPROC) As Long
If lpfnCallback=NULL Then
EnumFile = FALSE
Exit Function
End If
Dim hFind As HANDLE
Dim w32fd As WIN32_FIND_DATA
hFind=FindFirstFile(lpFileName,w32fd)
If hFind=INVALID_HANDLE_VALUE Then
EnumFile = FALSE
Exit Function
End If
While lpfnCallback(w32fd)=0
If FindNextFile(hFind,w32fd)=FALSE Then Exit While
Wend
FindClose(hFind)
EnumFile = TRUE
End Function
コード: 全て選択
Function test(ByRef wfd As WIN32_FIND_DATA) As Long
If InStr(1,MakeStr(wfd.cFileName),"WINDOWS") Then
MessageBox(NULL,wfd.cFileName,"test",0)
test=1
End If
End Function
EnumFile("C:\*.*",AddressOf(test))
Re: コールバック関数とは?
そういうことになります。(ただ全てがカーネルとは限りませんが)つまり、コード上で明示的に呼び出される関数ではなくて、
いったんWindowsのような機能提供側(「カーネル」で良いんでしょうか?)に
処理を投げた(DispatchMessageとか)後に、提供側から決まった形(引数etc)で
呼び出される関数のことを、「制御が呼び出し側に戻された=コールバック」という意味で
コールバック関数と呼ぶ、という理解で合ってますでしょうか?
そもそもクラスのメンバ関数を含め全ての関数はメモリ上に存在する以上,どんな関数にも必ずアドレスがあります。1) コールバック関数は、仕様としてコンパイル時点で関数アドレスが
存在していなければならない(提供側が呼び出す故?)。
(インライン展開されてしまえば別ですが)
と書きましたがNoWestさんのDLLというのは見過ごしていました。
ただそれでも依然DLLのコンパイル時に存在するということになります。
いいえ。仮想関数と言えども上記1の理由で実体へのアドレスは存在します。2) 呼び出しの時点までアドレスが存在しない関数を仮想関数と呼ぶ。
呼び出すときまでアドレスが存在しないのではなく,呼び出すときまでわからないというだけです。
どういうことかというと仮想関数へのポインタが収められた表(VTBL)が
オブジェクトの中に埋め込まれているので,
これを参照してみないことにはどこの関数を呼び出してよいかわからないからです。
逆に言えば対象となるオブジェクトが定まれば呼び出す仮想関数の実体も決まるということです。
これはAB4のVirtual関数にも当てはまることです。
概ねいいえです。3) クラスはオブジェクトが生成されるまでは実体が無い。
したがって、そのメソッドはコンパイル時は仮想関数である。
どういうことかというとオブジェクトが生成されるまでクラスの実体がないというのは概念上間違っていないのですが,
現実にはメンバ関数もその他と同じように機械語になるという点において,結局はその他の関数と何も変りません。
仮想関数か否かはあくまで仮想関数だという指定がなされているかどうかによります。
(少なくともABやC++/C#などの場合はそうです。その他の方法で仮想関数かどうか決める言語もあります)
これは前述の通りメンバ関数が実際には単なる関数と変らないことに起因します。(故に、メソッドにstatic以外ではコールバック関数を持てない。)
メンバ関数は宣言に書いた引数以外にオブジェクトへのポインタを引数に取ることからそのままでは引数の数が合わず,
さらに呼び出し規約も合わないためということもあります。
逆にこれらの問題を解決すればできるということでもあります。COMオブジェクトが良い例です。
WinAPIのコールバック関数でも大抵余計な引数を受け渡しできるようになっています。
これを使えばなんとかなります。
大抵そうです。4) C/C++でいうところの WINAPI 識別子(で良いのかな?)が
付いているものは、コールバック関数である。
厳密に言えばコールバック関数でなくともWINAPIをつけられるのです。あまり意味はありませんが。
逆に言えばお互いにきちんと決めて統一してあればどのような関数でもコールバック関数になれます。
とは言えWindowsプログラムではWINAPIが一般的ですけどね。
これはたしかにそうです。明白ですね。5) 現在、ABにはstatic識別子はない。
Re: コールバック関数とは?≫NoWestさん
NoWestさん、ありがとうございます。
普通に関数を呼んでいるだけじゃないかと。
ん~、もしかして「コールバック関数」とは、
たとえば、サンプルであれば
While lpfnCallback(w32fd)=0
で呼び出される関数は、Function EnumFile() の引数次第で変更できますよね。
でもこの機能は、コールバック関数ゆえの機能というより
関数ポインタゆえの機能な気がしますが・・・(^^;)
それとも、関数ポインタを通して呼ばれるものを
「コールバック関数」と呼ぶのでしょうか?
でも、だとするとCreateThread()で呼ばれるサブスレッドも
コールバック関数になってしまいますね。
でもあれはコールバック関数ではないらしい・・・。うーん難しい(@_@)
「仮想関数」が指すものは、開発環境次第なのですね。
「コールバック関数を持てない」と書いたのは、直には持てない、という意味でした。
誤解を招く発言でしたね。失礼^^;
グローバルアトムを使うほかにも、
肝心のウィンドウプロシージャの場合は、SetProp関数を使うのが
一般的(MFCのCWndクラスもそれ?)みたいですが、
SetProp()はアトム(=文字列)を使っているため若干重いらしく、
それを嫌う人は
「ウィンドウに関連付けられたアプリケーション定義の 32 ビット値」
を利用してstaticメソッド→目的のメソッド、と呼び出すことで、
コールバック関数をクラスのメソッドに含めているみたいです。
(蛇足へ続く^^;)
参考http://techtips.belution.com/ja/vc/0009/
http://forums.belution.com/ja/vc/000/047/79s.shtml
なんのための識別子なのか、ひとつスッキリしました♪
以下、いつものように蛇足です(笑)。
SetProp()を使わずに、クラスのメソッドに
ウィンドウプロシージャを含める方法です。
***
CreateWindow()の第11引数(~Ex()なら第12引数)のlpParm As VoidPtrに
VatPtr(This)を渡す、つまり窓に関連付けられた32bit値として
呼び出しもとのクラスのポインタを保持して、
staticなメソッドで受けた後にGetWindowLong( hWnd, GWL_USERDATA )
で取り出してstaticでないメソッドを呼び出します。
***
もちろん、GWL_USERDATAを後で使えないのが欠点。
しかし、窓をクラス化してしまったのなら、使う場面は無いように
思うので、たぶん不具合はないかと(へヴィーなものを書く人は別^^;)。
C++ですが、サンプルは・・・と最初は載せたのですが、
あくまでも「蛇足」の話題ですし、ABのフォーラムなので止めておきます^^;。
削除しました(12月23日の2時ごろ)
すいません。こっちの説明は分かったんですが、ようするに、OSさんがプログラマに
「こんなメッセージが送られてきたんですけど、このウィンドウはどんなふうに動くんですか?」
と、コールバック関数を介して、聞きに来ているんですね。
でよく分からなくなりました。下はファイル名を列挙するサンプルです。
普通に関数を呼んでいるだけじゃないかと。
ん~、もしかして「コールバック関数」とは、
のように、送り先を選択できる関数ってことですか?適切なウィンドウにメッセージが送信されます。
たとえば、サンプルであれば
While lpfnCallback(w32fd)=0
で呼び出される関数は、Function EnumFile() の引数次第で変更できますよね。
でもこの機能は、コールバック関数ゆえの機能というより
関数ポインタゆえの機能な気がしますが・・・(^^;)
それとも、関数ポインタを通して呼ばれるものを
「コールバック関数」と呼ぶのでしょうか?
でも、だとするとCreateThread()で呼ばれるサブスレッドも
コールバック関数になってしまいますね。
でもあれはコールバック関数ではないらしい・・・。うーん難しい(@_@)
なるほど。開発環境が違ってくると仮想関数も違ってきます。
「仮想関数」が指すものは、開発環境次第なのですね。
調べていただいてありがとうございます。・・・が、そっちは知ってました(^^;)基本的にはそうです
SetProp関数だったかなんかで、クラスのメソッドにコールバック関数を含めることが出来たような気がします。
追記2005/12/22/23:10ごろ
SetProp関数が使えるのはウィンドウプロシージャだけのようです。
でも、グローバルアトムとか使ってデータの共有化を行えばVatPtr(This)ポインタとかをコールバック関数に引き渡すことが出来そうです。
「コールバック関数を持てない」と書いたのは、直には持てない、という意味でした。
誤解を招く発言でしたね。失礼^^;
グローバルアトムを使うほかにも、
といった方法があるみたいですね。一つ目の参考サイトより抜粋 さんが書きました:コールバック関数の中には、引数の中に「アプリケーション定義の変数を渡す void ポインタ」のパラメータがあるものがあります。このようなコールバック関数の場合は、そのパラメータにクラスのポインタを渡してやり、コールバック関数内でそのクラスの型にキャストして、メンバ変数や関数にアクセスすることでこの問題を解決できます。
肝心のウィンドウプロシージャの場合は、SetProp関数を使うのが
一般的(MFCのCWndクラスもそれ?)みたいですが、
SetProp()はアトム(=文字列)を使っているため若干重いらしく、
それを嫌う人は
「ウィンドウに関連付けられたアプリケーション定義の 32 ビット値」
を利用してstaticメソッド→目的のメソッド、と呼び出すことで、
コールバック関数をクラスのメソッドに含めているみたいです。
(蛇足へ続く^^;)
参考http://techtips.belution.com/ja/vc/0009/
http://forums.belution.com/ja/vc/000/047/79s.shtml
あ、そっちでしたか。WINAPIというのは関数がstdcall形式で定義されていることを表しています。
なんのための識別子なのか、ひとつスッキリしました♪
えぇ。ぜひ欲しいですねwQuote:
> 5) 現在、ABにはstatic識別子はない。
そうです。ぜひ欲しいですよね。
以下、いつものように蛇足です(笑)。
SetProp()を使わずに、クラスのメソッドに
ウィンドウプロシージャを含める方法です。
***
CreateWindow()の第11引数(~Ex()なら第12引数)のlpParm As VoidPtrに
VatPtr(This)を渡す、つまり窓に関連付けられた32bit値として
呼び出しもとのクラスのポインタを保持して、
staticなメソッドで受けた後にGetWindowLong( hWnd, GWL_USERDATA )
で取り出してstaticでないメソッドを呼び出します。
***
もちろん、GWL_USERDATAを後で使えないのが欠点。
しかし、窓をクラス化してしまったのなら、使う場面は無いように
思うので、たぶん不具合はないかと(へヴィーなものを書く人は別^^;)。
C++ですが、サンプルは・・・と最初は載せたのですが、
あくまでも「蛇足」の話題ですし、ABのフォーラムなので止めておきます^^;。
削除しました(12月23日の2時ごろ)
蛇足の蛇足(マテ)。 [ここをクリックすると内容が表示されます]
メソッドを1つ呼び出すだけで、
窓登録+窓作成+窓プロシージャ作成を行うクラスのサンプルでした。
ABでもそういうクラスを作っておけば、RAD無しでの窓作成が楽になりますよね~♪
・・・と思ったんですが、C++でいうところのvirtual指定が使えないと、
継承してメッセージ処理を追加上書きすることができないんだな~( ̄▽ ̄;)
窓登録+窓作成+窓プロシージャ作成を行うクラスのサンプルでした。
ABでもそういうクラスを作っておけば、RAD無しでの窓作成が楽になりますよね~♪
・・・と思ったんですが、C++でいうところのvirtual指定が使えないと、
継承してメッセージ処理を追加上書きすることができないんだな~( ̄▽ ̄;)
最後に編集したユーザー 淡幻星 [ 2005年12月23日(金) 02:03 ], 累計 2 回
Re: コールバック関数とは?≫イグトランスさん
≫イグトランスさん
おっと。
レスを書いているうちに、投稿されてましたか。
失礼。もうすこし読んでから、またレスします。
今一通り流し読みしただけでは理解できませんでした^^;
おっと。
レスを書いているうちに、投稿されてましたか。
失礼。もうすこし読んでから、またレスします。
今一通り流し読みしただけでは理解できませんでした^^;
Re: NoWestさんからの回答
いえいえ、その説明はコールバック関数(ここではウィンドウプロシージャ)が呼び出されるまでを説明したものです。淡幻星 さんが書きました:> ん~、もしかして「コールバック関数」とは、
>のように、適切なウィンドウにメッセージが送信されます。
送り先を選択できる関数ってことですか?
もう一度全体の流れを詳しく順に書きます。
1.アプリケーションが実行されるとウィンドウを作成しGetMessage関数を含むループが回り始める。
追記:回るとはいってもメッセージが無い状態では実際にはGetMessage関数で待機状態となっています。
2.OSからウィンドウが操作されたりキーが押されたことをメッセージとして
実行中のアプリケーションのスレッドへ送信
3.GetMessage関数がメッセージを受信すると、DispatchMessage関数を呼び出し、アプリケーションが作成したウィンドウにメッセージが送信される。
4.メッセージを受信したウィンドウはウィンドウクラスに関連付けられているウィンドウプロシージャつまりコールバック関数を呼び出す。
コールバック関数が実行されるのは最後の4.のプロセスです。
コールバック関数=関数ポインタ淡幻星 さんが書きました:> たとえば、サンプルであれば
> While lpfnCallback(w32fd)=0
> で呼び出される関数は、Function EnumFile() の引数次第で変更できますよね。
> でもこの機能は、コールバック関数ゆえの機能というより
> 関数ポインタゆえの機能な気がしますが・・・(^^;)
というのは正しくは無いですが、間違いでもありません。
コールバック関数とはシステム内部では関数ポインタの形で扱われます。
ウィンドウプロシージャもウィンドウクラスを定義する際にはAddressOfを使用して関数ポインタを登録します。
上でも書きましたが、ウィンドウは自分のウィンドウクラスに登録されているコールバック関数への関数ポインタを頼りにウィンドウプロシージャを呼び出しているだけです。
「コールバック」という言葉には「電話をかけ直す」という意味があります。淡幻星 さんが書きました:> それとも、関数ポインタを通して呼ばれるものを
> 「コールバック関数」と呼ぶのでしょうか?
> でも、だとするとCreateThread()で呼ばれるサブスレッドも
> コールバック関数になってしまいますね。
> でもあれはコールバック関数ではないらしい・・・。うーん難しい(@_@)
つまり、コールバックという仕組みは、「ある処理を実行する際に、その一部を外部で後から追加できるようにしたもの」あるいは「ある処理が確実に実行されたことを通知するもの」だと定義できます。
コールバックというのは何も「関数」だけではないんです。
コールバックスレッドやコールバックタスク、コールバックイベントなんてのもあります。
あまり、実感は湧かないかも知れませんが、
GetMessage関数を使ったループは実はコールバックスレッドなんですよ。
OSが「あなたのスレッドに、こんなウィンドウハンドルのウィンドウにこんなウィンドウメッセージが送られてきていますよ。」
と通知してきたら、プログラマはそのメッセージをウィンドウに送信するかしないかを決定できるんです。
具体的な例 [ここをクリックすると内容が表示されます]
このようにすると特定のウィンドウにだけメッセージを送るというようなことができます。
こんなことをするのはあまり良くないですが。。。
さらに下のように特定のメッセージをマスクすることもできます。
Windowsのシステムの特定の処理に対して、プログラマが選択的に処理を組むことができる仕組みがコールバックです。もちろんコールバックと言う仕組みはWindowsが提供しているものだけに限ったものではありませんし、ウィンドウ制御だけに使うわけではありません。コード: 全て選択
'---------------------------
' Window Message Loop
'---------------------------
Dim msgMain As MSG, iResult As Long
Do
iResult=GetMessage(msgMain,0,0,0)
If iResult=0 or iResult=-1 Then Exit Do
If msgMain.hwnd=hMainWnd Then
TranslateMessage(msgMain)
DispatchMessage(msgMain)
End If
Loop
ExitProcess(0)
こんなことをするのはあまり良くないですが。。。
さらに下のように特定のメッセージをマスクすることもできます。
コード: 全て選択
'---------------------------
' Window Message Loop
'---------------------------
Dim msgMain As MSG, iResult As Long
Do
iResult=GetMessage(msgMain,0,0,0)
If iResult=0 or iResult=-1 Then Exit Do
If (msgMain.hwnd=hMainWnd) and _
(msgMain.message=WM_NCLBUTTONDOWN) and _
(msgMain.wParam=HTZOOM) Then
OutputDebugString(Ex"メインウィンドウの最大化は無視されました。\n")
Else
TranslateMessage(msgMain)
DispatchMessage(msgMain)
End If
Loop
ExitProcess(0)
コールバックを使用するものには、
例えばWAVE関連のAPIや、MIDIストリーミング、OpenGL(Glut)の描画ルーチン、ウィンドウの検索、ディスプレイの列挙、GDIオブジェクトの列挙、フォントの列挙など多種多様です。
かなり話題がそれましたので
分からないことがあれば別のスレッドにでもしてください。
Re: コールバック関数とは?≫NoWestさん
いや,CreateThreadの場合も形としてはコールバック関数です。淡幻星さん さんが書きました:でも、だとするとCreateThread()で呼ばれるサブスレッドも
コールバック関数になってしまいますね。
でもあれはコールバック関数ではないらしい・・・。うーん難しい(@_@)
ただスレッドを作るという機能が強調されるので,
特にコールバックとは言われないことが多いだけですよ。
SetPropに文字列ではなくアトムを使った場合は,SetWindowLongを使った場合とほぼ同じ速度だという計測結果があります。淡幻星さん さんが書きました:SetProp()はアトム(=文字列)を使っているため若干重いらしく、
それを嫌う人は
「ウィンドウに関連付けられたアプリケーション定義の 32 ビット値」
を利用してstaticメソッド→目的のメソッド、と呼び出すことで、
コールバック関数をクラスのメソッドに含めているみたいです。
http://hilbert.elcom.nitech.ac.jp/~taki/program.html
ある程度参考になるのではないでしょうか。
これはSet/GetWindowLongでGWL_USERDATA以外を使えばいいのです。淡幻星さん さんが書きました:もちろん、GWL_USERDATAを後で使えないのが欠点。
SetClassLongでGCL_CBWNDEXTRAを指定するとGWL_USERDATAとは別に
ウィンドウ毎のメモリ領域が用意されます。
(本来はRegisterClassExで指定するのでしょうがRADツールが隠してしまっているので)
これはABのヘルプのSet/GetWindowLongの項目にある,
「拡張ウィンドウメモリからデータを取得するときは、0 から始まるオフセットをバイト単位で指定します。」
というものに該当するものです。GWL_USERDATAと同じ感覚で使えます。