ページ 1 / 1
BytePtr型の変数のメモリを解放した時にエラーが出る
Posted: 2006年5月22日(月) 21:01
by ゲスト
EditBox1の内容をBytePtr型で取得するコードを書いたのですが、
free関数でメモリ領域を解放するところでエラーが出ます。
どこが悪いのでしょうか。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
'-----------------------------------------------------------------------------
' イベント プロシージャ
'-----------------------------------------------------------------------------
' このファイルには、ウィンドウ [MainWnd] に関するイベントをコーディングします。
' ウィンドウ ハンドル: hMainWnd
' TODO: この位置にグローバルな変数、構造体、定数、関数を定義します。
'-----------------------------------------------------------------------------
' ウィンドウメッセージを処理するためのコールバック関数
Function MainWndProc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As DWord
' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。
' イベントプロシージャの呼び出しを行います。
MainWndProc=EventCall_MainWnd(hWnd,dwMsg,wParam,lParam)
End Function
'-----------------------------------------------------------------------------
' ここから下は、イベントプロシージャを記述するための領域になります。
Sub MainWnd_Destroy()
Test_DestroyObjects()
PostQuitMessage(0)
End Sub
Sub MainWnd_CommandButton1_Click()
Dim hEdit As HWND
Dim EditStr As BytePtr
Dim MemoryF As Byte
hEdit=GetDlgItem(hMainWnd,EditBox1)
Select Case MemoryF
Case 0
EditStr=malloc(SendMessage(hEdit,WM_GETTEXTLENGTH +1,0,0))
MemoryF = 1
Case 1
realloc( EditStr,SendMessage (hEdit,WM_GETTEXTLENGTH +1,0,0))
End Select
GetWindowText(hEdit,EditStr,SendMessage(hEdit,WM_GETTEXTLENGTH,0,0) +1)
MsgBox 0,MakeStr(EditStr)
free(EditStr) 'ここでエラーが出てしまいます。
End Sub
Re: BytePtr型の変数のメモリを解放した時にエラーが出る
Posted: 2006年5月22日(月) 21:29
by イグトランス
こんばんは。
とりあえず気づいた点だけを書いておきます。無愛想ですみません。
コード: 全て選択
Dim MemoryF As Byte
Select Case MemoryF
MemoryFは関数内の変数ですから,MainWnd_CommandButton1_Clickを抜ける度にその内容は破棄されます。
そのため,これでは常にCase 0へ行ってしまいます。
もっともreallocは(freeしないまま)確保するメモリの量を変えるためにあるので,
このコードのように終わりで毎回freeするのであれば,reallocを使う必要はありません。
つまりMemoryFで場合分けする必要も無いと思います。
コード: 全て選択
EditStr=malloc(SendMessage(hEdit,WM_GETTEXTLENGTH +1,0,0))
たぶんこれがfreeでだめになる根本の原因だと思います。
EditStr = malloc(SendMessage(hEdit,WM_GETTEXTLENGTH,0,0) + 1)ではないでしょうか。
ちなみに自プログラムのウィンドウですから,WM_GETTEXTLENGTHだけでなくGetWindowTextLengthを使う方法もあります。
その方がこんな間違いをせずにすむでしょう。
コード: 全て選択
realloc(EditStr,SendMessage(hEdit,WM_GETTEXTLENGTH +1,0,0))
もし本当にreallocを使うときにはEditStr = realloc(EditStr, ...);とする必要があります。忘れやすいので,なかなか嵌ります。
コード: 全て選択
GetWindowText(hEdit,EditStr,SendMessage(hEdit,WM_GETTEXTLENGTH,0,0) +1)
蛇足ながら,どうせまたここでも文字数を得る必要がありますから,私ならWM_GETTEXTLENGTHが返した値を変数に保存しておきます。
Posted: 2006年5月23日(火) 00:25
by ゲスト
いろいろ間違っていましたね。ご指摘ありがとうございます。
この処理を関数化したいのですが、上手くいきません。
BytePtr型なので、Functionの戻り値を「プロシージャ名=戻り値」
では駄目ですよね。
lstrcpy ( プロシージャ名 , 戻り値 ) にした時、malloc=「プロシージャ名」
でメモリを確保した後は、free関数で開放する必要があると思うのですが、
そうすると戻り値がなくなってしまいます。
戻り値を設定せず、BytePtr型のグローバル変数に lstrcpy で
文字列をコピーすることで代用できるとも思いましたが、
文字化けしたものしか取得できませんでした。
どうすればいいのでしょうか。
Posted: 2006年5月23日(火) 00:41
by イグトランス
そんなときは、たとえば呼び出し側はfreeせずにBytePtrの値を返し、
戻り値を受け取った側がfreeするという方法が考えられます。
ほかにはString型を返すようにするという方法です。
StringはABがメモリの面倒を見てくれます。
#ちなみにGetDlgItemTextStr類もよろしければどうぞ。
Posted: 2006年5月23日(火) 01:01
by ゲスト
freeを呼び出すのは、戻り値を設定したプロシージャ内でないと、
無効な識別子です とエラーが出ました。
できるだけBytePtr型で取得したいので、どうしても駄目な時に、
GetDlgItemTextStrを使わせていただこうと思います。
Posted: 2006年5月23日(火) 09:25
by 7
自分はこんな感じにしてますけど。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
Function GetWindowStr(ByVal hWnd As HWND) As LPSTR
Dim length As Long
length=GetWindowTextLength(hWnd)+1
GetWindowStr=malloc(length)
GetWindowStr[GetWindowText(hWnd,GetWindowStr,length)]=0
End Function
' 使い方
Dim lpStringBuf As LPSTR
lpStringBuf=GetWindowStr(hMainWnd)
MessageBox(hMainWnd,lpStringBuf,"タイトル",MB_OK)
free(lpStringBuf)
Posted: 2006年5月23日(火) 17:24
by ゲスト
7さんのでも文字化けしてしまいます。
そこで GetDlgItemTextStr 関数を使わせて頂こうと思うのですが、
このstr変数にではなく、関数の戻り値に取得したテキストを設定したいのですが、
よくわかりません。どうすればいいのでしょうか。
Posted: 2006年5月23日(火) 18:19
by 淡幻星
えっと。
ゲストさんがやりたいことがイマイチよく分からないのですが(汗)、
以下のようなことでよいのでしょうか?
・コントロール(今回はEditBox)の文字列を得る。
・その際に、Byte型の文字列を戻り値とする関数を作りたい。
だとすると、こんな感じではいかがでしょうか?
コードはこちら。 [ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
'指定したIDのコントロールのテキストを取得し、BytePtr型で返す。
'必要なくなったら、そのBytePtr変数はfree()しなければならない。
'取得に失敗した場合はNULLが返る。その場合は当然free()する必要は無い。
Function CallocAndGetDigItemTestBytePtr( nIDDlgItem As Long ) As BytePtr
Dim hAny As HANDLE
Dim nLength As Long
hAny = GetDlgItem( hMainWnd, nIDDlgItem )
nLength = GetWindowTextLength( hAny ) + 1
CallocAndGetDigItemTestBytePtr = Calloc( nLength )
If( 0=GetWindowText( hAny, CallocAndGetDigItemTestBytePtr, nLength ) )Then
free( CallocAndGetDigItemTestBytePtr )
CallocAndGetDigItemTestBytePtr = NULL
EndIf
EndFunction
'ここから実際にEditBoxの文字列取得。
Dim pBuf As BytePtr
'戻り値がBytePtr型。NULLで無ければ、文字列が格納されている。
pBuf = CallocAndGetDigItemTestBytePtr( EditBox1 )
MsgBox hMainWnd, MakeStr( pBuf ), "確認"
free( pBuf )
まぁ、これはイグトランスさんがおっしゃっている方法ですが^^;
というか、これがオーケーなら7さんのコードもオーケーのはずで・・・、うーむ。
ところで、EditBoxに入力している文字はShift-Jisですか?
EUC等だと、取得に成功していても表示の際にはたぶん文字化けします。
なお、GetDlgItemTextStrの戻り値にテキスト、に関しては、
コード: 全て選択
Function GetDlgItemTextStrEx(hDlg As HWND, idDlgItem As Long) As String
GetDlgItemTextStr( hDlg, idDlgItem, GetDlgItemTextStrEx )
EndFunction
ではだめでしょうか?
Posted: 2006年5月23日(火) 21:17
by ゲスト
ありがとうございます、何とかもう少しで出来そうです。全体的には、
1.EditBox1の内容を取得
2.それに任意の文字列を連結
3.2.の文字列をEditBox1にセット
ということをしたいのですが、セットする文字列(2回目の MsgBox で表示している文字列)は、
文字化けしてしまいますし、freeを行うと、そこでエラーが出ます。
何が駄目なんでしょうか。宜しくお願いします。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
'-----------------------------------------------------------------------------
' イベント プロシージャ
'-----------------------------------------------------------------------------
' このファイルには、ウィンドウ [MainWnd] に関するイベントをコーディングします。
' ウィンドウ ハンドル: hMainWnd
' TODO: この位置にグローバルな変数、構造体、定数、関数を定義します。
Dim BHandle As HWND
'-----------------------------------------------------------------------------
' ウィンドウメッセージを処理するためのコールバック関数
Function MainWndProc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As DWord
' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。
' イベントプロシージャの呼び出しを行います。
MainWndProc=EventCall_MainWnd(hWnd,dwMsg,wParam,lParam)
End Function
'-----------------------------------------------------------------------------
' ここから下は、イベントプロシージャを記述するための領域になります。
Sub MainWnd_Destroy()
Test_DestroyObjects()
PostQuitMessage(0)
End Sub
Sub MainWnd_Create(ByRef CreateStruct As CREATESTRUCT)
BHandle = GetDlgItem(hMainWnd,EditBox1)
End Sub
Sub MainWnd_CommandButton1_Click()
GetText("2")
End Sub
Sub GetText(Addition As BytePtr)
Dim GStr As BytePtr
GStr=GetStr(BHandle)
MsgBox hMainWnd, MakeStr( GStr )
GStr=realloc(GStr,Len(MakeStr(Addition))+1)
Invalid(lstrcat (GStr,Addition))
MsgBox 0,MakeStr(GStr)
free(GStr)
free(Addition)
End Sub
Function GetStr(Handle As HWND) As BytePtr
Dim StrByte As Byte
StrByte = SendMessage (
Handle ,
WM_GETTEXTLENGTH ,
NULL ,
NULL
) As Byte + 1
GetStr = malloc (StrByte)
Select Case GetWindowText( Handle , GetStr, StrByte )
Case Not NULL
free (GetStr)
GetStr = NULL
End Select
End Function
Sub Invalid ( GStr As BytePtr )
SetDlgItemText (
hMainWnd ,
EditBox1 ,
GStr ,
)
End Sub
Posted: 2006年5月23日(火) 22:50
by 淡幻星
>セットする文字列(2回目の MsgBox で表示している文字列)は、
> 文字化けしてしまいますし、freeを行うと、そこでエラーが出ます。
realloc()するバッファの大きさが足りません。
それから、この場合のAdditionは静的確保されたメモリなので、
free()することは出来ません。
GetText()を以下のようにしてみてください。
コード: 全て選択
Sub GetText(Addition As BytePtr)
Dim GStr As BytePtr
Dim pTemp As BytePtr
GStr=GetStr(BHandle)
MsgBox hMainWnd, MakeStr( GStr )
'以下、変更点。---
pTemp = GStr
GStr = calloc( lstrlen(GStr)+lstrlen(Addition)+1 ) '※
lstrcpy( GStr, pTemp ) '※
free( pTemp ) '※
Invalid( lstrcat(GStr,Addition) )
MsgBox 0,MakeStr(GStr)
free(GStr)
End Sub
個人的にrealloc()は好きじゃないので、callocを使いました。
「※」の三行は次の一行で代えられるかもしれません。
コード: 全て選択
GStr = realloc( GStr, lstrlen(GStr)+lstrlen(Addition)+1 )
私が使い慣れてないせいか、どうもrealloc()の挙動が信用できないんですよね~(苦笑)。
Posted: 2006年5月23日(火) 23:24
by イグトランス
話はそれますが,私もreallocを使う必要は無いと思います。
reallocは例外安全の観点から使わないほうがよいという話を聞きかじったことがあります。
なんでも,reallocがもし失敗したらNULLを返しますが,
C/C++ではそのとき元のメモリの扱いがどうなるかは定められていないからだそうです。
つまり下手したら元のメモリにも読み書きできなくなる可能性が生じるというわけです。
http://msdn.microsoft.com/library/ja/jp ... frame=true
ABの現在のreallocの実装はWinAPIのHeapReAllocを呼んでいるだけですが,
HeapReAllocはきちんと挙動が定められていますね。勉強になりました。
コード: 全て選択
HeapReAlloc 関数が失敗すると、元のメモリは解放されず,元のハンドルとポインタも有効なまま残ります。
ただ,問題は将来に渡ってABのreallocがこのHeapReAllocを使い続けるかということです。
この失敗時の挙動を信じるのならば,ABのreallocではなくHeap系関数を直接呼ぶべきでしょうね。
Posted: 2006年5月24日(水) 00:11
by ゲスト
以下もfreeしてはいけないみたいでした。
コード: 全て選択
Sub Invalid ( GStr As BytePtr )
SetDlgItemText (
hMainWnd ,
EditBox1 ,
GStr ,
)
free(GStr)
End Sub
これで上手く出来ました。ありがとうございます。
reallocは問題があるんですね。知りませんでした。
ところで、mallocは大丈夫なんでしょうか。
Posted: 2006年5月24日(水) 10:41
by 淡幻星
ゲストさん さんが書きました:> 以下もfreeしてはいけないみたいでした。
>
コード: 全て選択
Sub Invalid ( GStr As BytePtr )
> SetDlgItemText (
> hMainWnd ,
> EditBox1 ,
> GStr ,
> )
> free(GStr)
> End Sub
ここでGStrに渡されているポインタは、
動的確保されたメモリなので、free()自体は成功します。
しかし、そこでメモリを解放するということは、
その後(Invalidを抜けた後)はGStrで示されるバッファは利用不可になります。
しかし、ゲストさんのコードではその後にMsgBoxでGStrを
利用していますからエラーとなるわけです。
free()を使うタイミングは、そのポインタが示すバッファの利用が全て終わってからになります。
ゲストさん さんが書きました:> ところで、mallocは大丈夫なんでしょうか。
大丈夫だと思います。
もっとも、私はmalloc()+Zeromemory()の動作に相当するcalloc()を好んで使っています。
文字列を扱う際にはゼロクリアしてある方が安心できるので。
なお、malloc()せよ、calloc()にせよ、安全性を求めるならば、
返り値がNULLでないことを常に確認するコードを書く必要があります。
(私はよくサボリますが^^;)
≫イグトランスさん
イグトランスさん さんが書きました:C/C++ではそのとき元のメモリの扱いがどうなるかは定められていないからだそうです。
そうだったんですか。知りませんでした。
C++では基本的にnewを使っているので^^;
ABではHeap系のAPIを直に呼ぶことも多いですね~。
失礼。私も話が脱線しました。≫ゲストさん
Posted: 2006年5月24日(水) 19:03
by ゲスト
> 失礼。私も話が脱線しました。≫ゲストさん
いえ、勉強なります。
コード: 全て選択
Select Case GetWindowText( Handle , GetStr, StrByte )
Case Not NULL
free (GetStr)
GetStr = NULL
End Select
ところで、上記コードの GetStr = NULL の意味がいまいち分かりません。
free ( ) したあとは、変数の内容はヌル値になるのではないのでしょうか。
Posted: 2006年5月24日(水) 19:22
by イグトランス
free(GetStr)を行ったら、GetStrの指す先を読み書きしてはいけなくなります。
が、GetStrは依然としてそこを指しているということに変わりはありません。
誤って読み書きしようとすると偶然できてしまうこともあります。
そういうことを未然に防ぐためにNULLを代入することがあります。
そうすれば誤ってアクセスしようとするとアクセス違反になってデバッガが検出してくれるというわけです。
しかしこの例ではどうせすぐに関数を抜けて変数も破棄されるので殆ど有難味はありません。
私は要らないと思います。