UWSCのPOPUPMENUにBitmapを表示する

UWSCのPOPUPMENUでBitmapやアイコンを表示できるようにしました!
これに伴い、先日のクリップボード管理を更新しました、、、が、
単体では動かなくなる(このPopupMenuExモジュールに依存する)ので、こちらに書いてます。



拡張PopupMenu

使い方は、、、

  • POPUPMENUにBitmap表示用配列を追加
    • PopupMenuEx.Show(pp, ppex[, n, x, y])といった感じ。
  • ついでに、消すまでの秒数を指定可能にしてみた。(省略または0は、消さない)
    • が、XPでは少々上手くいっておりません。
  • ppexの指定は、ppと同じように「{」「}」で階層化できますが、、、「{-」「-}」には対応できてません。
    • 原因不明。何やらメニューのPositionが不可解な感じ、、、。
  • XPだと幅の調整が上手くいかなかったり、選択するとイメージが反転表示されたりします。(Vista以降は大丈夫なはず)


ppexの各項目は

  • .exeを指定するとそのアイコンを取得して表示(オプションは、「/1」を付与で大きいアイコン)
  • .icoを指定するとそのアイコンを表示(オプションは、「/w/h」で幅と高さを指定)
  • .bmpを指定すると画像を表示(オプションは、「/w/h」で幅と高さを指定)

あと一つあるけど、それはHBITMAPを渡す必要があるので、ここでは割愛。


例では、面倒なので同じものを指定していますが、その必要はもちろんありません。
例を実行してみる場合は、適当なビットマップファイルを「print.bmp」、適当なアイコンファイルを「warn.ico」として同じフォルダーに置いとくと効果がわかります。


PopupMenuEx.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "PopupMenuEx.uws" THEN
	DIM pp = SPLIT("exe {notepad.exe/1 uwsc.exe calc.exe} print.bmp/16/16 aaa {warn.ico {warn.ico/64/64} print.bmp} warn.ico/16/16")
	DIM res = PopupMenuEx.Show(pp, pp, 3)
	IF res <> -1 THEN MSGBOX(pp[res])
ENDIF


