Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

how to copy a file to the clipboard


  • Please log in to reply
58 replies to this topic
nitrix
  • Guests
  • Last active:
  • Joined: --
thanks lexikos ! :D

you are right it works with .bmp files but not with .jpg, or .gif

do you think it's best to convert the file (.jpg .gif ...) to a temporary bmp (using PhiLho's GDI+ wrapper) http://www.autohotke...topic11860.html) file prior to use your function or do you think it's possible to modify the function to accept more formats (well really, jpg, gif and png would be enough) ?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I've been trying to use GDI+ to load the image. Specifically, GdipCreateBitmapFromFile, then GdipCreateHBITMAPFromBitmap. It seems to give me a valid GDI bitmap*, but Paint and Paint.Net tell me there is no recognized format on the clipboard. :(

* Assigning the bitmap to a Picture control using STM_SETIMAGE shows the expected bitmap, and SetClipboardData succeeds, yet Paint and Paint.NET don't recognize it. They know something is on the clipboard, but somehow can't recognize the format. :? I'm giving up for the night (its 11 PM here)... maybe I'll have better luck in the morning.

Here's the code (which doesn't work), if anyone is interested:
; Initialize GDI+
pToken := Gdip_Startup()

Gdip_ImageToClipboard("insert\a\valid\image\path\here.jpg")

; Uninitialize GDI+
Gdip_Shutdown(pToken)


Gdip_ImageToClipboard(Filename)
{
    pBitmap := Gdip_CreateBitmapFromFile(Filename)
    if !pBitmap
        return
    hbm := Gdip_CreateHBITMAPFromBitmap(pBitmap)
    Gdip_DisposeImage(pBitmap)
    if !hbm
        return
;     Gui, Add, Picture, hwndpic W800 H600 0xE
;     SendMessage, 0x172, 0, hbm,, ahk_id %pic%
;     Gui, Show
    DllCall("OpenClipboard","uint",0)
    DllCall("EmptyClipboard")
    ; Place the data on the clipboard. CF_BITMAP=0x2
    if ! DllCall("SetClipboardData","uint",0x2,"uint",hbm)
        DllCall("DeleteObject","uint",hbm)
    DllCall("CloseClipboard")
}


Gdip_CreateBitmapFromFile(sFile)
{
    len:=StrLen(sFile), VarSetCapacity(wFile,len*2+1)
    DllCall("MultiByteToWideChar","uint",0,"uint",0,"str",sFile,"int",-1,"uint",&wFile,"int",len+1)
    DllCall("gdiplus\GdipCreateBitmapFromFile","uint",&wFile,"uint*",pBitmap)
    return pBitmap
}

Gdip_CreateHBITMAPFromBitmap(pBitmap, BackColor=0)
{
    DllCall("gdiplus\GdipCreateHBITMAPFromBitmap","uint",pBitmap,"uint*",hBitmap,"uint",BackColor)
    return hBitmap
}

Gdip_DisposeImage(pImage)
{
    return DllCall("gdiplus\GdipDisposeImage","uint",pImage)
}

Gdip_Startup()
{
    if ! DllCall("GetModuleHandle","str","gdiplus")
        DllCall("LoadLibrary","str","gdiplus")
    VarSetCapacity(si,16,0), NumPut(1,si)
    DllCall("gdiplus\GdiplusStartup","uint*",pToken,"uint",&si,"uint",0)
    return pToken
}

Gdip_Shutdown(pToken)
{
    DllCall("gdiplus\GdiplusShutdown","uint",pToken)
    If hmod := DllCall("GetModuleHandle","str","gdiplus")
        DllCall("FreeLibrary","uint",hmod)
}


nitrix
  • Guests
  • Last active:
  • Joined: --
i'm glad this interests you ;)

don't hesitate to give me ahk code you want me to test
...
like trying to open the bitmap in photoshop...

good night :D
cheers, nitrix

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
You can try the code I edited in while you were posting. Just replace the image path in the test code. There's also some commented (Gui) code that I used to check whether a valid bitmap is being created.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Specifically, GdipCreateBitmapFromFile, then GdipCreateHBITMAPFromBitmap. It seems to give me a valid GDI bitmap*, but Paint and Paint.Net tell me there is no recognized format on the clipboard.

I think GdiPlus uses DIB, so you chose wrong format. Have to construct hGlobal for CF_DIB or convert DIB to DDB for CF_BITMAP.

nitrix
  • Guests
  • Last active:
  • Joined: --
@lexikos, does not work in photoshop

@sean, would you be so kind to correct lexikos's code ? :oops:

on a side note :
people on the autohotkey forum are really nice and helping

thanks again :lol:

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

