1 (изменено: creature.ws, 2013-05-26 19:58:05)

Тема: AHK: Замена системного курсора изображением хранящимся в виде Hex

По мотивам сообщения
К сожалению, ссылки в посте выше не актуальны.


; http://www.autohotkey.com/forum/topic35600.html
#SingleInstance Force

~MButton::SetSystemCursor(A_ScriptDir "\blank.cur")
~MButton Up::RestoreCursors()

SetSystemCursor(Cursor)
{
   CursorHandle := DllCall("LoadCursorFromFile", Str, Cursor)
   DllCall("SetSystemCursor", Uint, CursorHandle, Int, "32512")
}

RestoreCursors()
{
   SPI_SETCURSORS := 0x57
   DllCall("SystemParametersInfo", UInt, SPI_SETCURSORS, UInt, 0, UInt, 0, UInt, 0)
}

Курсор: blank.cur

При нажатии Mbutton системный «Arrow» замещается приложенным «пустым» курсором, при отпускании – восстанавливается первоначальное состояние.

Есть желание избавиться от «лишнего файла», поместив blank.cur в тело скрипта, но нет достаточных для выполнения задачи знаний.

Как установить курсор сохранённый в виде Hex данных в переменной?

Добавлено:
Требуемая функциональность обеспечивается примером из справки. Модифицированный вариант:

Space::ToggleSystemCursor()

ToggleSystemCursor() {
    static state
    static Cursors := [
        (Join,
        [32512], [32513], [32514], [32515]
        [32516], [32642], [32643], [32644]
        [32645], [32646], [32648], [32649]
        [32650], [32651], [32641], [32640]
        )]

    (state = "") && InitCursors(Cursors)

    handle := (state := !state)? "blank":"default"

    loop % Cursors.MaxIndex() {
        Cursor := Cursors[A_Index]
        hImage := CopyCursorImage(Cursor[handle])
        DllCall("SetSystemCursor"
            , "Ptr",  hImage
            , "UInt", Cursor[1])
    }
}

InitCursors(Cursors) {
    loop % Cursors.MaxIndex() {
        Cursor := Cursors[A_Index]

        if !cursorInstance := DllCall("LoadCursor"
            , "Ptr", 0
            , "Ptr", Cursor[1])
        {
            Cursors.Remove(A_Index)
            continue
        }

        Cursor.default := CopyCursorImage(cursorInstance)
        Cursor.blank   := CreateBlankCursor()
    }
}

CreateBlankCursor() {
    static andMask, xorMask
    static initAndMask := VarSetCapacity(andMask, 32*4, 0xFF)
    static initXorMask := VarSetCapacity(xorMask, 32*4, 0)
    return DllCall("CreateCursor"
        , "Ptr", 0
        , "Int", 0
        , "Int", 0
        , "Int", 32
        , "Int", 32
        , "Ptr", &andMask
        , "Ptr", &xorMask)
}

CopyCursorImage(ByRef cursorInstance) {
    return DllCall("CopyImage"
        , "Ptr",  cursorInstance
        , "UInt", 2
        , "Int",  0
        , "Int",  0
        , "UInt", 0)
}

2

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

Не создавая временный файл?

3 (изменено: creature.ws, 2011-12-20 00:13:39)

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

Не знаю как действие было реализовано здесь, но хотелось бы без дополнительных сущностей

4

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

Такой вариант создаёт ресурс в памяти:

#SingleInstance, Force

IconDataHex = ; I_cross.cur
(join
000002000100202000000F00100030010000160000002800000020000000400000000100010000000
000800000000000000000000000000000000000000000000000FFFFFF000000000000000000000000
000000000000000000000000000000000000010000000100000001000000010000000100000001000
0000100000001000001FFFF0000010000000100000001000000010000000100000001000000010000
000100000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7FFFFFFD7FFFFFFD7FFFFFFD7FFFFFFD7FFFFF
FD7FFFFFFD7FFFFFFD7FFFFC01007FFDFFFF7FFC01007FFFFD7FFFFFFD7FFFFFFD7FFFFFFD7FFFFFF
D7FFFFFFD7FFFFFFD7FFFFFFC7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFF
)