MODULE PopupMenuEx
	HASHTBL _hBitmaps

	DEF_DLL SendMessageW(HWND, DWORD, DWORD, DWORD): DWORD: user32
	DEF_DLL SetMenuItemInfoW(DWORD, DWORD, BOOL, var DWORD[]): BOOL: user32
	DEF_DLL GetSubMenu(DWORD, int): DWORD: user32
	DEF_DLL DeleteObject(DWORD): BOOL: gdi32
	DEF_DLL LoadImageW(DWORD, wstring, DWORD, int, int, DWORD): DWORD: user32
	DEF_DLL RtlMoveMemory(DWORD[], DWORD, DWORD): kernel32

	DEF_DLL ExtractIconExW(wstring, int, var DWORD, var DWORD, DWORD): DWORD: shell32
	DEF_DLL DestroyIcon(DWORD): BOOL: user32
	DEF_DLL GetIconInfo(DWORD, var DWORD[]): BOOL: user32
	DEF_DLL GetSysColor(int): DWORD: user32
	DEF_DLL DrawIconEx(DWORD, int, int, DWORD, int, int, DWORD, DWORD, DWORD): BOOL: user32
	DEF_DLL CreateSolidBrush(DWORD): DWORD: gdi32

	DEF_DLL GetDC(HWND): DWORD: user32
	DEF_DLL ReleaseDC(HWND, DWORD): int: user32
	DEF_DLL CreateCompatibleDC(DWORD): DWORD: gdi32
	DEF_DLL DeleteDC(DWORD): BOOL: gdi32
	DEF_DLL SelectObject(DWORD, DWORD): DWORD: gdi32
	DEF_DLL GetDIBits(DWORD, DWORD, DWORD, DWORD, var DWORD[], var DWORD[], DWORD): int: gdi32
	DEF_DLL StretchBlt(DWORD, int, int, int, int, DWORD, int, int, int, int, DWORD): BOOL: gdi32
	DEF_DLL CreateCompatibleBitmap(DWORD, int, int): DWORD: gdi32


	FUNCTION Show(pp, pe, n=0, x=ERR_VALUE, y=ERR_VALUE)
		RESULT = -1
		THREAD EditPopup(pe, n)

		IF x = ERR_VALUE THEN x = G_MOUSE_X
		IF y = ERR_VALUE THEN y = G_MOUSE_Y
		RESULT = POPUPMENU(pp, x, y)

		DIM key
		WHILE LENGTH(_hBitmaps)
			key = _hBitmaps[0, HASH_KEY]
			IF _hBitmaps[key] THEN DeleteObject(_hBitmaps[key])
			key = _hBitmaps[key, HASH_REMOVE]
		WEND
	FEND

	PROCEDURE EditPopup(pp, n)
		DIM start = GETTIME() + G_TIME_ZZ / 1000
		DIM wid = GETID("", "#32768", -1)
		DIM hnd = IDTOHND(wid)
		DIM hmenu = SendMessageW(hnd, $1E1, 0, 0)
		HASHTBL hms, mis
		IF VARTYPE(pp) < VAR_ARRAY THEN pp = SPLIT(pp, "<#CR>")

		DIM mi[11], i, j = 0, k
		mi[0] = 48
		mi[1] = $80
		FOR i = 0 TO LENGTH(pp) - 1
			k = FALSE
			IFB COPY(pp[i], 1, 1) = "{" THEN
				hms[LENGTH(hms)] = hmenu
				mis[LENGTH(mis)] = j
				hmenu = GetSubMenu(hmenu, j - 1)
				j = 0
				IFB pp[i] = "{-" THEN
					j = j + 1	// TODO: positionが増えている?うまくいかない
					CONTINUE
				ENDIF
				pp[i] = COPY(pp[i], 2)
			ENDIF
			IFB COPY(pp[i], LENGTH(pp[i])) = "}" THEN
				IFB pp[i] = "-}" THEN
					hmenu = hms[LENGTH(hms) - 1]
					j = mis[LENGTH(mis) - 1]
					k = hms[LENGTH(hms) - 1, HASH_REMOVE]
					k = mis[LENGTH(mis) - 1, HASH_REMOVE]
					CONTINUE
				ENDIF
				pp[i] = COPY(pp[i], 1, LENGTH(pp[i]) - 1)
				k = TRUE
			ENDIF
			_hBitmaps[i] = GetBitmap(hnd, pp[i])
			mi[11] = _hBitmaps[i]
			IF mi[11] THEN SetMenuItemInfoW(hmenu, j, TRUE, mi)
			j = j + 1
			IFB k THEN
				hmenu = hms[LENGTH(hms) - 1]
				j = mis[LENGTH(mis) - 1]
				k = hms[LENGTH(hms) - 1, HASH_REMOVE]
				k = mis[LENGTH(mis) - 1, HASH_REMOVE]
			ENDIF
		NEXT

		IFB n > 0 THEN
			WHILE (GETTIME() + G_TIME_ZZ / 1000 < start + n)
				SLEEP(start + n - GETTIME() - G_TIME_ZZ / 1000 - 0.01)
			WEND
			IF IDTOHND(wid) = hnd THEN CTRLWIN(wid, HIDE)
		ENDIF
	FEND

	FUNCTION GetBitmap(hnd, data)
		RESULT = NULL
		DIM datas = SPLIT(data, "/"), l, op[2], i
		IF LENGTH(datas) = 0 THEN EXIT
		l = LENGTH(datas[0])
		FOR i = 0 TO LENGTH(op) - 1
			op[i] = 0
			IF LENGTH(datas) > i+1 THEN op[i] = VAL(datas[i+1])
		NEXT
		SELECT COPY(datas[0], l - 3)
		CASE ".exe"
			RESULT = GetBitmapExeIcon(hnd, datas[0], op[0])
		CASE ".bmp"
			RESULT = GetBitmapFile(hnd, datas[0], 0, op[0], op[1])
		CASE ".ico"
			RESULT = GetBitmapIcon(hnd, GetBitmapFile(hnd, datas[0], 1, op[0], op[1]))
		CASE "|hbm"
			RESULT = GetBitmapCopy(hnd, op[0], op[1], op[2])
		SELEND
	FEND

	FUNCTION GetBitmapFile(hnd, path, type=0, cx=0, cy=0)
		RESULT = LoadImageW(0, path, type, cx, cy, 16)
	FEND

	FUNCTION GetBitmapExeIcon(hnd, path, l=FALSE)
		RESULT = NULL
		DIM hIconLarge, hIconSmall
		IFB ExtractIconExW(path, 0, hIconLarge, hIconSmall, 1) THEN
			IF l THEN
				RESULT = GetBitmapIcon(hnd, hIconLarge)
			ELSE
				RESULT = GetBitmapIcon(hnd, hIconSmall)
			ENDIF
		ENDIF
		DestroyIcon(hIconLarge)
		DestroyIcon(hIconSmall)
	FEND

	FUNCTION GetBitmapIcon(hnd, hIcon)
		RESULT = NULL
		DIM ii[4], hdcOrg = GetDC(hnd)
		IFB GetIconInfo(hIcon, ii) THEN
			DIM hdc = CreateCompatibleDC(hdcOrg), hbmp = SelectObject(hdc, ii[4])
			RESULT = ii[4]
			DIM br = CreateSolidBrush(GetSysColor(4))
			DrawIconEx(hdc, 0, 0, hIcon, 0, 0, 0, br, 3)
			DeleteObject(br)
			SelectObject(hdc, hbmp)
			IF ii[3] > 0 THEN DeleteObject(ii[3])
			DeleteDC(hdc)
		ENDIF
		ReleaseDC(hnd, hdcOrg)
	FEND

	FUNCTION GetBitmapCopy(hnd, hbmp, w=0, h=0)
		RESULT = NULL
		DIM hdcOrg = GetDC(hnd), hdcs = CreateCompatibleDC(hdcOrg)
		DIM hbmps = SelectObject(hdcs, hbmp), bi[9]

		bi[0] = 40
		IFB GetDIBits(hdcs, hbmp, 0, 0, NULL, bi, 0) <> 0 THEN
			IFB w = 0 THEN
				w = bi[1]
				IFB h = 0 THEN
					h = bi[2]
				ELSE
					w = bi[1] * h / bi[2]
				ENDIF
			ELSEIF h = 0 THEN
				h = w * bi[2] / bi[1]
			ENDIF
			DIM hdcd = CreateCompatibleDC(hdcOrg), hbmpd = CreateCompatibleBitmap(hdcs, w, h)
			DIM hbmpdb = SelectObject(hdcd, hbmpd)
			IFB StretchBlt(hdcd, 0, 0, w, h, hdcs, 0, 0, bi[1], bi[2], $CC0020) THEN
				RESULT = hbmpd
			ELSE
				DeleteObject(hbmpd)
			ENDIF
			SelectObject(hdcd, hbmpdb)
			DeleteDC(hdcd)
		ENDIF

		SelectObject(hdcs, hbmps)
		DeleteDC(hdcs)
		ReleaseDC(hnd, hdcOrg)
	FEND

