Shoutbox

[REQ/HELP] Display Picture as Window Icon - Printable Version

-Shoutbox (https://shoutbox.menthix.net)
+-- Forum: MsgHelp Archive (/forumdisplay.php?fid=58)
+--- Forum: Messenger Plus! for Live Messenger (/forumdisplay.php?fid=4)
+---- Forum: Scripting (/forumdisplay.php?fid=39)
+----- Thread: [REQ/HELP] Display Picture as Window Icon (/showthread.php?tid=92466)

[REQ/HELP] Display Picture as Window Icon by NoWhereMan on 10-04-2009 at 02:57 PM

I was trying to do this myself but it looks like it is beyond my (barebone) knowledge of the Win32 API, so I'm asking if anyone is in the mood of doing it instead ;)

basically I'm asking for a script that changes a ChatWindow's icon to a mini-thumbnail of the contact's DP, as seen in other compatible clients such as Pidgin.

working code on matti's post

[Image: r5RSQl.jpg]

(sw in screenie: switcher)


RE: [REQ/HELP] Display Picture as Window Icon by matty on 10-05-2009 at 02:05 PM

That is an interesting idea. Only thought is that at 16x16 pixels you wont be able to see the image that well considering the original is 96x96...

Anyways it will look something like this...

Untested as I am at work!

Javascript code:
var objChatWnds = {};
 
function setDpAsIcon ( sDpLocation , oChatWnd ) {
    var GdipToken = Interop.Allocate ( 4 );
    var GdipStartupInput = Interop.Allocate ( 16 );
        GdipStartupInput.WriteDWORD( 0 , 1 );
       
    var bInitialized = Interop.Call( 'gdiplus' , 'GdiplusStartup' , GdipToken , GdipStartupInput , 0 ) === 0;
   
    if ( bInitialized === true ) {
        var nImgPtr = Interop.Allocate ( 4 );
        Interop.Call( 'gdiplus' , 'GdipLoadImageFromFile' , sDpLocation , nImgPtr );
        var hImage = nImgPtr.ReadDWORD ( 0 ) !== 0 ? nImgPtr.ReadDWORD ( 0 ) : false;
       
        if ( hImage !== false ) {
            var hIcon = Interop.Allocate ( 4 );
            Interop.Call ( 'gdiplus' , 'GdipCreateHICONFromBitmap' , hImage , hIcon );
            objChatWnds [ oChatWnd ] = hIcon;
            Interop.Call( 'user32' , 'SendMessageW' , oChatWd.Handle , 0x80 /* WM_SETICON */ , 0 /* ICON_SMALL */ , hIcon );
            Interop.Call( 'user32' , 'SendMessageW' , oChatWd.Handle , 0x80 /* WM_SETICON */ , 1 /* ICON_BIG */ , hIcon );
        }
    }
    Interop.Call( 'gdiplus' , 'GdiplusShutdown' , GdipToken.ReadDWORD ( 0 ) );
}
 
function OnEvent_ChatWndCreated ( oChatWnd ) {
    if ( oChatWnd.Contacts.Count === 1 ) {
        for ( var oContact = new Enumerator ( oChatWnd.Contacts ); !oContact.atEnd ( ); oContact.moveNext ( ) ) {
            setDpAsIcon ( oContact.item( ).DisplayPicture , oChatWnd );
        }
    }
}
 
function OnEvent_ChatWndDestroyed ( oChatWnd ) {
    if ( typeof objChatWnds [ oChatWnd.Handle ] === 'object ' ) {
        if ( oChatWnd.Contacts.Count === 1 ) {
            Interop.Call( 'user32' , 'SendMessageW' , oChatWd.Handle , 0x80 /* WM_SETICON */ , 0 /* ICON_SMALL */ , 0 );
            Interop.Call( 'user32' , 'SendMessageW' , oChatWd.Handle , 0x80 /* WM_SETICON */ , 1 /* ICON_SMALL */ , 0 );
            Interop.Call ( 'user32' , 'DestroyIcon' , objChatWnds [ oChatWnd.Handle ] );
            delete objChatWnds [ oChatWnd.Handle ];
        }
    }
}


If the image needs to be resized that isn't a problem. Just need to use the GdipGetImageThumbnail call.

This is from Screenshot Sender 5:

Javascript code:
 "CreateThumbnail" : function (hPicture) {
        var ImageThumb = Interop.Allocate(4);
        var lBitmap = Interop.Allocate(4);
        var hbmBitmap = Interop.Allocate(4);
        Interop.Call('gdiplus', 'GdipCreateBitmapFromHBITMAP', hPicture, 0, lBitmap)
        Interop.Call('gdiplus', 'GdipGetImageThumbnail', lBitmap.ReadDWORD(0), 100, this.ImageHeight/(this.ImageWidth/100), ImageThumb.DataPtr, 0, 0);
        Interop.Call('gdiplus', 'GdipCreateHBITMAPFromBitmap', ImageThumb.ReadDWORD(0), hbmBitmap, 0x00000000);
        return  hbmBitmap.ReadDWORD(0);
    }


RE: [REQ/HELP] Display Picture as Window Icon by NoWhereMan on 10-05-2009 at 03:48 PM

Here's an updated version with an (optional) fixed size 16x16 thumb; unfortunately, neither calling it or not calling it seems to be able to make it work :/ (the icon is only cleared)

thank you a lot for your help, though :)