VarSetCapacity(IconData, nSize:=StrLen(IconDataHex)//2)

Loop, % nSize
   NumPut("0x" . SubStr(IconDataHex,2*A_Index-1, 2), IconData, A_Index-1, "UChar")

~MButton::SetSystemCursor()
~MButton Up::RestoreCursors()

SetSystemCursor()
{
   global
   hCur:=DllCall("CreateIconFromResourceEx", "UInt", &IconData
                                           , "UInt", nSize
                                           , "Char", 0
                                           , "UInt", 0x30000
                                           , "Int", 1 ; w
                                           , "Int", 1 ; h
                                           , "UInt", 1)
   TrayTip,, % hCur ", "A_LastError
   DllCall("SetSystemCursor", "Uint", hCur, "Int", 32512)
}

RestoreCursors()
{
   DllCall("SystemParametersInfo", "UInt", SPI_SETCURSORS:=0x57
                                 , "UInt", 0
                                 , "UInt", 0
                                 , "UInt", 0)
}

Тут я использовал стандартный курсор (...\Windows\Cursors\I_cross.cur), а вот если использовать тот пустой, то хэндл функция не возвращает, но и ошибки тоже нет.
Второй вариант с созданием временного файла и удалением его по выходу, работает корректно:

#SingleInstance, Force
OnExit, ExitScript

IconDataHex = ; blank.cur
(join
000002000100202000000000000030010000160000002800000020000000400000000100010000000000000
100000000000000000000000000000000000000000000FFFFFF000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
)

WriteFile("blank.cur", IconDataHex)

~MButton::SetSystemCursor()
~MButton Up::RestoreCursors()

ExitScript:
   FileDelete, blank.cur
   ExitApp

WriteFile(File, Data)
{
   hFile:=DllCall("CreateFile", "Str", File, "Uint", 0x40000000, "Uint", 0
                                                               , "UInt", 0
                                                               , "UInt", 4
                                                               , "Uint", 0
                                                               , "UInt", 0)
   Loop, % StrLen(Data)//2
      DllCall("WriteFile", "UInt", hFile, "UCharP", "0x" . SubStr(Data, 2*A_Index-1, 2)
                                        , "UInt", 1, "UIntP", 0, "UInt", 0)
   DllCall("CloseHandle", "Uint", hFile)
}

SetSystemCursor()
{
   hCur2:=DllCall("LoadCursorFromFile", "Str", "blank.cur")
   DllCall("SetSystemCursor", "Uint", hCur2, "Int", 32512)
}

RestoreCursors()
{
   DllCall("SystemParametersInfo", "UInt", SPI_SETCURSORS:=0x57
                                 , "UInt", 0
                                 , "UInt", 0
                                 , "UInt", 0)
}

5

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

Спасибо.

6

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

Вроде этот код там был.

7

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

Спасибо, теперь картина полна
Вопрос же о методе не использующем временные файлы остаётся открытым.

8

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

InFlames пишет:

...код там был.

Был.

creature.ws пишет:

Есть желание избавиться от «лишнего файла», поместив blank.cur в тело скрипта...

Ну с телом скрипта косяк ввиду того, что функция CreateIconFromResourceEx выдаёт ноль вместо хэндла, а функция GetLastError ошибки не находит, и даже когда CreateIconFromResourceEx возвращает не ноль, то курсор отображается не корректно (поэтому только 1х1 пиксель как вариант). Можно поместить курсор в ресурсы испольняемого файла после компиляции:

#SingleInstance, Force

~MButton::SetSystemCursor()
~MButton Up::RestoreCursors()

SetSystemCursor()
{
   hInstance:=DllCall("GetWindowLong", "Int", A_ScriptHwnd, "Int", -6)
   hCursor:=DllCall("LoadCursor"(A_IsUnicode ? "W":"A"), "UInt", hInstance
                                                       , "Str", "BLANK") ; имя ресурса которое будет задано в ResHacker'e
   DllCall("SetSystemCursor", "UInt", hCursor ; hcur
                            , "Int", 32512) ; id - OCR_NORMAL
}

RestoreCursors()
{
   DllCall("SystemParametersInfo", "UInt", 0x57 ; uiAction - SPI_SETCURSORS
                                 , "UInt", 0 ; uiParam
                                 , "UInt", 0 ; pvParam
                                 , "UInt", 0) ; fWinIni
}

Компилируем скрипт, и открываем исполняемый файл в ResHacker'e, в пункте меню Action выбираем Add a new Resource:
http://i.imgur.com/3Ctov.jpg
Задаём ему имя:
http://i.imgur.com/9WIhm.jpg
Теперь ресурс добавлен, сохраняем:
http://i.imgur.com/eZm46.jpg
Таким способом можно поместить любой ресурс, главное правильно его использовать.

9

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

ResHacker позволяет модифицировать ресурсы через параметры командной строки:

ResHacker.exe -add SourceName.exe, NewName.exe, blank.cur, CURSORGROUP, BLANK, 0

Если нужно модифицировать за один раз несколько ресурсов, то можно создать скрипт:

[FILENAMES]
Exe=...\SourceName.exe
SaveAs=...\NewName.exe.exe
Log=LogName.log
[COMMANDS]
-add ...\blank.cur, CURSORGROUP, BLANK, 0

И запустить:

ResHacker.exe -script имя_файла_скрипта

10

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

История имеет продолжение , модифицировал скрипт в #6 сообщении этой темы, теперь курсоры загружаются исключительно из памяти, изменён способ отслеживания текущей раскладки на менее ресурсоёмкий - с помощью хука отслеживаем активацию окна и меняем курсор на соответствующий текущему языку в окне, делаем тоже при нажатии одной из клавиш Ctrl, Alt, Shift.

Код без метки Init и связанных с ней данных:


#NoEnv
#SingleInstance force

Gui, +LastFound
hgui:=WinExist()

if !DllCall( "RegisterShellHookWindow","uint",hgui)
	Return
MsgNum:=DllCall("RegisterWindowMessage","str","SHELLHOOK")

LIST=32512,32513,32514,32515,32516,32642,32643,32644,32645,32646,32648,32649,32650,32651
OCR_NORMAL:=32512
OCR_IBEAM:=32513
Gosub, Init

;~ Склеивание данных для больших файлов
hex_32514.=hex_32514_1 . hex_32514_2 . hex_32514_3 . hex_32514_4 . hex_32514_5
hex_32650.=hex_32650_1 . hex_32650_2 . hex_32650_3 . hex_32650_4 . hex_32650_5

Loop, Parse, LIST, CSV
{
	Size:=Hex2Bin(hex_%A_LoopField%)
	if hcur:=LoadAniCursorIcon(&hex_%A_LoopField%,Size)
	DllCall("SetSystemCursor","uint",hcur,"int",A_LoopField)
}

EN_ARROW_Size:=Hex2Bin(hex_EN_ARROW)
RU_ARROW_Size:=Hex2Bin(hex_RU_ARROW)
EN_IBEAM_Size:=Hex2Bin(hex_EN_IBEAM)
RU_IBEAM_Size:=Hex2Bin(hex_RU_IBEAM)

OnExit, Exit
OnMessage(MsgNum,"WndProc")
Return

~Esc::
Exit:
DllCall("SystemParametersInfo","uint",0x57,"uint",0,"uint",0,"uint",0)
DllCall("DeregisterShellHookWindow","uint",hgui)
ExitApp

~Shift::
~Ctrl::
~Alt::
hawnd:=DllCall("GetForegroundWindow")
KeyWait, % SubStr(A_ThisHotkey,2)
Gosub, ChangeCursorByLanguage
Return

WndProc(wParam,lParam)
{
	global hawnd
	if (wParam=4) ; HSHELL_WINDOWACTIVATED
	{
		hawnd:=lParam
		if !hawnd
		hawnd:=DllCall("GetForegroundWindow")
		Gosub, ChangeCursorByLanguage
	}
}

ChangeCursorByLanguage:
ThreadID:=DllCall("GetWindowThreadProcessId","uint",hawnd,"uint",0)
InputLocaleID:=DllCall("GetKeyboardLayout","uint",ThreadID)
if (InputLocaleID=Prev_InputLocaleID)
Return
if (InputLocaleID=0x4090409)
{	
	if hcur:=LoadAniCursorIcon(&hex_EN_ARROW,EN_ARROW_Size)
	DllCall("SetSystemCursor","uint",hcur,"int",OCR_NORMAL)
	if hcur:=LoadAniCursorIcon(&hex_EN_IBEAM,EN_IBEAM_Size)
	DllCall("SetSystemCursor","uint",hcur,"int",OCR_IBEAM)
}
else
{
	if hcur:=LoadAniCursorIcon(&hex_RU_ARROW,RU_ARROW_Size)
	DllCall("SetSystemCursor","uint",hcur,"int",OCR_NORMAL)
	if hcur:=LoadAniCursorIcon(&hex_RU_IBEAM,EN_IBEAM_Size)
	DllCall("SetSystemCursor","uint",hcur,"int",OCR_IBEAM)
}
Prev_InputLocaleID:=InputLocaleID
return

LoadAniCursorIcon(pBuf,BufSize)
{
	type:=NumGet(pBuf+0,2,"char") ; 0- курсор, 1-иконка, 70-анимированный курсор
	if type=70
	hcur:=DllCall("CreateIconFromResourceEx","uint",pBuf,"uint",BufSize,"int",0,"uint",0x30000,"int",0,"int",0,"uint",0x40)
	else if type=1
	hcur:=DllCall("CreateIconFromResourceEx","uint",pBuf+22,"uint",NumGet(pBuf+0,14),"int",1,"uint",0x30000,"int",0,"int",0,"uint",0x40)
	else
	{
		NumPut(NumGet(pBuf+10,0,"short"),pBuf+18,0,"short"), NumPut(NumGet(pBuf+12,0,"short"),pBuf+20,0,"short")
		hcur:=DllCall("CreateIconFromResourceEx","uint",pBuf+18,"uint",NumGet(pBuf+0,14),"int",0,"uint",0x30000,"int",0,"int",0,"uint",0x40)
		
		/* альтернативный способ создания курсора или иконки с помощью CreateIconIndirect
		cw:=NumGet(pBuf+6,0,"char"), ch:=NumGet(pBuf+7,0,"char"), bmsize:=NumGet(pBuf+0,14), pbi:=pBuf+22
		NumPut(cw,pbi+4), NumPut(ch,pbi+8)
		; комбинируем значение альфа или другого цветового канала со своим значением
		Loop, % bmsize//4
		{
			alphach:=pBuf+65+A_Index*4
			NumPut(0x7f&NumGet(alphach+0,0,"char"),alphach+0,0,"char")
		}
		hdc:=DllCall("GetDC","uint",0)
		hbmColor:=DllCall("CreateDIBitmap","uint",hdc,"uint",pbi,"uint",4,"uint",pbi+40,"uint",pbi,"uint",0)
		DllCall("ReleaseDC","uint",0,"uint",hdc)
		biBitCount:=NumGet(pbi+14,0,"short"), MaskBitsOffset:=(cw*ch*biBitCount)/8
		hbmMask:=DllCall("CreateBitmap","int",cw,"int",ch,"uint",1,"uint",1,"uint",pbi+40+MaskBitsOffset)
		; переворачиваем биты маски по вертикали
		hdc:=DllCall("GetDC","uint",0)
		hmemdc:=DllCall("CreateCompatibleDC","uint",hdc)
		hprbm:=DllCall("SelectObject","uint",hmemdc,"uint",hbmMask)
		DllCall("StretchBlt","uint",hmemdc,"int",0,"int",0,"int",cw,"int",ch,"uint",hmemdc,"int",0,"int",ch,"int",cw,"int",-ch,"uint",0xCC0020)
		DllCall("SelectObject","uint",hmemdc,"uint",hprbm)
		DllCall("DeleteDC","uint",hmemdc)
		DllCall("ReleaseDC","uint",0,"uint",hdc)
		VarSetCapacity(ICONINFO,20), NumPut(NumGet(pBuf+10,0,"short"),&ICONINFO+4)
		NumPut(NumGet(pBuf+12,0,"short"),&ICONINFO+8), NumPut(hbmMask,&ICONINFO+12), NumPut(hbmColor,&ICONINFO+16)
		hcur:=DllCall("CreateIconIndirect","uint",&ICONINFO)
		DllCall("DeleteObject","uint",hbmMask)
		DllCall("DeleteObject","uint",hbmColor)
		*/
	}
	Return hcur
}

Hex2Bin(Byref Hex)
{
	nSize:=StrLen(Hex)//2
	VarSetCapacity(buf,nSize)
	Loop % nSize
	NumPut("0x" . SubStr(Hex,2*A_Index-1,2),buf,A_Index-1,"char")
	DllCall("RtlMoveMemory","uint",&Hex,"uint",&buf,"uint",nSize)
	return nSize
}

При испытании случайным образом был обнаружен конфликт с приложением аналогичного назначения Aml Maple, пояснительный рисунок прилагается:

http://s014.radikal.ru/i329/1112/50/3ecd94fdd8e3.png

На этом история возможно не заканчивается, в следующей серии хочется увидеть exe файл способный самостоятельно загружать в себя курсоры и хранить их там в качестве ресурсов с последующим их использованием на замену стандартным системным.

Cursors_from_hex.zip

Полезные ссылки:

Microsoft Windows Cursor and Icon
http://www.iconolog.org/info/icoFormat.html
ICO (file format)
http://en.wikipedia.org/wiki/ICO_(file_format)
ICO (формат файлов)
http://ru.wikipedia.org/wiki/ICO_(формат_файлов)

What is an animated cursor
http://www.gdgsoft.com/anituner/help/aniformat.htm
The Programmer's File and Data Resource
http://www.wotsit.org
ANI (file format)
http://en.wikipedia.org/wiki/ANI_(file_format)

About Cursors
http://msdn.microsoft.com/en-us/library … s.85).aspx
Windows XP icon and cursor support
http://support.microsoft.com/kb/307213
How To Create an Alpha Blended Cursor or Icon in Windows XP
http://support.microsoft.com/kb/318876

Loading Animated Cursor from AutoHotkeySC.bin
http://www.autohotkey.com/forum/topic75100.html
Crazy Scripting : Include an Icon in your script
http://www.autohotkey.com/forum/viewtopic.php?t=33955
MakeICOBars - Create icon bar graphs for tray & pic
http://www.autohotkey.com/forum/topic25992-15.html
Menu Icons v2
http://www.autohotkey.com/forum/topic21991.html

11

Re: AHK: Замена системного курсора изображением хранящимся в виде Hex

Напоминаю разработчикам не стесняться помещать ценные разработки в Коллекцию.