ENDMODULE

DrawIconExなんてAPI、知らなかった、、、。
GetBitmapCopy関数、CreateCompatibleBitmapの第一引数はhdcs(hdcdではない)

PopupMenuEx依存クリップボード管理

左端(マウスポインターのX座標0)にすると、今度はイメージを表示します。
また、文字列を保持している場合は、それをそのままppexに渡すため、exeをフルパスでコピーしても面白いかもしれません。
パスの通っているものなら、たとえば「notepad.exe」をコピーするだけでもいけます。
グラフィック表示があると、やっぱり嬉しいですね!


Clipboard2.uws

OPTION EXPLICIT
CALL PopupMenuEx

IFB GET_UWSC_NAME = "Clipboard2.uws" THEN
	DIM loop = TRUE, i, num, p, j, fmt, pex
	WHILE loop
		WHILE G_MOUSE_X
			SLEEP(0.1)
		WEND

		GETKEYSTATE(VK_CTRL)
		IF GETKEYSTATE(VK_CTRL) THEN Clipboard.Get()
		num = Clipboard.Count()
		p = SAFEARRAY(0, num * 4)
		pex = SAFEARRAY(0, num * 4)
		FOR i = 1 TO num
			fmt = Clipboard.GetTypicalFormat(i)
			SELECT fmt
			CASE Clipboard.F_UNICODETEXT
				p[i*4-4] = Clipboard.Peek(fmt, i)
				j = POS("<#CR>", p[i*4-4])
				WHILE j = 1
					p[i*4-4] = COPY(p[i*4-4], 3)
					j = POS("<#CR>", p[i*4-4])
				WEND
				IF j THEN p[i*4-4] = COPY(p[i*4-4], 1, j - 1)
				//pex[i*4-4] = "notepad.exe/1"
			CASE Clipboard.F_BITMAP
				p[i*4-4] = Clipboard.GetFormatName(fmt)
				pex[i*4-4] = "|hbm/" + Clipboard.Peek(fmt, i) + "/64"
			DEFAULT
				p[i*4-4] = Clipboard.GetFormatName(fmt)
				IF LENGTH(p[i*4-4]) = 0 THEN p[i*4-4] = "fmt:" + fmt
			SELEND
			IF LENGTH(p[i*4-4]) = 0 THEN p[i*4-4] = "-"
			p[i*4-3] = "{&Set"
			p[i*4-2] = "&View"
			p[i*4-1] = "&Del}"
		NEXT
		p[num * 4] = "&End"
		FOR i = 0 TO LENGTH(pex) - 1
			IF LENGTH(pex[i]) = 0 THEN pex[i] = p[i]
		NEXT
		j = PopupMenuEx.Show(p, pex)
		i = INT(j / 4) + 1
		IFB j = -1 THEN
			// nop
		ELSEIF j = num * 4 THEN
			loop = FALSE
		ELSEIF j MOD 4 = 3 THEN
			Clipboard.Del(i)
		ELSEIF j MOD 4 = 2 THEN
			fmt = Clipboard.GetTypicalFormat(i)
			p = Clipboard.Peek(fmt, i, TRUE)
			SELECT fmt
			CASE Clipboard.F_UNICODETEXT
				PRINT p
			CASE Clipboard.F_HDROP
				FOR i = 0 TO LENGTH(p) - 1
					PRINT p[i]
				NEXT
			CASE Clipboard.F_BITMAP
				j = SAFEARRAY(0, 1)
				pex = SAFEARRAY(0, 9)
				j[0] = PopupMenuEx.GetDC(Clipboard._hwnd)
				j[1] = PopupMenuEx.SelectObject(j[0], p)
				pex[0] = 40
				IFB PopupMenuEx.GetDIBits(j[0], p, 0, 0, NULL, pex, 0) <> 0 THEN
					PRINT "(" + pex[1] + ", " + pex[2] + ") " + INT(pex[3] / $10000) + "bpp"
				ENDIF
				PopupMenuEx.SelectObject(j[0], j[1])
				PopupMenuEx.ReleaseDC(Clipboard._hwnd, j[0])
			SELEND
			PRINT ""
		ELSE
			Clipboard.Set(i, FALSE)
		ENDIF
	WEND

	Clipboard.Dispose()