I think GdiPlus uses DIB, so you chose wrong format. Have to construct hGlobal for CF_DIB or convert DIB to DDB for CF_BITMAP.

Actually, I tried creating a DIB from the HBITMAP to use CF_DIB, but that did exactly the same -- Paint and Paint.NET couldn't paste it (though I did figure out why; see below.) CF_BITMAP is described as "A handle to a bitmap." It doesn't say it has to be a device-dependant bitmap...

Here is some working (for me) code, anyway:
; Initialize GDI+
pToken := Gdip_Startup()

Gdip_ImageToClipboard("image.jpg")

; Uninitialize GDI+
Gdip_Shutdown(pToken)


Gdip_ImageToClipboard(Filename)
{
    pBitmap := Gdip_CreateBitmapFromFile(Filename)
    if !pBitmap
        return
    hbm := Gdip_CreateHBITMAPFromBitmap(pBitmap)
    Gdip_DisposeImage(pBitmap)
    if !hbm
        return
    if hdc := DllCall("CreateCompatibleDC","uint",0)
    {
        ; Get BITMAPINFO.
        VarSetCapacity(bmi,40,0), NumPut(40,bmi)
        DllCall("GetDIBits","uint",hdc,"uint",hbm,"uint",0
             ,"uint",0,"uint",0,"uint",&bmi,"uint",0)
        ; GetDIBits seems to screw up and give the image the BI_BITFIELDS
        ; (i.e. colour-indexed) compression type when it is in fact BI_RGB.
        NumPut(0,bmi,16)
        ; Get bitmap bits.
        if size := NumGet(bmi,20)
        {
            VarSetCapacity(bits,size)
            DllCall("GetDIBits","uint",hdc,"uint",hbm,"uint",0
                ,"uint",NumGet(bmi,8),"uint",&bits,"uint",&bmi,"uint",0)
            ; 0x42 = GMEM_MOVEABLE(0x2) | GMEM_ZEROINIT(0x40)
            hMem := DllCall("GlobalAlloc","uint",0x42,"uint",40+size)
            pMem := DllCall("GlobalLock","uint",hMem)
            DllCall("RtlMoveMemory","uint",pMem,"uint",&bmi,"uint",40)
            DllCall("RtlMoveMemory","uint",pMem+40,"uint",&bits,"uint",size)
            DllCall("GlobalUnlock","uint",hMem)
        }
        DllCall("DeleteDC","uint",hdc)
    }
    if hMem
    {
        DllCall("OpenClipboard","uint",0)
        DllCall("EmptyClipboard")
        ; Place the data on the clipboard. CF_DIB=0x8
        if ! DllCall("SetClipboardData","uint",0x8,"uint",hMem)
            DllCall("GlobalFree","uint",hMem)
        DllCall("CloseClipboard")
    }
}

Gdip_CreateBitmapFromFile(sFile)
{
    len:=StrLen(sFile), VarSetCapacity(wFile,len*2+1)
    DllCall("MultiByteToWideChar","uint",0,"uint",0,"str",sFile,"int",-1
        ,"uint",&wFile,"int",len+1)
    DllCall("gdiplus\GdipCreateBitmapFromFile","uint",&wFile
        ,"uint*",pBitmap)
    return pBitmap
}

Gdip_CreateHBITMAPFromBitmap(pBitmap, BackColor=0)
{
    DllCall("gdiplus\GdipCreateHBITMAPFromBitmap","uint",pBitmap
         ,"uint*",hBitmap,"uint",BackColor)
    return hBitmap
}

Gdip_DisposeImage(pImage)
{
    return DllCall("gdiplus\GdipDisposeImage","uint",pImage)
}

Gdip_Startup()
{
    if ! DllCall("GetModuleHandle","str","gdiplus")
        DllCall("LoadLibrary","str","gdiplus")
    VarSetCapacity(si,16,0), NumPut(1,si)
    DllCall("gdiplus\GdiplusStartup","uint*",pToken,"uint",&si,"uint",0)
    return pToken
}

Gdip_Shutdown(pToken)
{
    DllCall("gdiplus\GdiplusShutdown","uint",pToken)
    If hmod := DllCall("GetModuleHandle","str","gdiplus")
        DllCall("FreeLibrary","uint",hmod)
}
It turns out GetDIBits was returning BI_BITFIELDS for biCompression when it should be returning BI_RGB. This line fixes it:
NumPut(0,bmi,16)
According to this page, it might be a problem with my drivers:

GetDIBits is performed on the device driver level. Because different devices may use different formats to store data, only the device knows its particular format. Thus, it is the responsibility of the driver to convert the device format to a device-independent format. If the device driver is not completely reliable, then GetDIBits may return erroneous information. This should be considered when using GetDIBits.



Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
The following has been working fine in my system, although it's not rigorous. The cases where it might not work is .bmp with bpp<=8, but I never tried it with those files.