note: with the 32x32 or 48x48 icon available the avatar would be displayed on alt-tab too (and eventually on docks if you have one)

Javascript code:
var objChatWnds = {};
 
function getThumbnailFromPic(hPicture) {
        var ImageThumb = Interop.Allocate(4);
        var lBitmap = Interop.Allocate(4);
        var hbmBitmap = Interop.Allocate(4);
        Interop.Call('gdiplus', 'GdipCreateBitmapFromHBITMAP', hPicture, 0, lBitmap); /* 100, this.ImageHeight/(this.ImageWidth/100), */
        Interop.Call('gdiplus', 'GdipGetImageThumbnail', lBitmap.ReadDWORD(0), 16, 16, ImageThumb.DataPtr, 0, 0);
        Interop.Call('gdiplus', 'GdipCreateHBITMAPFromBitmap', ImageThumb.ReadDWORD(0), hbmBitmap, 0x00000000);
        return  hbmBitmap.ReadDWORD(0);
}
 
function setDpAsIcon ( sDpLocation , oChatWnd ) {
    var GdipToken = Interop.Allocate ( 4 );
    var GdipStartupInput = Interop.Allocate ( 16 );
        GdipStartupInput.WriteDWORD( 0 , 1 );
       
    var bInitialized = Interop.Call( 'gdiplus' , 'GdiplusStartup' , GdipToken , GdipStartupInput , 0 ) === 0;
   
    if ( bInitialized === true ) {
        var nImgPtr = Interop.Allocate ( 4 );
        Interop.Call( 'gdiplus' , 'GdipLoadImageFromFile' , sDpLocation , nImgPtr );
        var hImage = nImgPtr.ReadDWORD ( 0 ) !== 0 ? nImgPtr.ReadDWORD ( 0 ) : false;
       
        if ( hImage !== false ) {
            var hIcon = Interop.Allocate ( 4 );
 
            // uncomment for thumb:
            // hImage = getThumbnailFromPic(hImage);
           
            Interop.Call ( 'gdiplus' , 'GdipCreateHICONFromBitmap' , hImage , hIcon );
            objChatWnds [ oChatWnd.Handle ] = hIcon;
            Interop.Call( 'user32' , 'SendMessageW' , oChatWnd.Handle , 0x80 /* WM_SETICON */ , 0 /* ICON_SMALL */ , hIcon );
            Interop.Call( 'user32' , 'SendMessageW' , oChatWnd.Handle , 0x80 /* WM_SETICON */ , 1 /* ICON_BIG */ , hIcon );
        }
    }
    Interop.Call( 'gdiplus' , 'GdiplusShutdown' , GdipToken.ReadDWORD ( 0 ) );
}
 
function OnEvent_ChatWndCreated ( oChatWnd ) {
    if ( oChatWnd.Contacts.Count === 1 ) {
        for ( var oContact = new Enumerator ( oChatWnd.Contacts ); !oContact.atEnd ( ); oContact.moveNext ( ) ) {
            setDpAsIcon ( oContact.item( ).DisplayPicture , oChatWnd );
        }
    }
}
 