ENDIF


MODULE Clipboard
	DEF_DLL OpenClipboard(HWND): BOOL: user32
	DEF_DLL CloseClipboard(): BOOL: user32
	DEF_DLL EmptyClipboard(): BOOL: user32
	DEF_DLL CountClipboardFormats(): int: user32
	DEF_DLL EnumClipboardFormats(DWORD): DWORD: user32
	DEF_DLL GetClipboardFormatNameW(DWORD,var wstring,int): int: user32
	DEF_DLL GetClipboardData(DWORD): DWORD: user32
	DEF_DLL SetClipboardData(DWORD, DWORD): DWORD: user32

	DEF_DLL GlobalAlloc(DWORD, DWORD): DWORD: kernel32
	DEF_DLL GlobalLock(DWORD): DWORD: kernel32
	DEF_DLL GlobalUnlock(DWORD): BOOL: kernel32
	DEF_DLL GlobalSize(DWORD): DWORD: kernel32
	DEF_DLL GlobalFree(DWORD): DWORD: kernel32

	DEF_DLL GetProcessHeap(): DWORD: kernel32
	DEF_DLL HeapAlloc(DWORD, DWORD, DWORD): DWORD: kernel32
	DEF_DLL HeapFree(DWORD, DWORD, DWORD): BOOL: kernel32
	DEF_DLL HeapSize(DWORD, DWORD, DWORD): LONG: kernel32

	DEF_DLL DragQueryFileW(DWORD,DWORD,var wstring,DWORD): DWORD: shell32

	HASHTBL _datas
	PUBLIC _hwnd, _heap
	CONST F_TEXT = 1
	CONST F_BITMAP = 2
	CONST F_METAFILEPICT = 3
	CONST F_SYLK = 4
	CONST F_DIF = 5
	CONST F_TIFF = 6
	CONST F_OEMTEXT = 7
	CONST F_DIB = 8
	CONST F_PALETTE = 9
	CONST F_PENDATA = 10
	CONST F_RIFF = 11
	CONST F_WAVE = 12
	CONST F_UNICODETEXT = 13
	CONST F_ENHMETAFILE = 14
	CONST F_HDROP = 15
	CONST F_LOCALE = 16
	CONST F_DIBV5 = 17

	PROCEDURE Clipboard
		_hwnd = IDTOHND(GETID(GET_THISUWSC_WIN))
		_heap = GetProcessHeap()
	FEND

	PROCEDURE Dispose()
		WHILE Del(); WEND
	FEND


	FUNCTION GetTypicalFormat(n=-1)
		RESULT = 0
		IF n = -1 THEN n = LENGTH(_datas)
		IF (n <= 0) OR (n > LENGTH(_datas)) THEN EXIT
		n = _datas[n - 1, HASH_KEY]
		DIM data = _datas[n], i, hMem, fmtName, size
		IF LENGTH(GetFormatName(data[0])) THEN RESULT = data[0]
		FOR i = 0 TO LENGTH(data) - 1 STEP 2
			SELECT data[i]
			CASE F_UNICODETEXT, F_BITMAP, F_HDROP
				RESULT = data[i]
				BREAK
			DEFAULT
				IFB RESULT = 0 THEN
					IF LENGTH(GetFormatName(data[i])) THEN RESULT = data[i]
				ENDIF
			SELEND
		NEXT
		IF RESULT = 0 THEN RESULT = data[0]
	FEND

	FUNCTION Trans(fmt)
		IF fmt = F_DIBV5 OR fmt = F_DIB THEN fmt = F_BITMAP
		IF fmt = F_ENHMETAFILE THEN fmt = F_METAFILEPICT
		IF fmt = F_TEXT OR fmt = F_OEMTEXT THEN fmt = F_UNICODETEXT
		RESULT = fmt
	FEND

	FUNCTION Peek(fmt=F_HDROP, n=-1, p=FALSE)
		// 相互変換の読み替え
		fmt = Trans(fmt)
		IFB fmt = F_HDROP THEN
			RESULT = SPLIT(EMPTY)
		ELSEIF fmt = F_UNICODETEXT THEN
			RESULT = EMPTY
		ELSE
			RESULT = 0
		ENDIF
		IF n = -1 THEN n = LENGTH(_datas)
		IF (n <= 0) OR (n > LENGTH(_datas)) THEN EXIT
		n = _datas[n - 1, HASH_KEY]
		DIM data = _datas[n], i, hMem, size
		FOR i = 0 TO LENGTH(data) - 1 STEP 2
			IF p THEN PRINT "Peek " + data[i] + ":" + GetFormatName(data[i])
			IFB data[i] = fmt THEN
				hMem = data[i+1]
				IFB fmt = F_HDROP THEN
					DIM fileNum = 0, fileName, j
					IF hMem THEN fileNum = DragQueryFileW(hMem, -1, NULL, 0)
					IF fileNum THEN RESULT = SAFEARRAY(0, fileNum - 1)
					FOR j = 0 TO fileNum - 1
						size = DragQueryFileW(hMem, j, NULL, 0)
						IFB size THEN
							size = size + 1
							fileName = FORMAT(CHR(0), size)
							IF DragQueryFileW(hMem, j, fileName, size) THEN RESULT[j] = fileName
						ENDIF
					NEXT
				ELSEIF fmt = F_UNICODETEXT THEN
					size = HeapSize(_heap, 0, hMem)
					RESULT = FORMAT(CHR(0), size / 2)
					DEF_DLL RtlMoveMemory(var wstring, DWORD, DWORD): kernel32
					RtlMoveMemory(RESULT, hMem, size)
					DEF_DLL RtlMoveMemory(DWORD[], DWORD, DWORD): kernel32
				ELSE
					RESULT = hMem
				ENDIF
			ENDIF
		NEXT
	FEND

	FUNCTION GetFormatName(fmt)
		DIM size = 64, fmtName = FORMAT(CHR(0), size)
		SELECT fmt
		CASE F_TEXT
			fmtName = "Text"
		CASE F_BITMAP
			fmtName = "Bitmap"
		CASE F_METAFILEPICT
			fmtName = "Meta File Pict"
		CASE F_SYLK
			fmtName = "SYLK"
		CASE F_DIF
			fmtName = "DIF"
		CASE F_TIFF
			fmtName = "Tiff"
		CASE F_OEMTEXT
			fmtName = "OEM Text"
		CASE F_DIB
			fmtName = "DIB"
		CASE F_PALETTE
			fmtName = "Palette"
		CASE F_PENDATA
			fmtName = "Pen Data"
		CASE F_RIFF
			fmtName = "Riff"
		CASE F_WAVE
			fmtName = "Wave"
		CASE F_UNICODETEXT
			fmtName = "Unicode Text"
		CASE F_ENHMETAFILE
			fmtName = "Enh-Meta File"
		CASE F_HDROP
			fmtName = "Files"
		CASE F_LOCALE
			fmtName = "Locale"
		CASE F_DIBV5
			fmtName = "DIBv5"
		DEFAULT
			WHILE GetClipboardFormatNameW(fmt, fmtName, size - 1) >= size - 2
				size = size * 2
				fmtName = FORMAT(CHR(0), size)
			WEND
		SELEND
		RESULT = fmtName
	FEND

	FUNCTION Count()
		RESULT = LENGTH(_datas)
	FEND

	FUNCTION Get()
		RESULT = OpenClipboard(_hwnd)
		IFB RESULT THEN
			DIM hClip, size, data = SAFEARRAY(0, 0), index = 0
			DIM fmt = EnumClipboardFormats(0)
			DEF_DLL RtlMoveMemory(DWORD, DWORD, DWORD): kernel32
			WHILE fmt <> 0
				hClip = GetClipBoardData(fmt)
				IFB fmt = Trans(fmt) THEN
					// 相互変換があるのは捨てる
					RESIZE(data, LENGTH(data) + 1)
					data[index] = fmt
					IFB fmt = F_BITMAP THEN
						data[index+1] = PopupMenuEx.GetBitmapCopy(_hwnd, hClip)
					ELSE
						size = GlobalSize(hClip)
						data[index+1] = HeapAlloc(_heap, 0, size)
						RtlMoveMemory(data[index+1], GlobalLock(hClip), size)
						GlobalUnlock(hClip)
					ENDIF
					index = index + 2
				ENDIF
				fmt = EnumClipboardFormats(fmt)
			WEND
			DEF_DLL RtlMoveMemory(DWORD[], DWORD, DWORD): kernel32
			IFB LENGTH(data) > 1 THEN
				// 一つ余分なので削る
				RESIZE(data, LENGTH(data) - 2)
				index = 1
				IF LENGTH(_datas) THEN index = VAL(_datas[LENGTH(_datas) - 1, HASH_KEY]) + 1
				_datas[index] = data
				RESULT = LENGTH(_datas)
			ELSE
				RESULT = 0
			ENDIF
			CloseClipboard()
		ELSE
			MSGBOX("OpenClipboard fail.")
		ENDIF
	FEND

	FUNCTION Set(n=-1, del=TRUE)
		IF n = -1 THEN n = LENGTH(_datas)
		RESULT = (n > 0) AND (n <= LENGTH(_datas))
		IFB RESULT THEN
			RESULT = OpenClipboard(_hwnd)
		ELSE
			MSGBOX("No Clipboard stack. " + n)
			EXIT
		ENDIF
		IFB RESULT THEN
			RESULT = EmptyClipboard()
		ELSE
			MSGBOX("OpenClipboard fail.")
			EXIT
		ENDIF
		IFB RESULT THEN
			DIM data = _datas[n - 1, HASH_VAL], size = LENGTH(data), index = 0, j, k, m
			IF del THEN j = _datas[n, HASH_REMOVE]

			DEF_DLL RtlMoveMemory(DWORD, DWORD, DWORD): kernel32
			WHILE (index < size) AND (RESULT)
				IFB data[index] = F_BITMAP THEN
					RESULT = (SetClipboardData(data[index], PopupMenuEx.GetBitmapCopy(_hwnd, data[index+1])) > 0)
					IF del THEN PopupMenuEx.DeleteObject(data[index+1])
				ELSE
					k = HeapSize(_heap, 0, data[index+1])
					m = GlobalAlloc(2, k)
					j = GlobalLock(m)
					IFB j THEN
						RtlMoveMemory(j, data[index+1], k)
						GlobalUnlock(m)
						RESULT = (SetClipboardData(data[index], m) > 0)
					ELSE
						MSGBOX("GlobalLock fail.")
						RESULT = FALSE
					ENDIF
					IF del THEN HeapFree(_heap, 0, data[index+1])
				ENDIF
				index = index + 2
			WEND
			DEF_DLL RtlMoveMemory(DWORD[], DWORD, DWORD): kernel32

			CloseClipboard()
		ELSE
			CloseClipboard()
			MSGBOX("EmptyClipboard fail.")
		ENDIF
	FEND

	FUNCTION Del(n=-1)
		RESULT = FALSE
		IF n = -1 THEN n = LENGTH(_datas)
		IF (n <= 0) OR (n > LENGTH(_datas)) THEN EXIT
		DIM key = _datas[n - 1, HASH_KEY], data = _datas[key], i
		RESULT = _datas[key, HASH_REMOVE]
		FOR i = 0 TO LENGTH(data) - 1 STEP 2
			IFB data[i] = F_BITMAP THEN
				PopupMenuEx.DeleteObject(data[i+1])
			ELSE
				HeapFree(_heap, 0, data[i+1])
			ENDIF
		NEXT
	FEND

ENDMODULE

なお、イメージの場合、幅64ピクセルに縮小(場合によっては拡大)します。
サイズを変えたい場合は、"/64"で探すとすぐ見つかると思います。


バージョンは、処理を共通化するため、CF_BITMAPを捨ててましたが、今度はDIB系を捨ててCF_BITMAPを使ってます。
(PopupMenuExで使いまわす利便性の都合上)