sFile	:= "C:\test.jpg"

pToken	:= Gdip_Startup()
pBitmap	:= Gdip_CreateBitmapFromFile(sFile)
hBitmap	:= Gdip_CreateHBITMAPFromBitmap(pBitmap)
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)
SetClipboardData(8, hBitmap)
DllCall("DeleteObject", "Uint", hBitmap)
Return

SetClipboardData(nFormat, hBitmap)
{
	DllCall("GetObject", "Uint", hBitmap, "int", VarSetCapacity(oi,84,0), "Uint", &oi)
	hDBI :=	DllCall("GlobalAlloc", "Uint", 2, "Uint", 40+NumGet(oi,44))
	pDBI :=	DllCall("GlobalLock", "Uint", hDBI)
	DllCall("RtlMoveMemory", "Uint", pDBI, "Uint", &oi+24, "Uint", 40)
	DllCall("RtlMoveMemory", "Uint", pDBI+40, "Uint", NumGet(oi,20), "Uint", NumGet(oi,44))
	DllCall("GlobalUnlock", "Uint", hDBI)
	DllCall("OpenClipboard", "Uint", 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "Uint", nFormat, "Uint", hDBI)
	DllCall("CloseClipboard")
}


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
It also works fine for me. I used GetDIBits only because I overlooked the fact that GetObject will return a DIBSECTION when cbBuffer is set to sizeof(DIBSECTION). GetObject requires less code, and is apparently less buggy. :)

I tested both scripts with a 16 colour bitmap, and both worked! :shock:

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

CF_BITMAP is described as "A handle to a bitmap." It doesn't say it has to be a device-dependant bitmap...

I knew this from experience, but, never confirmed it in the official documentation. However, I suppose it's a reasonable assumption as there exists a separate format CF_DIB.

It turns out GetDIBits was returning BI_BITFIELDS for biCompression when it should be returning BI_RGB. This line fixes it:

NumPut(0,bmi,16)

I think BI_BITFIELDS is legitimate here (for 16/32 bpp) too. If you ever used Window's PrintScreen, you would notice that the captured (32bpp) DIB always had this value set. In this case, you only need to append 3 color masks in ARGB format for Red/Green/Blue after BITMAPINFOHEADER.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

However, I suppose it's a reasonable assumption as there exists a separate format CF_DIB.

I would have made that assumption had CF_DIB used a HBITMAP, but it uses a BITMAPINFO structure. Also, for symmetry, it should be CF_DDB if it must be device dependant... :roll:

I think BI_BITFIELDS is legitimate here (for 16/32 bpp) too. If you ever used Window's PrintScreen, you would notice that the captured (32bpp) DIB always had this value set. In this case, you only need to append 3 color masks in ARGB format for Red/Green/Blue after BITMAPINFOHEADER.

At some point I knew that, but must have discarded it as "irrelevant." When I re-read the description, I saw "color table" and immediately thought "it doesn't have one!" (which is true, but it has a "color mask table" in its place. :roll:)

Thanks.

Now I wonder how those 4-bit bitmaps worked... I guess GetDIBits also returns the "color table".

(I have to enclose "color" in quote marks since my mind keeps screaming "COLOUR!!" ;))

nitrix
  • Guests
  • Last active:
  • Joined: --
this is so cool thank you so much !

it really is a time-saver

say you want to copy a photograph into photoshop file... you have to open the file, select all, copy, go to your file, paste

now it's just : shortcut key ... and paste !

awesome :D

  • Guests
  • Last active:
  • Joined: --
how about for selected file in a filemanager. that is copy full path/filename/extension in text to clipboard. I get alot of hits with search but cant find this. If anyone know please post url

thanks

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Send ^c
:?:

If you want to paste a file (path) as text...
^v::Send %Clipboard%


Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Now I wonder how those 4-bit bitmaps worked... I guess GetDIBits also returns the "color table".

Possibly. However, I'm reluctant to use GetDIBits for this, as the documentation says the hBitmap should be DDB with G(S)etDIBits. GetDIBColorTable may be the one to use.

(I have to enclose "color" in quote marks since my mind keeps screaming "COLOUR!!" ;))

What a surprise (:actually I understand the reason here). I had to resist the temptation to use ColourZoomer when naming the script ColorZoomer.ahk... I still don't know why, though.

PS. Maybe converting to DDB is a better choice, using BitBlt or alike, which is equally applicable for any value of bpp. Even in case of bpp>8, using CreateDIBitmap appears simpler.