function OnEvent_ChatWndDestroyed ( oChatWnd ) {
    if ( typeof objChatWnds [ oChatWnd.Handle ] === 'object ' ) {
        if ( oChatWnd.Contacts.Count === 1 ) {
            Interop.Call( 'user32' , 'SendMessageW' , oChatWnd.Handle , 0x80 /* WM_SETICON */ , 0 /* ICON_SMALL */ , 0 );
            Interop.Call( 'user32' , 'SendMessageW' , oChatWnd.Handle , 0x80 /* WM_SETICON */ , 1 /* ICON_SMALL */ , 0 );
            Interop.Call ( 'user32' , 'DestroyIcon' , objChatWnds [ oChatWnd.Handle ] );
            delete objChatWnds [ oChatWnd.Handle ];
        }
    }
}


RE: [REQ/HELP] Display Picture as Window Icon by NoWhereMan on 10-05-2009 at 04:46 PM

«An exception was thrown while calling "GdipCreateHICONFromBitmap" in "gdiplus"» :/

EDIT: I think the problem is that function requires a Bitmap struct as input :/


RE: [REQ/HELP] Display Picture as Window Icon by Matti on 10-05-2009 at 05:26 PM

That doesn't work since GdipCreateHICONFromBitmap requires Bitmap to be a GDI+ Bitmap and not a HBITMAP.

However, I managed to get this working by using the code in matty's reply to [REQ/HELP] Display Picture as Window Icon. The only real thing that needed changing was that the hIcon had to be read from the DataBloc before being passed to WM_SETICON or DestroyIcon since those normal Win32 functions use the handle as a regular DWORD. Oh, and of course you made some typos. :P

Javascript code:
var objChatWnds = {};
 
// Let's not forget the already opened chats
function OnEvent_Initialize(MessengerStart) {
    if(Messenger.MyStatus > 0) {
        for( var oChatWnd = new Enumerator ( Messenger.CurrentChats ); !oChatWnd.atEnd ( ); oChatWnd.moveNext ( ) ) {
            OnEvent_ChatWndCreated( oChatWnd.item() );
        }
    }
}
 
// Destroy our generated icons on shutdown
function OnEvent_Uninitialize() {
    for( var hChatWnd in objChatWnds ) {
        Interop.Call ( 'user32' , 'DestroyIcon' , objChatWnds [ oChatWnd.Handle ] );
        delete objChatWnds [ oChatWnd.Handle ];
    }
}
 
// Main function
function setDpAsIcon ( sDpLocation , oChatWnd ) {
    var GdipToken = Interop.Allocate ( 4 );
    var GdipStartupInput = Interop.Allocate ( 16 );
        GdipStartupInput.WriteDWORD( 0 , 1 );
 
    // Initialize GDI+
    var bInitialized = Interop.Call( 'gdiplus' , 'GdiplusStartup' , GdipToken , GdipStartupInput , 0 ) === 0;
 
    if ( bInitialized === true ) {
        // Load a GDI+ Image from the file
        var nImgPtr = Interop.Allocate ( 4 );
        Interop.Call( 'gdiplus' , 'GdipLoadImageFromFile' , sDpLocation , nImgPtr );
        var hImage = nImgPtr.ReadDWORD ( 0 ) !== 0 ? nImgPtr.ReadDWORD ( 0 ) : false;
 
        if ( hImage !== false ) {
            // Get a HICON from the GDI+ Bitmap
            var hIcon = Interop.Allocate ( 4 );
            Interop.Call ( 'gdiplus' , 'GdipCreateHICONFromBitmap' , hImage , hIcon );
            // Read the actual handle and save it
            hIcon = hIcon.ReadDWORD ( 0 );            objChatWnds [ oChatWnd.Handle ] = hIcon;
            // Set the chat window icon
            Interop.Call( 'user32' , 'SendMessageW' , oChatWnd.Handle , 0x80 /* WM_SETICON */ , 0 /* ICON_SMALL */ , hIcon );
            Interop.Call( 'user32' , 'SendMessageW' , oChatWnd.Handle , 0x80 /* WM_SETICON */ , 1 /* ICON_BIG */ , hIcon );
        }
    }
 
    // Shutdown GDI+
    Interop.Call( 'gdiplus' , 'GdiplusShutdown' , GdipToken.ReadDWORD ( 0 ) );
}
 
// Set the icon on new chat windows
function OnEvent_ChatWndCreated ( oChatWnd ) {
    if ( oChatWnd.Contacts.Count === 1 ) {
        var oContact = new Enumerator ( oChatWnd.Contacts ).item();
        setDpAsIcon ( oContact.DisplayPicture , oChatWnd );
    }
}
 
