Now I've tried the usual ways of grabbing a file's icon:
Shell32\SHGetFileInfo: only returns 32x32 sized icons and I cannot make it return the path of a file's icon using SHGFI_ICONLOCATION
ExtractAssociatedIcon: only returns 32x32 sized icons
Gdip_CreateBitmapFromFile(): doesn't read every format
AssocQueryApp(sExt): only returns the exe path, not the icon number
reading the registry: isn't reliable
But all of those ways usually fail to get the right icon or it's the wrong size, especially with those oddball shell shortcuts. So the best I could see is to use that image that appears over your cursor while dragging. From what I've found in this script, what's required is way over my head:
Code: Select all
#SingleInstance Force
GuiOpen:
Gui, +LastFound
Gui, Add, ListView, R20 W600, #|Format|Size|Data
Gui, Show, X10 Y10
hGui := WinExist()
VarSetCapacity(IDropTarget,32,0)
NumPut(&IDropTarget+4,IDropTarget)
nParams=3116516
Loop, Parse, nParams
NumPut( RegisterCallback("IDropTarget","",A_LoopField,A_Index-1) ,IDropTarget,4*A_Index)
DllCall("ole32\OleInitialize","Uint",0)
DllCall("ole32\RegisterDragDrop","Uint",hGui,"Uint",&IDropTarget)
Return
GuiClose:
Gui, Destroy
DllCall("ole32\RevokeDragDrop","Uint",hGui)
DllCall("ole32\OleUninitialize")
ExitApp
IDropTarget(this, pdata=0, key=0, x=0, y=0, peffect=0)
{
If A_EventInfo = 4 ; dragging loop over window
{
NumPut(NumGet(y+0)&5,y+0)
tooltip DragOver
}
Else If A_EventInfo = 3 ;dragging to window
{
NumPut(NumGet(peffect+0)&5,peffect+0)
tooltip DragEnter
}
Else If A_EventInfo = 6 ;item dropped
{
NumPut(NumGet(peffect+0)&5,peffect+0), IEnumFormatEtc(pdata)
tooltip Drop
}
Else If A_EventInfo = 0
{
NumPut(this,key+0)
tooltip some4
}
if A_EventInfo = 5 ;leaving window
tooltip DragLeave
Return 0
}
IEnumFormatEtc(this)
{
LV_Delete()
DllCall(NumGet(NumGet(1*this)+32),"Uint",this,"Uint",1,"UintP",penum) ; DATADIR_GET=1, DATADIR_SET=2
Loop
{
VarSetCapacity(FormatEtc,20,0)
If DllCall(NumGet(NumGet(1*penum)+12), "Uint", penum, "Uint",1, "Uint", &FormatEtc, "Uint",0)
Break
0+(nFormat:=NumGet(FormatEtc,0,"Ushort"))<18 ? RegExMatch("CF_TEXT CF_BITMAP CF_METAFILEPICT CF_SYLK CF_DIF CF_TIFF CF_OEMTEXT CF_DIB CF_PALETTE CF_PENDATA CF_RIFF CF_WAVE CF_UNICODETEXT CF_ENHMETAFILE CF_HDROP CF_LOCALE CF_DIBV5", "(?:\w+\s+){" . nFormat-1 . "}(?<FORMAT>\w+\b)", CF_) : nFormat>=0x80&&nFormat<=0x83 ? RegExMatch("CF_OWNERDISPLAY CF_DSPTEXT CF_DSPBITMAP CF_DSPMETAFILEPICT", "(?:\w+\s+){" . nFormat-0x80 . "}(?<FORMAT>\w+\b)", CF_) : nFormat=0x8E ? CF_FORMAT:="CF_DSPENHMETAFILE" : CF_FORMAT:=GetClipboardFormatName(nFormat)
VarSetCapacity(StgMedium,12,0)
If DllCall(NumGet(NumGet(1*this)+12), "Uint", this, "Uint", &FormatEtc, "Uint", &StgMedium)
Continue
If NumGet(StgMedium,0)=1 ; TYMED_HGLOBAL=1
{
hData:=NumGet(StgMedium,4)
pData:=DllCall("GlobalLock", "Uint", hData)
nSize:=DllCall("GlobalSize", "Uint", hData)
VarSetCapacity(sData,1023), DllCall("wsprintf", "str", sData, "str", DllCall("advapi32\IsTextUnicode", "Uint", pData, "Uint", nSize, "Uint", 0) ? "%S" : "%s", "Uint", pData, "Cdecl")
DllCall("GlobalUnlock", "Uint", hData)
;~ if (CF_FORMAT = "FileName") ;here we can get file name of dropped file
LV_Add("", A_Index, CF_FORMAT, nSize, sData)
}
Else RegExMatch("TYMED_NULL TYMED_FILE TYMED_ISTREAM TYMED_ISTORAGE TYMED_GDI TYMED_MFPICT TYMED_ENHMF", "(?:\w+\s+){" . Floor(ln(NumGet(StgMedium)+1)/ln(2)) . "}(?<STGMEDIUM>\w+\b)", TYMED_), LV_Add("", A_Index, CF_FORMAT, "?", TYMED_STGMEDIUM)
DllCall("ole32\ReleaseStgMedium","Uint",&StgMedium)
}
DllCall(NumGet(NumGet(1*penum)+8), "Uint", penum)
LV_ModifyCol()
}
GetClipboardFormatName(nFormat)
{
VarSetCapacity(sFormat, 255)
DllCall("GetClipboardFormatName", "Uint", nFormat, "str", sFormat, "Uint", 256)
Return sFormat
}
Over on MSDN I found this, which explains:
I just have no idea how to translate that into something I can do with AHK. Would it be possible to use DLL_Struct() to make all of this easier? All I really want to do is use a file's large icon to make a bitmap with GDIP.Drop targets that have a window can register a DI_GETDRAGIMAGE window message for it by initializing the drag-and-drop helper object with IDragSourceHelper::InitializeFromWindow. When the target receives a DI_GETDRAGIMAGE message, the handler puts the drag image bitmap information in the SHDRAGIMAGE structure that is passed as the message's lParam value.