2024/02/03(土) [n年前の日記]
#1 [basic] FreeBASICで多重起動禁止
Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit で、多重起動禁止ができるのかどうか動作確認。
以下が参考になった。
_Mutex instances - freebasic.net
_Mutex Troubles [Solved] - freebasic.net
_Windows上で多重起動を防止する方法
Windowsに特化した方法になってしまうけど、CreateMutex() を使えば多重起動禁止ができるはず。
サンプルソースを書いてみた。
_mutex.bas
fbc mutex.bas で、mutex.exe を生成できる。
実行すると、最初の起動時は以下になる。
1つ目のプログラムがまだ実行されている間に、別のDOS窓で同じプログラムを実行すると、以下の表示になって終了する。
多重起動禁止になっているようだなと…。
以下が参考になった。
_Mutex instances - freebasic.net
_Mutex Troubles [Solved] - freebasic.net
_Windows上で多重起動を防止する方法
Windowsに特化した方法になってしまうけど、CreateMutex() を使えば多重起動禁止ができるはず。
サンプルソースを書いてみた。
_mutex.bas
#include once "windows.bi"
Const mutex_name = "MyProgramName"
Dim As HANDLE hMsp
hMsp = CreateMutex(NULL, TRUE, strptr(mutex_name))
If hMsp <> 0 Then
Dim As DWORD ret = GetLastError()
If ret = ERROR_ALREADY_EXISTS Or ret = ERROR_ACCESS_DENIED Then
' Already
Print "Already running. Exit."
ReleaseMutex(hMsp)
CloseHandle(hMsp)
end
End If
End If
Print "Running ... "
Print "Push Any Key"
sleep
ReleaseMutex(hMsp)
CloseHandle(hMsp)
fbc mutex.bas で、mutex.exe を生成できる。
実行すると、最初の起動時は以下になる。
> mutex.exe Running ... Push Any Key
1つ目のプログラムがまだ実行されている間に、別のDOS窓で同じプログラムを実行すると、以下の表示になって終了する。
> mutex.exe Already running. Exit.
多重起動禁止になっているようだなと…。
[ ツッコむ ]
#2 [basic] FreeBASICでexeに画像バイナリを含めたい
FreeBASICで生成した実行形式ファイル(exeファイル)の中に、画像のバイナリデータを含めてしまって、それをウインドウに描画したい。
環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
FreeBASIC の公式掲示板で検索しまくってみたところ、以下のような方法があるらしい。
環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
FreeBASIC の公式掲示板で検索しまくってみたところ、以下のような方法があるらしい。
- Windows限定になるけれど、リソースファイルに画像データを含めてしまう。BASICから FindResource() や LoadResource() 等でアクセスする。
- bin2bas と言うツールを使って、バイナリをBASICの配列の形にする。ただ、この方法はファイルサイズ制限があるらしい。
- FreeBASICはアセンブラの記述も盛り込めるので、asm - end asm や .incbin を使ってバイナリを内包してしまう。
- バイナリをオブジェクトファイルに変換して、リンカで結合して内包させる。
◎ リソースファイルを利用する方法 :
Windows限定になってしまうけれど、リソースファイルを利用する方法を試してみた。
バイナリ(リソース)にアクセスするところまでは FreeBASIC の標準機能だけで処理できるけれど、メモリ上の画像バイナリをFreeBASICの描画に利用できるように変換するあたりは、FreeBASIC標準機能だけでは難しいらしい。今回は FBImage という画像処理ライブラリの LoadRGBAMemory() を使わせてもらって、メモリ上の画像バイナリを変換することにした。
_mieki256's diary - FreeBASICで画像描画
_FBImage static Win/Lin 32/64-bit - freebasic.net
サンプルソースを書いてみた。一式は以下。
_take1_png_in_rc.zip
一応ソース群も貼っておく。
_loadpnginmemory.bas
リソースファイルは以下。"pngimage" というリソース名、データ種類は RCDATA、png画像ファイル名は "image_png.png" と記述してある。
_resource.rc
使用画像は以下。RGBA 32bit のpng画像。50,344 byte。
_image_png.png
コンパイルは以下。 _mk.bat も一応書いておいた。
生成された loadpnginmemory.exe を実行すると、以下のようにウインドウが開いて、png画像が描画される。
少し解説。
リソースファイルでpng画像を含めるように指定する際は、RCDATA という種類を指定すると良さそう。他の種類として BMP や ICON もあるけれど、ググった感じでは、どれにも当てはまらない時は RCDATA を指定することが多いように見えた。
リソースにアクセスする際は以下の関数を使う。
バイナリ(リソース)にアクセスするところまでは FreeBASIC の標準機能だけで処理できるけれど、メモリ上の画像バイナリをFreeBASICの描画に利用できるように変換するあたりは、FreeBASIC標準機能だけでは難しいらしい。今回は FBImage という画像処理ライブラリの LoadRGBAMemory() を使わせてもらって、メモリ上の画像バイナリを変換することにした。
_mieki256's diary - FreeBASICで画像描画
_FBImage static Win/Lin 32/64-bit - freebasic.net
サンプルソースを書いてみた。一式は以下。
_take1_png_in_rc.zip
一応ソース群も貼っておく。
_loadpnginmemory.bas
' リソースにアクセスするために windows.bi を使う
#include "windows.bi"
' 画像読み込み用ライブラリ FBImage を使う
#include once "FBImage.bi"
' カレントディレクトリを exeファイルのある場所にする
chdir exepath()
' リソースにアクセスするための名前
Const res_name = "pngimage"
Dim As HRSRC hResInfo
Dim As HGLOBAL hResData
Dim As ubyte Ptr resPtr
Dim As DWORD resSize
' リソースを探す
hResInfo = FindResource(NULL, strptr(res_name), RT_RCDATA)
If hResInfo = Null Then
Print "Error : Not found resource" : end
End If
' リソースを読み込み
hResData = LoadResource(0, hResInfo)
If hResData = Null Then
Print "Error : Not load resource"
FreeResource(hResData) : end
End If
' リソースのポインタを取得
resPtr = CPtr(ubyte Ptr, LockResource(hResData))
If resPtr = Null Then
Print "Error : Not get resource pointer"
FreeResource(hResData) : end
End If
' リソースのサイズを取得
resSize = SizeofResource(NULL, hResInfo)
If resSize = 0 Then
Print "Error : Can not get resource size"
FreeResource(hResData) : end
End If
' デスクトップ解像度を指定
Dim As Integer scrw = 512
Dim As Integer scrh = 288
' ウインドウサイズと色深度(bpp)を指定
screenres scrw, scrh, 32
' メモリ上にある画像データを読み込む。ポインタとバイト数を渡す
var img = LoadRGBAMemory(resPtr, resSize)
' リソースを解放
FreeResource(hResData)
' 画像を描画。RGB=(255, 0, 255) のピクセルは透明色として扱う
Put (16, 16), img, TRANS
' キー入力があるまで待ち続ける
sleep
' 画像を使い終わったので破棄
ImageDestroy img
リソースファイルは以下。"pngimage" というリソース名、データ種類は RCDATA、png画像ファイル名は "image_png.png" と記述してある。
_resource.rc
pngimage RCDATA "image_png.png"
使用画像は以下。RGBA 32bit のpng画像。50,344 byte。
_image_png.png
コンパイルは以下。 _mk.bat も一応書いておいた。
fbc loadpnginmemory.bas resource.rc
生成された loadpnginmemory.exe を実行すると、以下のようにウインドウが開いて、png画像が描画される。
少し解説。
リソースファイルでpng画像を含めるように指定する際は、RCDATA という種類を指定すると良さそう。他の種類として BMP や ICON もあるけれど、ググった感じでは、どれにも当てはまらない時は RCDATA を指定することが多いように見えた。
リソースにアクセスする際は以下の関数を使う。
- FindResource() ... リソースを検索する。
- LoadResource() ... リソースを読み込む。
- LockResource() ... リソースのアドレスを取得する。
- SizeofResource() ... リソースのサイズを取得する。
- FreeResource() ... 読み込んだリソースを解放する。
◎ bin2basを利用する方法 :
bin2bas というツールがあるらしい。バイナリファイルをBASICの ubyte (Unsigned byte)配列にして出力してくれるツールだそうで…。BASICソースの中に、配列としてバイナリを含めてしまえば、たしかに目的は果たせそう。
FreeBASICの掲示板で bin2bas のソースが貼ってあった。
_Bin2Bas - freebasic.net
例えば、test.bin という、48 byte しかないテストバイナリファイルを変換してみると、以下のような内容になる。
test.bi
test.bas
しかし、この方法は問題がある…。バイナリファイルのサイズが小さい場合はこれでもイケるけれど、大きいファイルサイズになると FreeBASIC がコンパイルできずにクラッシュしてしまう。fbc.exe が .asm を途中まで生成したところで終了してしまうので困ってしまった。Windowsのイベントビューアを確認したら、fbc.exe がクラッシュしまくっていた。
以下の報告によると、20,000 byte 前後に限界値があるらしい。ただ、DATA文を利用した形に書き直せば、もう少し大きいサイズを含めることができる模様。
_fbc.exe has stopped working - freebasic.net
_fbc can't compile large array ! - freebasic.net
そんなわけで、Python を使って、DATA文の形にして標準出力に出力するスクリプトを書いてみた。先に出力結果を貼っておく。
DATA を読み込んで、配列の中に格納していく形になっている。これを .bas の最初のほうで include しておけば、その行以降で、この配列を参照できるはず。
変換ツール bin2bas.py は以下。CC0 / Public Domain ってことで。
_bin2bas.py
使い方は以下。
この bin2bas.py を利用して、ubyte配列を得て、exe の中に含めてみる。
ファイル一式は以下。
_take2_bin2bas.zip
ソース群も一応貼っておく。
元画像、image_png.png は以下。
_image_png.png
bin2bas.py を使って、image_png.png を image_png.bas に変換。
_image_png.bas
basicのソースは以下。
_loadpngbin2bas.bas
リソースファイルを利用する版に比べて、かなり短くなった。
以下を打って、.basをコンパイル。
実行すると以下のようにウインドウが表示される。pngが表示できている。
しかし、この方法はファイルサイズ制限があるあたりが…。必ず動作するソースになってくれるのかどうか、そのあたりは微妙だなと…。
FreeBASICの掲示板で bin2bas のソースが貼ってあった。
_Bin2Bas - freebasic.net
- 上記のページで紹介されているソースをコピーして、bin2bas.bas というファイル名でソースを保存する。
- fbc bin2bas.bas と打てば bin2bas.exe が生成できる。
- 使い方は、bin2bas.exe INPUT_FILE。bin2bas.exe hoge.png と打てば、hoge.bi と hoge.bas が生成される。
例えば、test.bin という、48 byte しかないテストバイナリファイルを変換してみると、以下のような内容になる。
bin2bas.exe test.bin
test.bi
extern test_data(0 to 48-1) as ubyte
test.bas
#include once "test.bi"
dim shared test_data(0 to 48-1) as ubyte = { _
&h0,&h1,&h2,&h3,&h4,&h5,&h6,&h7,&h8,&h9,&hA,&hB,&hC,&hD,&hE,&hF,&h10,&h11,&h12,&h13,&h14,&h15,&h16,&h17,&h18,&h19,&h1A,&h1B,&h1C,&h1D,&h1E, _
&h1F,&h20,&h21,&h22,&h23,&h24,&h25,&h26,&h27,&h28,&h29,&h2A,&h2B,&h2C,&h2D,&h2E,&h2F }
しかし、この方法は問題がある…。バイナリファイルのサイズが小さい場合はこれでもイケるけれど、大きいファイルサイズになると FreeBASIC がコンパイルできずにクラッシュしてしまう。fbc.exe が .asm を途中まで生成したところで終了してしまうので困ってしまった。Windowsのイベントビューアを確認したら、fbc.exe がクラッシュしまくっていた。
以下の報告によると、20,000 byte 前後に限界値があるらしい。ただ、DATA文を利用した形に書き直せば、もう少し大きいサイズを含めることができる模様。
_fbc.exe has stopped working - freebasic.net
_fbc can't compile large array ! - freebasic.net
そんなわけで、Python を使って、DATA文の形にして標準出力に出力するスクリプトを書いてみた。先に出力結果を貼っておく。
python bin2bas.py -i test.bin
' input file = test.bin
Dim As Integer test_bin_data_length = 48
Dim shared test_bin_data(0 To (test_bin_data_length - 1)) As ubyte
For i As Integer = 0 To (test_bin_data_length - 1)
read test_bin_data(i)
Next i
DATA _
&H00,&H01,&H02,&H03,&H04,&H05,&H06,&H07,&H08,&H09,&H0a,&H0b,&H0c,&H0d,&H0e,&H0f, _
&H10,&H11,&H12,&H13,&H14,&H15,&H16,&H17,&H18,&H19,&H1a,&H1b,&H1c,&H1d,&H1e,&H1f, _
&H20,&H21,&H22,&H23,&H24,&H25,&H26,&H27,&H28,&H29,&H2a,&H2b,&H2c,&H2d,&H2e,&H2f
DATA を読み込んで、配列の中に格納していく形になっている。これを .bas の最初のほうで include しておけば、その行以降で、この配列を参照できるはず。
変換ツール bin2bas.py は以下。CC0 / Public Domain ってことで。
_bin2bas.py
import struct
import argparse
def main():
desc = "Cpnvert binary file to basic source"
parser = argparse.ArgumentParser(description=desc)
parser.add_argument("-i", "--input", help="Input binary file", required=True)
args = parser.parse_args()
data = []
with open(args.input, "rb") as f:
while True:
b = f.read(1)
if b:
data.append(struct.unpack("B", b)[0])
else:
break
dataname = args.input.replace(".", "_")
dataname += "_data"
lengthname = dataname + "_length"
print("' input file = %s" % (args.input))
print("")
print("Dim As Integer %s = %d" % (lengthname, len(data)))
print("Dim shared %s(0 To (%s - 1)) As ubyte" % (dataname, lengthname))
print("")
print("For i As Integer = 0 To (%s - 1)" % (lengthname))
print(" read %s(i)" % (dataname))
print("Next i")
print("")
print("DATA _")
s = ""
cnt = 0
for i in range(len(data)):
s += "&H%02x," % data[i]
cnt += 1
if cnt % 16 == 0:
if cnt == len(data):
print(s[:-1])
else:
print("%s _" % s)
s = ""
if s != "":
print(s[:-1])
if __name__ == "__main__":
main()
使い方は以下。
> python bin2bas.py --help
usage: bin2bas.py [-h] -i INPUT
Convert binary file to basic source
options:
-h, --help show this help message and exit
-i INPUT, --input INPUT
Input binary file
この bin2bas.py を利用して、ubyte配列を得て、exe の中に含めてみる。
ファイル一式は以下。
_take2_bin2bas.zip
ソース群も一応貼っておく。
元画像、image_png.png は以下。
_image_png.png
bin2bas.py を使って、image_png.png を image_png.bas に変換。
python bin2bas.py -i image_png.png > image_png.bas
_image_png.bas
' input file = image_png.png
Dim As Integer image_png_png_data_length = 50344
Dim shared image_png_png_data(0 To (image_png_png_data_length - 1)) As ubyte
For i As Integer = 0 To (image_png_png_data_length - 1)
read image_png_png_data(i)
Next i
DATA _
&H89,&H50,&H4e,&H47,&H0d,&H0a,&H1a,&H0a,&H00,&H00,&H00,&H0d,&H49,&H48,&H44,&H52, _
&H00,&H00,&H01,&H00,&H00,&H00,&H01,&H00,&H08,&H06,&H00,&H00,&H00,&H5c,&H72,&Ha8, _
' ...
&Hba,&H45,&Hf0,&H3f,&Hac,&H83,&H26,&H35,&H43,&H1b,&H66,&Hed,&H00,&H00,&H00,&H00, _
&H49,&H45,&H4e,&H44,&Hae,&H42,&H60,&H82
basicのソースは以下。
_loadpngbin2bas.bas
' 画像読み込み用ライブラリ FBImage を使う #include once "FBImage.bi" ' ubytes配列の画像データバイナリ #include "image_png.bas" ' カレントディレクトリを exeファイルのある場所にする chdir exepath() ' デスクトップ解像度を指定 Dim As Integer scrw = 512 Dim As Integer scrh = 288 ' ウインドウサイズと色深度(bpp)を指定 screenres scrw, scrh, 32 ' メモリ上にある画像データを FBImage で読み込む。ポインタとバイト数を渡す Dim As Long size = image_png_png_data_length var img = LoadRGBAMemory(@(image_png_png_data(0)), size) ' 画像を描画。RGB=(255, 0, 255) のピクセルは透明色として扱う Put (16, 16), img, TRANS ' キー入力があるまで待ち続ける sleep ' 画像を使い終わったので破棄 ImageDestroy img
リソースファイルを利用する版に比べて、かなり短くなった。
以下を打って、.basをコンパイル。
fbc loadpngbin2bas.bas
実行すると以下のようにウインドウが表示される。pngが表示できている。
しかし、この方法はファイルサイズ制限があるあたりが…。必ず動作するソースになってくれるのかどうか、そのあたりは微妙だなと…。
◎ asmを利用する方法は失敗 :
asm - asm end を記述して、中で .incbin "hoge.png" をする方法はどうだろうと試してみたのだけど、これは失敗に終わった。
_loadpngasm.bas
fbc loadpngasm.bas でコンパイルして、生成された loadpngasm.exe を実行してみたら、クラッシュしかしない…。
ファイルサイズが大き過ぎるとダメなのだろうかと疑って、小さいファイルと差し替えて試してみたけれど、結果は同じで、正常動作しないプログラムになった。
以下を参考にして記述してみたのだけど…。どういう書き方をすればいいのやら。
_Displaying images from files and memory - freebasic.net
_loadpngasm.bas
' 画像読み込み用ライブラリ FBImage を使う #include once "FBImage.bi" #define IMAGEDATA_SIZE 50344 ' #define IMAGEDATA_SIZE 297 ' 画像バイナリをアセンブラの記述で内包する Asm .data .global imgdata imgdata: .incbin "image_png.png" End Asm ' カレントディレクトリを exeファイルのある場所にする chdir exepath() ' デスクトップ解像度を指定 Dim As Integer scrw = 512 Dim As Integer scrh = 288 ' ウインドウサイズと色深度(bpp)を指定 screenres scrw, scrh, 32 ' 画像バイナリのアドレスを取得している? Dim As Any Ptr image Asm push OFFSET imgdata pop [image] End Asm ' メモリ上にある画像データを FBImage で読み込む。ポインタとバイト数を渡す var img = LoadRGBAMemory(image, IMAGEDATA_SIZE) ' 画像を描画。RGB=(255, 0, 255) のピクセルは透明色として扱う Put (16, 16), img, TRANS ' キー入力があるまで待ち続ける sleep ' 画像を使い終わったので破棄 ImageDestroy img
fbc loadpngasm.bas でコンパイルして、生成された loadpngasm.exe を実行してみたら、クラッシュしかしない…。
ファイルサイズが大き過ぎるとダメなのだろうかと疑って、小さいファイルと差し替えて試してみたけれど、結果は同じで、正常動作しないプログラムになった。
以下を参考にして記述してみたのだけど…。どういう書き方をすればいいのやら。
_Displaying images from files and memory - freebasic.net
[ ツッコむ ]
以上、1 日分です。