// Remove the icon when a chat window is closed
function OnEvent_ChatWndDestroyed ( oChatWnd ) {
    if ( typeof objChatWnds [ oChatWnd.Handle ] === 'object' ) {
        Interop.Call( 'user32' , 'SendMessageW' , oChatWnd.Handle , 0x80 /* WM_SETICON */ , 0 /* ICON_SMALL */ , 0 );
        Interop.Call( 'user32' , 'SendMessageW' , oChatWnd.Handle , 0x80 /* WM_SETICON */ , 1 /* ICON_SMALL */ , 0 );
        Interop.Call ( 'user32' , 'DestroyIcon' , objChatWnds [ oChatWnd.Handle ] );
        delete objChatWnds [ oChatWnd.Handle ];
    }
}}

Just one small note: when tabbed chats are enabled, the big icon won't show up, but the small ones will still work in the title bar.

Enjoy! ;)

quote:
Originally posted by NoWhereMan
EDIT: I think the problem is that function requires a Bitmap struct as input :/
Luckily, it doesn't.

So my guess would be that the Image class resulted from the loaded file will already be a Bitmap instance, it simply decides which derived class to use based on the type of the loaded file.

quote:
Originally posted by matty
Typos?
Yes, like oChatWd where you want oChatWnd and typeof o === 'object ' where you want typeof o === 'object'. (note the loss of the space between object and ' )
But I'll forgive you because you couldn't test it. :)
RE: [REQ/HELP] Display Picture as Window Icon by matty on 10-05-2009 at 05:31 PM

quote:
Originally posted by Matti
That doesn't work since GdipCreateHICONFromBitmap requires Bitmap to be a GDI+ Bitmap and not a HBITMAP.

However, I managed to get this working by using the code in matty's reply to [REQ/HELP] Display Picture as Window Icon. The only real thing that needed changing was that the hIcon had to be read from the DataBloc before being passed to WM_SETICON or DestroyIcon since those normal Win32 functions use the handle as a regular DWORD. Oh, and of course you made some typos. :P
Typos?

And oops... forgot about the DataBloc... as stated untest as I am at work.

This is going to sound like an excuse... however my keyboard has been acting funny. It causes me to return to the previous page, or overwrite previous typed text, or highlight the last letter typed... its fucked. But its intermittent.
RE: [REQ/HELP] Display Picture as Window Icon by NoWhereMan on 10-05-2009 at 05:47 PM

well guys, thank you for the hard work, this is just awesome :)
I updated my first post with a link to the working code. Maybe you'll want to wrap this thing up and submit it on the main site :)

EDIT: screenie in first post for added awesomeness


RE: [REQ/HELP] Display Picture as Window Icon by ryxdp on 10-06-2009 at 01:35 AM

Cool script, thanks matti, but what about when the contact has no display picture? I've added to the code a bit (for my own use unless you want me to share it :P) to use the WLM9 image for no DP. It doesn't seem to like transparency much so I put a white layer under the actual image so it looks a bit better.

Also, what about when the contact changes their DP mid-conversation? I guess all that can be done is put a timer on there that goes off every 15 seconds or so and checks the path against the current stored data.


RE: [REQ/HELP] Display Picture as Window Icon by NoWhereMan on 10-06-2009 at 06:26 AM

quote:
Originally posted by ryxdp
Also, what about when the contact changes their DP mid-conversation? I guess all that can be done is put a timer on there that goes off every 15 seconds or so and checks the path against the current stored data.


I wouldn't poll the filesystem just for that. I would just ignore the change; your DP will be updated the next time you close and re-open the chat window anyway :)
RE: RE: [REQ/HELP] Display Picture as Window Icon by ryxdp on 10-06-2009 at 07:19 AM

quote:
Originally posted by NoWhereMan
I wouldn't poll the filesystem just for that. I would just ignore the change; your DP will be updated the next time you close and re-open the chat window anyway :)

It sort of is the whole point of the script, but yeah, you're probably right. Anyway, maybe 15 seconds would have been a bit too fast; perhaps a minute or five might be more appropriate :P
RE: [REQ/HELP] Display Picture as Window Icon by Matti on 10-06-2009 at 02:55 PM

I'd say: if anyone wanted to build a full-blown script for this with a nice GUI and all kinds of settings and tweaks, then go for it. :)


RE: [REQ/HELP] Display Picture as Window Icon by NoWhereMan on 10-06-2009 at 03:26 PM

I'll pass the ball around on this :)
by the way this script is already fine for me :)