オブジェクトの削除に伴うデータリンクの削除 †
ここでは拡張プラグインを使ってオブジェクトに何らかのデータを関連付けた際に、clsやclrobjなど、オブジェクトの削除に伴って、リンクされたデータの削除方法を解説していきます。
- スクリプトに
1
|
| set_delmsg 0, "Good bye !"
|
と書くとp1で指定したIDのオブジェクトが削除される際、p2で指定したメッセージをメッセージボックスに表示し、[ OK ]ボタンを押したあとは通常通りオブジェクトを削除するプラグインを作る。
- HSPOBJINFO 構造体を使って、オブジェクトとデータ、両方の削除を同期する方法を学ぶ。
+
| | step5Main.cpp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
-
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
|
!
-
|
|
!
-
|
!
| #define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "hsp3plugin.h"
#include "delmsg.h"
static int cmdfunc( int cmd )
{
HSPOBJINFO *objinf; BMSCR *bm;
int id;
char *str, *text;
code_next();
switch( cmd ) {
case 0x00 :
id = code_geti();
str = code_gets();
text = hspmalloc( lstrlen( str ) + 1 );
lstrcpy( text, str );
bm = (BMSCR *)getbmscr( active_window );
if (( id < 0 ) || ( bm->objmax <= id ))
puterror( HSPERR_ILLEGAL_FUNCTION );
objinf = bm->mem_obj;
if( objinf[id].hCld != NULL )
set_delmsg( &objinf[id], text );
break;
case 0x01 :
enum_delmsg();
break;
default:
puterror( HSPERR_UNSUPPORTED_FUNCTION );
}
return RUNMODE_RUN;
}
EXPORT void WINAPI reg_delmsg( HSP3TYPEINFO *info )
{
hsp3sdk_init( info );
info->cmdfunc = cmdfunc;
}
BOOL WINAPI DllEntryPoint(HINSTANCE hInst, DWORD dwReason, LPVOID lpvReserved)
{
return TRUE;
}
|
|
+
| | delmsg.h
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
-
|
|
|
|
|
!
| #ifndef __delmsg_h
#define __delmsg_h
typedef struct _msginfo{
HWND hCld; char *text;
void (*delfunc)( HSPOBJINFO *); struct _msginfo *next; } MSGINFO;
void set_delmsg( HSPOBJINFO *, char * );
void enum_delmsg( void );
#endif
|
|
+
| | delmsg.cpp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
-
|
|
|
-
|
|
|
!
|
!
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
-
|
|
-
|
|
|
-
|
|
|
|
|
|
|
|
|
|
!
!
-
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
!
|
|
!
| #define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "hsp3plugin.h"
#include "delmsg.h"
static MSGINFO *head = NULL;
static MSGINFO **prev;
static MSGINFO *find_delmsg( HSPOBJINFO *objinf )
{
MSGINFO *msginf = head;
prev = &head;
while( msginf != NULL ){
if( msginf->hCld == objinf->hCld ) return msginf; prev = &msginf->next;
msginf = msginf->next;
}
return NULL; }
static void put_delmsg( HSPOBJINFO *objinf )
{
MSGINFO *msginf;
void (*func)( HSPOBJINFO * );
if(( msginf = find_delmsg( objinf )) == NULL )
puterror( HSPERR_DLL_ERROR );
MessageBox(( (BMSCR *)objinf->bm )->hwnd, msginf->text, "終了時メッセージ", MB_OK );
func = msginf->delfunc; *prev = msginf->next;
hspfree( msginf->text ); hspfree( msginf );
(*func)( objinf );
}
void set_delmsg( HSPOBJINFO *objinf, char *text )
{
MSGINFO *msginf;
if(( msginf = find_delmsg( objinf )) != NULL ){
hspfree( msginf->text ); msginf->text = text; }else{
msginf = (MSGINFO *)hspmalloc( sizeof(MSGINFO) );
msginf->hCld = objinf->hCld; msginf->text = text; msginf->delfunc = objinf->func_delete;
*prev = msginf;
objinf->func_delete = put_delmsg; }
}
void enum_delmsg( void )
{
MSGINFO *msginf;
HANDLE hFile;
DWORD dwSize;
hFile = CreateFile(
"report.txt", GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
);
if( hFile == INVALID_HANDLE_VALUE )
puterror( HSPERR_DLL_ERROR );
msginf = head;
while( msginf != NULL ){
WriteFile( hFile, msginf->text, lstrlen( msginf->text ), &dwSize, NULL );
WriteFile( hFile, "\n", lstrlen( "\n" ), &dwSize, NULL );
msginf = msginf->next;
}
CloseHandle( hFile );
}
|
|
今回新たに作った関数は以下の4つです。
- set_delmsg
オブジェクトのHSPOBJINFO構造体へのポインタと文字列へのポインタを受け取り、リストに登録する関数。
該当するオブジェクトが既にリストに登録されている場合は、文字列を変更する。
- find_delmsg
リストを探索して指定されたオブジェクトに一致するセルへのポインタを返す関数。
見つからない場合はNULLを返す。
- put_delmsg
オブジェクトが削除されようとした時にHSPから呼ばれる関数。
与えられたHSPOBJINFO構造体へのポインタを元に、リストを探索し登録されている文字列を表示する。
ユーザーが[OK]ボタンを押した後、該当セルをリストから削除し、既存の削除関数を呼び出してオブジェクトを削除する。
- enum_delmsg
リストに登録されている文字列を順次参照し、ファイルに書き出す関数。( おまけ )
ヘッダを見てもらうと分かる通り、delmsgプラグインではデータの管理法に片方向リンクリスト( 以下、リスト )を使っています。
ここでのデータとはオブジェクトと対の表示する文字列になります。
オブジェクトと文字列を関連付けることは簡単ですがオブジェクトがプラグインの知らないところで削除されたのを放っておくと無駄なメモリを使い続けることになるので、オブジェクトの削除と同時にデータをメモリから解放させることが目的です。
リストの要素( セル )はオブジェクトのハンドルとメッセージを対で管理します。
このハンドルは削除関数の引数で渡される削除される、オブジェクトのHSPOBJINFO構造体との比較用のキーになります。
また、メッセージを表示しただけではオブジェクトは削除されないので既存の削除関数へのポインタを保持しておきます。
こうしておけば既存の削除関数の具体的な処理を知らなくても関数ポインタを通して関数を呼べば適切な処理をし、オブジェクトが削除されるはずです。( 断言しない理由はすぐ下 )
場合によっては既存の削除関数はHSPオリジナルの削除関数ではなく、他のプラグインの削除関数かも知れませんが、他のプラグインが独自の処理をした後でキチンと既存の削除関数を呼び出していれば問題はないと思います。
でももっとウィンドウとデータを一対一で管理する方法もありますがここでは詳しく説明しません。
簡単に説明すると1つのウィンドウには1つのウィンドウ作成データを持たせることができます。
1
|
| SetWindowLong( hwnd, GWL_USERDATA, lpdata );
|
などとしてデータへのポインタをウィンドウに関連付けることができます。
ただ今回のプラグインもそうですがこのテの手法は複数のプラグインを同時に使った時に競合する恐れがあります。
clsやclrobjなどオブジェクトを削除しようとした時、HSPはそのオブジェクトのHSPOBJINFO構造体のオブジェクト削除関数へのポインタfunc_deleteメンバを通して削除関数を呼び出し、オブジェクトを削除します。
func_deleteメンバはHSPOBJINFO構造体の中で次のように定義されています。
1
|
| void (*func_delete)( struct HSPOBJINFO * );
|
ドキュメントではプラグイン独自のコントロールを配置する際にHSPOBJINFO構造体を設定する、というような記述がありますがオブジェクトの配置後でもHSPOBJINFO構造体の内容を変更することが可能です。
delmsgプラグインではオブジェクトの削除に合わせて、リストから削除するオブジェクトに該当するセルも削除したいため、既存の削除関数をラップさせた独自の削除関数put_delmsgを定義しています。
独自に定義する削除関数の戻り値・仮引数は次のように関数ポインタの戻り値・仮引数の型に合わせなければなりません。
1
2
3
4
|
-
|
!
| void myDeleteFunction( HSPOBJINFO *objinf )
{
}
|
ソースを良く見てみると今回はHSPOBJINFO構造体を取得するgetobj関数を使っていません。
HSPOBJINFO構造体を取得し、set_delmsg関数にHSPOBJINFO構造体へのポインタと、テキストへのポインタを渡してリストにメッセージを登録する部分を抜粋すると・・・
1
2
3
4
5
6
7
8
9
10
11
12
|
| bm = (BMSCR *)getbmscr( active_window );
if (( id < 0 ) || ( bm->objmax <= id ))
puterror( HSPERR_ILLEGAL_FUNCTION );
objinf = bm->mem_obj;
if( objinf[id].hCld != NULL )
set_delmsg( &objinf[id], text );
|
としています。
getobj関数の場合はHSPOBJINFO構造体の内容をコピーしますが、HSPはオブジェクトの情報を必要としたときBMSCR構造体のHSPOBJINFO構造体へのポインタmem_objメンバの指す構造体配列を参照するので、そちらを書き換えなければなりません。
よってgetobj関数は使わずにBMSCR構造体へのポインタを取得し、ポインタを辿って目的のHSPOBJINFO構造体へのポインタをset_delmsg関数に渡しています。
+
| | プラグインを使わないサンプル
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
-
|
!
-
|
-
|
!
| objsize 100
pos 0, 0: button gosub "Delete ->", *del
delmsg.stat = "一番上のボタンを削除するよ"
pos 110, 0: s="": input s
delmsg.stat = "入力ボックスを削除します"
pos 0, 30: button gosub "Delete ->", *del
delmsg.stat = "上から二番目のボタンを削除なり"
pos 110, 30: chkbox "Check Please", chk
delmsg.stat = "チェックボックスが削除されます"
pos 0, 60: button gosub "Delete ->", *del
delmsg.stat = "上から三番目のボタンを削除なり"
pos 110, 60
list = "Item\nSelect\nPlease"
combox sel, 100, list
delmsg.stat = "コンボボックスを削除します"
pos 0, 90: button gosub "CLS", *del
delmsg.stat = "残っているオブジェクトを消しました"
repeat 7
old.cnt = objinfo( cnt, 2 )
loop
*lpstart
wait 10
repeat 7
new.cnt = objinfo( cnt, 2 )
if old.cnt != new.cnt{
dialog delmsg.cnt, 0, "削除時メッセージ"
}
old.cnt = new.cnt
loop
goto *lpstart
*del
if stat = 6{
clrobj 0, -1
}else{
clrobj stat+1, stat+1
}
return
|
|
+
| | プラグインを使ったサンプル
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
| #regcmd "reg_delmsg", "delmsg.hpi"
#cmd set_delmsg $00
screen 0, 320, 240
*start
if count\10 = 0: pos count/10*64\320, 0
button "Button"+count, *dmy
title ""+count+"番目のボタンのID : "+stat
if count\2: set_delmsg stat, "Button ID : "+stat+"を削除します"
stop
*dmy
clrobj stat, stat
count++
goto *start
|
|
- というわけで大筋だけ書いて一休み´ー` -- kz3
- 「既存の削除関数〜」のところで一言。delmsgプラグインではリストに該当オブジェクトが登録済みの場合は表示文字列の変更するだけで削除関数へのポインタは更新しません。ということはあっちで張替え、こっちで張替えを繰り返すとマズいです。 -- kz3
- リストからちゃんとテキストが削除されているかどうか・・・ということで出力関数追加しました( enum_delmsg )。report.txtというファイルにリストの先頭セルから順番にメッセージを書き出していきます。「再読込」などのあるエディタで確認してみてください。 -- kz3