Fanatic Live: MSN Messenger 7 Current Playing Song - Fanatic Live

Jump to content

This is not a support forum!

This forum is intended for users to post source they have created and want to share, it is not to be used for asking for source. New topics made here require moderator approval, and questions will be deleted.
  • (3 Pages)
  • +
  • 1
  • 2
  • 3
  • You cannot start a new topic
  • You cannot reply to this topic

MSN Messenger 7 Current Playing Song Rate Topic: -----

#21 User is offline   Doggie

  • I'm Watching You -_-'
  • Icon
  • Group: Admins
  • Posts: 5,334
  • Joined: 04-February 02
  • Gender:Male
  • Location:Australia
  • Interests:Things that are interesting?

Posted 15 February 2005 - 03:35 AM

nice work :)
0

#22 User is offline   CookieRevised

  • I'm a new version
  • Icon
  • Group: Valued Members
  • Posts: 71
  • Joined: 17-December 03
  • Location:Belgium

Posted 15 February 2005 - 06:48 AM

sorry for this long post, but there are some things which needs correction :D
(especially in silverspeed's final code)


shaneh, on Feb 14 2005, 09:43 AM, said:

I dont know about VB, but Len probably returns the length of the string or something.
Len() returns the amount of characters in a string (not the byte length of the string, if you want the byte length you use LenB()).

shaneh, on Feb 14 2005, 09:43 AM, said:

Note that the structure must be layed out in memory with no other garbage, just pure WCHARs. I believe VB uses BSTRs with all sorts of control characters surrounding arrays and strings etc.
No, VB does not put "all sorts of characters surrounding arrays and strings etc"... VB stores its strings indeed as BSTRs, but these are actually your normal WCHAR[]s in a way. BSTRs are nothing more then pointers to pointers to the address where the WCHAR[]s are stored (and minus 4 bytes of this address you have the length of the string as it isn't 0-terminated)

If you know how to handle these, they work as easy as WCHAR[]s or any other form of data storage... When you want the pointer to the actual data contained in the WCHAR[] string you use StrPtr(), when you want the pointer to the BSTR "string" (which is actually contains the pointer to the WCHAR[] string) you use VarPtr(). VarPtr() can also be used on any other element (thus, strings are actually a special case, hence the use of StrPtr()).

shaneh, on Feb 14 2005, 09:59 AM, said:

Actually the problem with the VB code is you are creating a structure with Strings. In VB, Strings are basically just pointers.

You need an array of 100 wide characters, not a pointer to a string. The pointers are taking up just 4 bytes, you need 200 (100 wide characters  = 200 bytes) per title/artist/album.
And because VB uses unicode to store the actual strings (thus WCHAR[]s) you just need to assign 100 (wide) characters to your declaration of strings. (otherwise they are indeed BSTRs pointers): Dim S as String * 100

shaneh, on Feb 14 2005, 09:59 AM, said:

Im not even sure whether its possible to do it in VB (ugh).
Of course, why shouldn't it? VB is not the lame language that many people think it is (because of hear-say). It's not because it is called "basic" that it can't do anything...



Now for the VB code...

shaneh, on Feb 14 2005, 03:29 PM, said:

but I would suspect you would need to do something like:
Type msnstruct
  intMsnCommand As Integer
  szTitle (0 To 200) As Byte
  szArtist (0 To 200) As Byte
  szTitle (0 To 200) As Byte
  szWmCommand (0 To 80) As Byte
End Type
That's indeed a possible way (using byte arrays) in most cases. But a long and unneeded way in the end for this program (see the code from silverspeed). Because, in the end you want to end up with WCHAR[]s (which is what VB internally uses anyway in a way), so why not using the normal/internal way of string handling in basic...

By using byte-arrays this is what will happen:
You have an ascii string S = "Hello"
1) VB stores it internally as a BSTR (which is in the end already stored as a WCHAR = "H.e.l.l.o.")
2) you grab the string from memory
3) VB converts it back to ascii ("Hello") to give you the string
4) you do some manipulation so you can store it as a WCHAR[] (again back to "H.e.l.l.o.") into another variable being the byte array.
5) VB stores this byte array as bytes (ofcourse), thus "H.e.l.l.o."
6) in the end you must give a pointer to this array

Now, doesn't this seem like a very long complicated and useless way (for this problem case)?

But there is another thing which isn't correct though (even if you do use the byte array method): szTitle (0 To 200) As Byte
This makes an byte array of 201 elements. The whole structure will be bigger then 684 bytes (0x02AC)! And hence I see some strange and unneeded calculations in silverspeeds final code to compensate this (putting #0 in front of 2 strings, starting to count from 1 instead of 0, ...) And above all, it will result in wrongly aligned text anyways (try to fill all the available space or try to put some accented characters like "é" in and you'll see):

'change title
sTitle = Chr(0) & sTitle
For i = 0 To Len(sTitle) - 1
  msnpush.Title(i * 2) = Asc(Mid(sTitle, i + 1, 1))
Next
     
'change artist
sArtist = Chr(0) & sArtist
For i = 1 To Len(sArtist) - 1
  msnpush.Artist((i * 2) - 1) = Asc(Mid(sArtist, i + 1, 1))
Next
     
'change album
For i = 0 To Len(sAlbum) - 1
  msnpush.Album(i * 2) = Asc(Mid(sAlbum, i + 1, 1))
Next

If you declared your intMsnCommand and byte arrays correctly (200 bytes, instead of 201) in the first place, all that manipulation (which is wrong anyways) isn't needed:

Type msnstruct
  intMsnCommand As Long
  szTitle (0 To 199) As Byte
  szArtist (0 To 199) As Byte
  szTitle (0 To 199) As Byte
  szWmCommand (0 To 79) As Byte
End Type

'change title
For i = 0 To Len(sTitle) - 1
  msnpush.Title(i * 2) = Asc(Mid(sTitle, i + 1, 1))
Next
or
'change title
For i = 1 To Len(sTitle)
  msnpush.Title(i * 2 - 2) = Asc(Mid(sTitle, i, 1))
Next
And this for all three the converting routines.

But, you don't need to start the base of the array at 0, 1 or whatever; you can choose what base it has. So if choosen carefully you even can reduce the amount of calculations (the -1's, -2's, +1's, etc):

Type msnstruct
  intMsnCommand As Long
  szTitle (2 To 201) As Byte
  szArtist (2 To 201) As Byte
  szTitle (2 To 201) As Byte
  szWmCommand (2 To 81) As Byte
End Type

'change title
For i = 1 To Len(sTitle)
  msnpush.Title(i * 2) = Asc(Mid(sTitle, i, 1))
Next

Now you see that the code is already getting smaller (and faster) and more logical to follow and read.

But there is 1 (big) thing left to say. I started by saying that the use of byte array's is not needed because VB stores his strings as WCHAR[]s anyway. And also, with that method of converting an ascii string to a forced unicode string, you loose the ability to actually use unicode characters in your songtitles, artists, etc... (try to add characters like é into the strings with your code, then try it with mine)

So to cut the already too long post, here is the whole corrected code:

Option Explicit

Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Private Const WM_COPYDATA = &H4A

Private Type MSNMSGSTRUCT  ' Internal structure of MSNMSGSTRUCT will be:
   MsnCommand As Long       ' ####T.i.t.l.e.A.r.t.i.s.t.A.l.b.u.m.W.m.C.o.m.m.a.n.d.
   Title As String * 100    ' \4b/\200bytes/\-200bytes-/\200bytes/\----80 bytes----/
   Artist As String * 100
   Album As String * 100
   WmCommand As String * 40
End Type

Private Type COPYDATASTRUCT
   dwData As Long
   cbData As Long
   lpData As Long
End Type

Private Sub Form_Load()
    'Change the MSN song info
    ChangeSong "This is the title", "This is the artist", "This is the album"
End Sub

Private Sub Form_Unload(Cancel As Integer)
    'Song stops playing
    StopSong
End Sub

Private Sub ChangeSong(sTitle As String, sArtist As String, sAlbum As String)
     Dim msndata As COPYDATASTRUCT
     Dim msnpush As MSNMSGSTRUCT
     Dim msnui As Long
    
     'Set msncommand, title, artist and album
     'Notice that we don't do any string manipulation as VB internaly
     'converts strings to unicode anyways. Also we can get away with
     'this because we don't provide the MSNMSGSTRUCT structure
     'directly to the API call, but rather the pointer to it, hence VB does
     'not convert the strings back to ANSI, as it normaly would do when
     'you call an API with string parameters.
     msnpush.MsnCommand = 1
     msnpush.Title = sTitle & vbNullChar          'Zero terminate the string
     msnpush.Artist = sArtist & vbNullChar        'Zero terminate the string
     msnpush.Album = sAlbum & vbNullChar          'Zero terminate the string
     msnpush.WmCommand = vbNullChar               'Zero terminate the null string
     
     'Find window & send message
     msnui = FindWindow("MsnMsgrUIManager", vbNullString)
     msndata.dwData = &H547
     msndata.cbData = &H2AC
     msndata.lpData = VarPtr(msnpush)
     SendMessage msnui, WM_COPYDATA, Me.hWnd, msndata   
End Sub

Private Sub StopSong()
     Dim msndata As COPYDATASTRUCT
     Dim msnpush As MSNMSGSTRUCT
     Dim msnui As Long
     
     ''Set msncommand
     ''Not realy needed though, it will be initialized to 0 upon creation anyways.
     ''And if MsnCommand is 0, the rest of the data will be ignored also, no matter
     ''what it contains (hence all this is commented out).
     'msnpush.MsnCommand = 0
     'msnpush.Title = vbNullChar          'Zero terminate the null string
     'msnpush.Artist = vbNullChar         'Zero terminate the null string
     'msnpush.Album = vbNullChar          'Zero terminate the null string
     'msnpush.WmCommand = vbNullChar      'Zero terminate the null string

     'Find window & send message
     msnui = FindWindow("MsnMsgrUIManager", vbNullString)
     msndata.dwData = &H547
     msndata.cbData = &H2AC
     msndata.lpData = VarPtr(msnpush)
     SendMessage msnui, WM_COPYDATA, Me.hWnd, msndata   
End Sub
Edit: fixed space-bug. Also, for a better and more optimalized code, converted to a module and to take polygamy in account, see the new listing in next post.


ok final note for those who are confused or to be more confused :P:
a BSTR is actualy a pointer to a pointer to a UCS-2 unicode string.
a WCHAR* is a pointer to a wide character string.

(note that "unicode" strings and "wide character" strings aren't always the same; a wide character can be longer then 2 bytes (see UTF-8). But in 99% of all cases they are the same. To be strict, the term "wide character" can mean both unicode and UTF-8. In UCS-2 unicode the length of a wide character is always 2 bytes (or 4 bytes in UCS-4 unicode). In UTF-8 the length can be anything from 1 to 6 bytes)

S = "Hello"
In the internal VB memory it looks like this:
####H.e.l.l.o. (4 bytes for the length, followed by the UCS-2 unicode string)

If you want the BSTR pointer:
VarPtr(S)

If you want a pointer to the unicode string itself (the memory offset of "H"):
StrPtr(S)

The 4 bytes which contain the length of the unicode string can be found like so:
StrPtr(S) - 4

If you want to store S in memory as ascii for whatever reason (instead of the default unicode):
A = StrConv(S, vbFromUnicode)
In the memory it looks like this:
####Hello (4 bytes for the length, followed by the string)
Note that when you print A, VB will actually handle it as an unicode string and thus will not print "Hello", but "???".

This post has been edited by CookieRevised: 17 February 2005 - 11:31 AM

0

#23 User is offline   shaneh

  • Nudging is fun!
  • PipPip
  • Group: Members
  • Posts: 17
  • Joined: 14-February 05

Posted 15 February 2005 - 08:19 AM

Sure, Byte arrays arent needed and shouldnt be used. I simply used them to illustrate the memory allocation difference. As I said, I dont 'do' VB. I thought the C code was self explanatory, but didn't realise most people only speak 'vb' around here. :P

Oh, and with regards to the BSTR, AFAIK they are ref counted, so there would be a reference count in there somewhere (the 'garbage' I was talking about - quite different to a straight WCHAR[]). Hence the 'pointer to a pointer' etc, which is why you cant just use a 'String'. A string 'array' in VB on the other hand I guess is essentially just a WCHAR[].

A WCHAR is not a pointer to a wide character string, a WCHAR* is. A WCHAR is just a two byte variable effectively. A WCHAR[] is an array of those.

This post has been edited by shaneh: 15 February 2005 - 08:26 AM

0

#24 User is offline   silverspeed

  • and they keep on going
  • PipPipPipPip
  • Group: Members
  • Posts: 217
  • Joined: 02-August 02
  • Location:Limburg Belgium
  • Interests:Programming and girls

Posted 15 February 2005 - 09:02 AM

@CookieRevised: your code gives me:

his is the title_________________________ - his is the artist_____
0

#25 User is offline   Daniel

  • Liveâ„¢ n00b
  • Icon
  • Group: Admins
  • Posts: 4,598
  • Joined: 01-February 02
  • Location:New Zealand

Posted 15 February 2005 - 09:42 AM

You'll need to add " & vbNullChar" to the lines like "msnpush.Title = sTitle", not sure about the run-away first character :P
0

#26 User is offline   CookieRevised

  • I'm a new version
  • Icon
  • Group: Valued Members
  • Posts: 71
  • Joined: 17-December 03
  • Location:Belgium

Posted 15 February 2005 - 11:56 AM

shaneh, on Feb 15 2005, 09:19 AM, said:

Oh, and with regards to the BSTR, AFAIK they are ref counted, so there would be a reference count in there somewhere (the 'garbage' I was talking about - quite different to a straight WCHAR[]).
The BSTR is a 4-byte pointer which points to a place in the stringtable of VB, the value found on that location is again a pointer which contains the location of the actual start of the WCHAR[]. The "garbage" you are talking about are the 4 bytes before the WCHAR[] which is the length of the WCHAR[]. These 4 bytes are needed because strings in VB aren't 0-terminated, and thus they are also only needed in variable length strings. Hence the pointer to the WCHAR[] points to the beginning of the WCHAR[], not to the length of the string.

In structures, such as used in the program here, those "length bytes" aren't present since the strings are fixed length. The whole structure is just like it should be, a sequence of data without any "garbage" in front, in between or at the end.

shaneh, on Feb 15 2005, 09:19 AM, said:

Hence the 'pointer to a pointer' etc, which is why you cant just use a 'String'.
I'm not using a 'string' (in the BSTR sense), I'm using the actual internal WCHAR[]. I can do this without any manipulation because 1) the strings are in a structure 2) they have a fixed length 3) the API is called with a pointer to the structure.

shaneh, on Feb 15 2005, 09:19 AM, said:

A string 'array' in VB on the other hand I guess is essentially just a WCHAR[].
If you mean 'string array' as in a 'byte array', then yes, and that is also what VB uses internally, and thus which makes it work without any alterations. (If the API needed an ascii string, then you had to explicitly do some alterations, like explained in my last note in my previous post)

shaneh, on Feb 15 2005, 09:19 AM, said:

A WCHAR is not a pointer to a wide character string, a WCHAR* is. A WCHAR is just a two byte variable effectively. A WCHAR[] is an array of those.
Oh, sorry, I meant WCHAR[] all the time...
I've edited my previous post to correct this, thanks...

Daniel, on Feb 15 2005, 10:42 AM, said:

You'll need to add " & vbNullChar" to the lines like "msnpush.Title = sTitle", not sure about the run-away first character :P
View Post

Ok, that doesn't make sense or it is something I don't understand from the C code then:

Are WCHAR[]s zero-terminated???
If so then the total structure size can't be 684 (0x02AC) bytes.

4 bytes for the integer
200 bytes for the wide string + 1 nullchar
200 bytes for the wide string + 1 nullchar
200 bytes for the wide string + 1 nullchar
80 bytes for the wide string + 1 nullchar
-------------------------------------------
=688 bytes


Or what is the most significant byte of a WCHAR?
The first or the last? VB uses normal little endian unicode, "A" is stored as "0x41 0x00".


solved: see Huuf's (very important) remark that VB doesn't fill fixed length strings with null characters but rather with spaces.

final correction :D: VB does fill newly created strings with null characters!! However, when you assign a text to a fixed length string, VB will fill the remaining (unused) characters with spaces.

(and just looking for the lost characters :()

solved: was a bad leftover which I didn't noticed before from the first C to VB ported source earlier in this thread (integer data type in VB is only 2 bytes, in C it is 4 bytes)

This post has been edited by CookieRevised: 17 February 2005 - 11:43 AM

0

#27 User is offline   Daniel

  • Liveâ„¢ n00b
  • Icon
  • Group: Admins
  • Posts: 4,598
  • Joined: 01-February 02
  • Location:New Zealand

Posted 15 February 2005 - 12:15 PM

Man, I dunno, you've got me confused now. But with the code you posted, the first character is missing and is has a load of trailing nothing characters, adding the null byte stops the trailing ones. Did you test it and it worked for you?

EDIT: I think the nullchar whould be apart of the 200, not added on.
0

#28 User is offline   CookieRevised

  • I'm a new version
  • Icon
  • Group: Valued Members
  • Posts: 71
  • Joined: 17-December 03
  • Location:Belgium

Posted 15 February 2005 - 12:43 PM

Daniel, on Feb 15 2005, 01:15 PM, said:

Man, I dunno, you've got me confused now. But with the code you posted, the first character is missing and is has a load of trailing nothing characters, adding the null byte stops the trailing ones. Did you test it and it worked for you?

EDIT: I think the nullchar whould be apart of the 200, not added on.
View Post
Or maybe not, since the WCHAR[]s are also fixed length (in this structure), there is no need for a terminater character.
Edit: indeed they aren't zero terminated in the strict sense. But when a fixed string is declared in C it is filled with null chars anyways, in VB it wil be filled with spaces, #§*&*!.

final correction :D: VB does fill newly created strings with null characters!! However, when you assign a text to a fixed length string, VB will always fill the remaining (unused) characters with spaces. And that's the thruth and nothing but the thruth, so help me God :D

Also, even if nullchars were needed but weren't added, the rest of the string are nullchars anyways (if not the whole 100 characters are used) so it would've contain 'a nullcharacter' anyways ...
Edit: so stupidly wrong from me to say this, see remark above... sorry :angel:

But ermmm... (/me slaps himself) an integer in C is 4 bytes, right? In VB it is only 2 bytes :rolleyes: stupid me... I totaly overlooked that fact in silverspeeds code. In VB intMsnCommand should be declared as Long, not as Integer. That of course explains the 1 character (2 bytes) of...

I fixed my listing...

This post has been edited by CookieRevised: 17 February 2005 - 11:38 AM

0

#29 User is offline   shaneh

  • Nudging is fun!
  • PipPip
  • Group: Members
  • Posts: 17
  • Joined: 14-February 05

Posted 15 February 2005 - 01:15 PM

Yes the "ZeroMemory" call effectively terminates the string, as does "lpstrcpyW" - it inserts a terminating null character. Im suprised VB doesnt initialise variables to NULL, but perhaps you are working with a debug build which wouldnt.

The Integer size of 2 bytes certainly would explain the behaviour you are getting. In C they are 4 bytes when using most (all?) 32 bit compilers.

Good to see the VB code is working now, or at least I assume it is :)

A slight improvement on the code is to do the following:

while (msnui = FindWindowEx(NULL, msnui, "MsnMsgrUIManager", NULL))
{
   do msnpush initialisation
   sendmessage(msnui, WM_COPYDATA,.. etc)
}


This is incase there are other windows registered with this class on the system. This may be the case if the user is running multiple clients (polygamy patches) or MS decides to have other windows with the same class etc.

The WMP plugin does exactly this.

This post has been edited by shaneh: 15 February 2005 - 01:21 PM

0

#30 User is offline   CookieRevised

  • I'm a new version
  • Icon
  • Group: Valued Members
  • Posts: 71
  • Joined: 17-December 03
  • Location:Belgium

Posted 15 February 2005 - 01:34 PM

shaneh, on Feb 15 2005, 02:15 PM, said:

Im suprised VB doesnt initialise variables to NULL
It does... hence that the procedure StopSong works without calling a function like ZeroMemory...

Edit: The procedure StopSong worked because MsnCommand was initialized to 0. And that's the important part of the function though, when MsnCommand is 0, the data is ignored anyways.

shaneh, on Feb 15 2005, 02:15 PM, said:

The Integer size of 2 bytes certainly would explain the behaviour you are getting. In C they are 4 bytes when using most (all?) 32 bit compilers.
Yeah, that's something from the past... C's Shortinteger is Basics Integer. When the animals still could talk there was only Integer (2b), Long (4b), Double (8b) and these have never been changed in Basic for compatibily reasons (luckely they added more numerical data types since then though :P)

This post has been edited by CookieRevised: 17 February 2005 - 11:40 AM

0

#31 User is offline   TheHuuf

  • I'm less than my age :(
  • Pip
  • Group: Members
  • Posts: 10
  • Joined: 16-February 05

Posted 16 February 2005 - 08:09 AM

[edit]
With all combined changes cookierevised made a "compilation"
[/edit]

This post has been edited by TheHuuf: 16 February 2005 - 09:53 PM

0

#32 User is offline   CookieRevised

  • I'm a new version
  • Icon
  • Group: Valued Members
  • Posts: 71
  • Joined: 17-December 03
  • Location:Belgium

Posted 16 February 2005 - 09:38 PM

Excellent :D

Though there are again a bunch of remarks and comments to say about this. (and I cba to PM them also, so I posted the entire module again) Sorry, please don't take my corrections wrongly though. :)

-----EDIT----------NEW----------EDIT----------NEW----------EDIT-----


The attached zip contains:

SETSONG_COMMENTS.BAS
see also the listing below
* proper type declarations for the API's
* no more global variables
* not depending on any formhandle
* very short code

SETSONG_AS_ANY.BAS
an alternative to SETSONG.BAS
* not so proper type declarations for the API's, thus:
* shows difference between: AS LONG vs. AS ANY
* shows difference between: ByRef vs. ByVal
* as an example, contains only 1 procedure to handle both setting and clearing the song

SETSONG_OLD_SOURCE.BAS
As a reference, the source that was previously posted in this post
* not so proper type declarations for the API's
* contains a global variable
* depending on the formhandle
* because of the above, the code was longer

SETSONG_FINAL.BAS
which I never intended to do, but here it is anyways
* proper type declarations for the API's
* no more global variables
* not depending on any formhandle
* not using the API callback function anymore
* very short code
* (but please read/look at the other modules first to understand the comments)





And now, the code for SETSONG_COMMENTS.BAS (thus heavly commented though :P)

For background info (eg: about what a string is in VB) and to see the first codechanges see my earlier post(s) in this thread.

The calls to use in your project based upon the module below:
Call ChangeSong("Title", "Artist", "Album")
Call StopSong()


Note: do not compile any modules with "Assume no aliasing" as aliasing is used in every example module.


Quote


'# INTRO NOTES:
'#
'# First of all, sorry for the heavy comments :D But as this source
'# actually depicts many different and interesting issues about VB and
'# API programming I felt like explaining some (internal) things, so
'# people would understand why something is done in a certain way and
'# because I see many bad things happening in VB sources. Hopefully a
'# few will learn something new from it.
'#
'# Now for the comment system that I used. All extra/explaining comments
'# begin with '#. The normal code-comments just begin with '. So if you
'# want to delete these extra long comments, you will find them easly
'# and you'll be left with the comments that you'll find in normal code.


Option Explicit

Private Declare Function EnumWindows Lib "user32.dll" _
  (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Declare Function GetClassName Lib "user32.dll" Alias "GetClassNameA" _
  (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" _
  (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'# API Declaration of SendMessage changed to reflect the true internal
'# types. Defining parameters as "AS ANY" in VB is sometimes very handy,
'# but if you don't realy know what AS ANY does or why sometimes it is
'# there and sometimes not, it can be very confusing (and your code can
'# have some very hard to catch bugs).
'#
'# Internaly the SendMessage API expects only pointers to be passed. In
'# VB this is reflected as defining the parameters AS LONG; a pointer is
'# 4 bytes. This means that you can't pass the actual value of a variable
'# to it (or directly a string or number for that matter), but you must
'# pass the pointer to that variable.
'#
'# To get a pointer from a variable you can use undocumented functions
'# like VarPtr(), VarObj(), VarStr(), etc... Oldtime Basic programmers
'# will remember these from the good old Basica/GWBasic/Q(uick)Basic
'# days. But for some reason MS doesn't want you to know about them ;)
'# (Also see http://support.micro...kb;en-us;199824)
'#
'# Now to overcome all this (the fact that you can only assign pointers
'# and the fact that the functions like VarPtr() are hidden from the
'# user) they invented the AS ANY declaration. (that's my theory though :D)
'#
'# This will make that VB itself will catch the pointer from a passed
'# variable and passes the pointer from that variable instead.
'#
'# The difference between ByVal and ByRef is easy once you get the hang
'# of it. With ByVal you pass the variable's contents. If you use that
'# in a procedure or function, it will be possible to change the variable
'# in that procedure or function without changing the original variable.
'#
'#  sString = "Hello"
'#  Call MySub(sString)
'#  MsgBox sString 'will result in "Hello"
'#
'#  Sub MySub(ByVal MyString)
'#      MyString = MyString & " World"
'#  End Sub
'#
'# When you use ByRef, then a reference (the pointer) to the variable
'# will be passes. This reference will be used to handle the variable
'# inside the procedure or function. This means that if you use it in
'# a procedure or function the contents of the original variable can
'# and will change.
'#
'#  sString = "Hello"
'#  Call MySub(sString)
'#  MsgBox sString 'will result in "Hello Hello"
'#
'#  Sub MySub(ByRef MyString)
'#    MyString = MyString & " World"
'#  End Sub
'#
'# If you omit ByRef or ByVal in front of a variable, then VB will use
'# ByRef as default!
'#
'# So, all this (ByRef/ByVal and AS ANY/AS <...> is the reason why
'# sometimes you see declaration of API's written as:
'#  APIName Lib "mylib.dll" (ByVal lParam As Long)
'# and sometimes as
'#  APIName Lib "mylib.dll" (lParam As Any)
'# or
'#  APIName Lib "mylib.dll" (ByRef lParam As Any)
'#
'# And that can be very confusing if you don't know what it actually
'# means or does. Once you get the hang of it, the use of ByRef/ByVal
'# and AS ANY can be very usefull and rewarding.
'#
'# And this explains why I did what I did in the source (I hope ;P)


Private Const WM_COPYDATA = &H4A

Private Type MSNMSGSTRUCT    ' Internal memory structure of MSNMSGSTRUCT will be:
  MsnCommand As Long        ' ####T.i.t.l.e.A.r.t.i.s.t.A.l.b.u.m.W.m.C.o.m.m.a.n.d.
  Title As String * 100      ' \4b/\200bytes/\-200bytes-/\200bytes/\----80 bytes----/
  Artist As String * 100
  Album As String * 100
  WmCommand As String * 40
End Type

Private Type COPYDATASTRUCT
  dwData As Long
  cbData As Long
  lpData As Long
End Type

'# Notice the lack of the parameter "frmOwner As Form" which was
'# present in the previous source. It is not needed anymore.

Public Sub ChangeSong(sTitle As String, sArtist As String, sAlbum As String)
  'Create our structures
  '# Notice that we don't do this anymore in the global memory space
  '# but rather in this procedure itself. This is because there is no
  '# use anymore for a global msndata variable and wasting memoryspace
  '# if we directly pass the variable to our subprocedure EnumWindows.

  Dim msnpush As MSNMSGSTRUCT
  Dim msndata As COPYDATASTRUCT
 
  'Set msncommand, title, artist, album and WmCommand
  '# Notice that we don't do any string manipulation as VB internaly
  '# converts strings to unicode anyways. Also we can get away with
  '# this because we don't provide the MSNMSGSTRUCT structure and its
  '# strings directly to the API call, but rather the pointer to it via
  '# the COPYDATASTRUCT structure. Hence VB does not convert the strings
  '# back to ANSI, as it normaly would do when you call an API with
  '# parameters which contain strings.

  msnpush.MsnCommand = 1
  msnpush.Title = sTitle & vbNullChar      'Zero terminate the string
  msnpush.Artist = sArtist & vbNullChar    'Zero terminate the string
  msnpush.Album = sAlbum & vbNullChar      'Zero terminate the string
  msnpush.WmCommand = vbNullChar          'Zero terminate the null string
   
  'Set the pointers and size to msnpush
  msndata.dwData = &H547
  msndata.cbData = &H2AC
  msndata.lpData = VarPtr(msnpush)
   
  'Loop trough all top-level windows and pass the pointer to the
  'msndata structure with it.

  '# Thus notice that we don't pass the handle anymore of the form as
  '# it was done in the previous source. It was of no use and was
  '# ignored by Messenger anyways. This makes that we now have again a
  '# free available parameter to use. Thus we will use it to pass the
  '# pointer of msndata. This makes that msndata doesn't need to be
  '# declared globaly anymore and doesn't take extra memoryspace in
  '# our project. The less global variables, the better.

  Call EnumWindows(AddressOf EnumWinProc, VarPtr(msndata))
   
  'Clean memory
  '# Not the same as clear memory though; Clearing the memory would
  '# mean that the variables wouldn't exist anymore. And this is not
  '# the case when you assign new values to the variables (no matter
  '# if they are 0 or Null or whatever; the memory will still contain
  '# the variables)
  '#
  '# For safety reasons, the previous source still contained:
  '#    msndata.dwData = 0
  '#    msndata.cbData = 0
  '#    msndata.lpData = 0
  '# Because msndata was a global variable. But now notice that we
  '# don't need to clean msnpush and neither msndata. Because they
  '# were defined inside the procedure they both will be automatically
  '# cleared when the procedure exists. Thus since there are no global
  '# variables anymore, there is nothing to clean :P.

End Sub

'# Notice the lack of the parameter "frmOwner As Form" which was
'# present in the previous source. It is not needed anymore.

Public Sub StopSong()
  'Create our structures
  '# See notes in ChangeSong
  Dim msnpush As MSNMSGSTRUCT
  Dim msndata As COPYDATASTRUCT
 
  ''Set msncommand
  'msnpush.MsnCommand = 0
  ''# The above is not realy needed in VB though, it will be initialized
  ''# to 0 already when the structure is created. And if MsnCommand
  ''# is 0, the rest of the msnpush data will be ignored by Messenger
  ''# also, no matter what it contains, but may I stress that msndata
  ''# and msnpush _are_ initialized to all zero's in VB, and thus will not
  ''# contain 'whatever'. (hence all this is double commented out;
  ''# if you do want to set MsnCommand to 0 yourself, then you only need
  ''# to remove one comment character of this entire comment block ;))
  ''# Initializing data to zero is however a commmon practice, especially
  ''# in larger programs where you quickly can loose the scope of things.
  ''# However, in small programs where you exactly know and can see what
  ''# the scope is of every data, it is not wrong to not initialize them
  ''# yourself, in fact you're code will be smaller and run a few ticks
  ''# faster as you wouldn't do a double job.
  ''#
  ''# Also in the previous source there was:
  ''#    msnpush.Title = vbNullChar      'Zero terminate the null string
  ''#    msnpush.Artist = vbNullChar      'Zero terminate the null string
  ''#    msnpush.Album = vbNullChar      'Zero terminate the null string
  ''#    msnpush.WmCommand = vbNullChar  'Zero terminate the null string
  ''# This was done, as an example, to make sure that the strings
  ''# contained at least 1 terminate/null character. Previously I said
  ''# that VB fills fixed length strings with spaces upon the creation
  ''# of the strings, unlike C which fills it with zero's. However, VB
  ''# indeed does fill them with zero's also. It is only when you assign
  ''# text to the variables that the remaining unused characters of the
  ''# fixed strings will be filled with spaces. In other words, assigning
  ''# a null character to those strings was even more not needed and a
  ''# waste of processor time then assigning 0 to MsnCommand ;)
 
  'Set the pointers and size to msnpush
  msndata.dwData = &H547
  msndata.cbData = &H2AC
  msndata.lpData = VarPtr(msnpush)
   
  'Loop trough all top-level windows and pass the pointer to the
  'msndata structure with it.

  '# See notes in ChangeSong
  Call EnumWindows(AddressOf EnumWinProc, VarPtr(msndata))
   
  'Clean memory
  '# See notes in ChangeSong

End Sub

'Callback loop function
'# As previously said, we are going to use the application-defined
'# 32bit value of lParam, which is passed from the calling EnumWindows
'# function. It would be a waste not to make a use of this. In previous
'# source we passed the formhandle with it. But as this wasn't needed
'# anyways we can now pass the pointer to msndata with it. See also the
'# intro notes and the notes in ChangeSong for more explaination.

Private Function EnumWinProc(ByVal LhWnd As Long, ByVal lParam As Long) As Long
  Dim RetVal As Long
  Dim WinClassBuf As String * 255
 
  'Retrieve the class name
  '# If GetClassName succeeds, the return value is the number of
  '# characters copied to the specified buffer. If it fails, the
  '# return value is 0. To get extended error information, call
  '# the GetLastError API (not implemented here).
  '#
  '# Earlier in the notes of ChangeSong I spoke of the fact that VB
  '# automatically converts his internaly used strings back to ANSI
  '# when they are passed to an API. In the case of WinClassBuf this
  '# is no different. Because it is a string it will internaly be
  '# handled as Unicode, and thus will take 255*2 bytes to store in
  '# memory. But when we pass it to the GetClassName API it will
  '# automatically be converted to ANSI, and thus will take only 255
  '# bytes. Hence the third parameter of GetClassName is 255.
  '#
  '# Once the temporarly ANSI string is created, the pointer to it will
  '# be passed to the API. When the API returns, VB will convert the
  '# returned ANSI string back to Unicode and store that in the place
  '# of the previous unicode string (and freeing the memory used for
  '# converting).

  RetVal = GetClassName(LhWnd, WinClassBuf, 255)
 
  'Get full returned string and see if it is the right class
  '# Notice the use of Left$() instead of Left(), this is slightly
  '# faster. Left$() will return a string while Left() will return a
  '# stringtype variant which takes more time to process.

  If Left$(WinClassBuf, RetVal) = "MsnMsgrUIManager" Then
      'Send the message to the found window
      '# Notice the 0&. In the previous source that parameter was the
      '# formhandle which was passed thru lParam. As this wasn't needed,
      '# we can simply pass a zero to that parameter. And use lParam
      '# to pass the pointer to the msndata structure.

      Call SendMessage(LhWnd, WM_COPYDATA, 0&, lParam)
  End If
 
  EnumWinProc = True
End Function

'# The End



The full package with all the example modules (updated with SETSONG_FINAL.BAS):

Attached File(s)


This post has been edited by CookieRevised: 18 February 2005 - 07:02 AM

0

#33 User is offline   shaneh

  • Nudging is fun!
  • PipPip
  • Group: Members
  • Posts: 17
  • Joined: 14-February 05

Posted 17 February 2005 - 10:28 AM

Ive written up a quick foobar plugin with this functionality, with source. You can grab it here if youre interested:

http://forums.winamp...581#post1593581

@CookieRevised: I would recommend using FindWindowEx rather than EnumWindows, its designed for this purpose.

Also, its generally good practice to initialise variables manually rather than rely on the quirks of a language and the state the variables "should" be in. There are plenty of programming texts on the web which discuss this.

The WMP plugin zeroes out the entire structure, so even if not zeroing out the entire structure works, its generally a good idea to emulate the plugin exactly. Messenger may decide to use the structure differently, and depend on the whole structure being initialised, at a later date.
0

#34 User is offline   CookieRevised

  • I'm a new version
  • Icon
  • Group: Valued Members
  • Posts: 71
  • Joined: 17-December 03
  • Location:Belgium

Posted 17 February 2005 - 07:08 PM

shaneh, on Feb 17 2005, 11:28 AM, said:

@CookieRevised: I would recommend using FindWindowEx rather than EnumWindows, its designed for this purpose.
I didn't came up with EnumWindows, Huuf did :D. And that's where I picked up upon with the heavy comments because I could use that as a base to show some stuff (ByRef/ByVal, As Any/As Long).

Anyways, What we are doing in the source is enumerating the windows, so how can EnumWindows not be designed for this? When you closely look at the API and how it is used in the example, EnumWindows is the perfect one to use for this. Note: in this example source though, where I showed how to pass a variable to a function without the use of a global defined variable.

FindWindowEx hasn't the extra user-definable parameter which I used as the important example on how to pass our data, of which almost every comment is based upon.

shaneh, on Feb 17 2005, 11:28 AM, said:

Also, its generally good practice to initialise variables manually rather than rely on the quirks of a language and the state the variables "should" be in. There are plenty of programming texts on the web which discuss this.
Believe me, VB always initializes the variables explicitly. There is realy no need to do it yourself, especially in such a small program. In bigger programs, where you could easly loose the scope it is indeed handy that you do it yourself, and that is were the good practice comes in. But is really not needed at all if you know what you're doing.

If I wouldn't had commented in the source on the initializing stuff (to inform about the automatic initialization) I would indeed initialize it myself to give as an example, but in the end it realy _is_ double code (in VB). If my comments are read carefully, you'll see that I don't say you _may not_ do it, but that I rather say that it isn't _needed_ (thus, in VB that is!).

"it is a good practice to..." comes from the fact that not all programming language set things to zero. Knowing this, and knowing that VB _does_ initialize everything to zero is equaly (if not more) important. Heck, the initializing problem has a very big comment block in the sourcecode just because of this and explaining all this (and the MsnCommand = 0 is still there, because of it). But if I explicitly must add: "it is a good practice to..." to the inline comments to make this clearer, I'll will do, no problem. Although I thought it would have been clear...

shaneh, on Feb 17 2005, 11:28 AM, said:

The WMP plugin zeroes out the entire structure, so even if not zeroing out the entire structure works, its generally a good idea to emulate the plugin exactly.
Why would one exactly want to emulate a plugin? It is Messengers beheviour that we need to take in account, not a plugin which possibly isn't installed on the users system.

Above all, like I said before, the structure _is_ zero'ed out upon initialization! That's the way declaring works in VB (and that's exactly of what I have commented on in the example source itself). It would have been a different story if I did not commented on why I wouldn't initialize the variable myself; then you could say I forgot it, or didn't know about it...

The comments in the source are almost more important to read then the source itself, heck I wrote/compiled the entire thing just to be able to comment on such things/issues.

As I said in the source, the program depicts some many interesting issues/twirks about VB. Otherwise I even wouldn't have bothered and the thread would have ended 10 posts ago or something with another half-finished and buggy VB source, and the C programmers would have had again a reason to call VB crap (no pun intended though).

It is one thing to be able to program, it is another to know in what environment you're programming and what that environment has to offer or can/can't do.

shaneh, on Feb 17 2005, 11:28 AM, said:

Messenger may decide to use the structure differently, and depend on the whole structure being initialised, at a later date.
Not possible anyways, if the plugin/program/whatever quits, the memoryspace of the structure is also cleared, heck even after ending the calling procedure the structure will be cleared.

This is also the way that the receiving program should and will handle. It _must_ make a copy of the passed data via the WM_COPYDATA message if it wants to keep it. See MSDN Library SendMessage/WM_COPYDATA. And if it doesn't need it anymore (MsnCommand = 0) it will be cleared by Messenger anyways. The lifespan of the passed structure is only as long as the time it takes to pass the data with WM_COPYDATA, nothing longer (and this goes for every data you send thru WM_COPYDATA to any program btw). Also it is very inlogic if Messenger decides to use his copy of the structure without first writing new stuff to it when MsnCommand was 0. And may I remind that the passed structure from VB _was_ initialized even if MsnCommand was 0 (see comments in source and above replies). If it wasn't, Messenger wouldn't even accepted it, which is a very logical/mandatory thing to do (see ASM listing earlier in this thread).

This post has been edited by CookieRevised: 18 February 2005 - 01:17 AM

0

#35 User is offline   shaneh

  • Nudging is fun!
  • PipPip
  • Group: Members
  • Posts: 17
  • Joined: 14-February 05

Posted 18 February 2005 - 01:37 AM

EnumWindows requires the checking of every window. ie, your (VB) code gets called for *every* top level window. FindWindowEx returns each window of a given class, and only that class, in sucession, exactly what you want. Its possible MS groups classes together in some table or whatever, so its probably quicker at finding the windows, certainly faster than doing text compares in VB. Its the API call Microsoft have used in their WMP plugin.

The use of lParam etc is a moot point if you are just doing:
while hwnd=findwindowex
sendmessage hwnd, msndata etc

It doesnt buy you any savings. Savings which are irrelevant when you are using a language such as VB which has a lot more overhead than the odd int here and there anyway.

Although the MS compiled VB code may initialise variables to zero, there could be other VB compilers (existing or will exist) that might not initialise variables. Thats why its good practice. Given that the code to do it is neglible to execute, its silly not to. Of course, few VB coders do, but seeing as you did a big write up of what should only be a few lines of code, it would appear you are trying to do it the 'right' way.

You want to emulate the plugin, because MS wrote it. And they are the ones that wrote MSN messenger. You want to emulate it exactly, because MSN messenger shouldnt be able to tell the difference between the WMP plugin or yours. They could change MSN messenger at any time, and its best to have the plugin operating exactly the same as the other "official" plugin.

By "at a later date", I mean at a later vesion of MSN messenger. For example, a later version of MSN messenger may check that the structure is initialised to 0 for whatever reason, when you pass the msncommand=0, or that there are no trailing spaces after the NULL in the titles etc. Its quite common to use NULL deliminated strings terminated by a double-NULL, so the WMP plugin could pass extra data in this way in a later version.

The WMP plugin initialises it, so Messenger can safely do this check without worrying. Unless of course your plugin doesnt operate exactly the same.

Of course, if the particular compiler you choose to compile your code does initialise the structure to 0, youve got nothing to worry about ;)

This post has been edited by shaneh: 18 February 2005 - 02:44 AM

0

#36 User is offline   CookieRevised

  • I'm a new version
  • Icon
  • Group: Valued Members
  • Posts: 71
  • Joined: 17-December 03
  • Location:Belgium

Posted 18 February 2005 - 06:01 AM

shaneh, on Feb 18 2005, 02:37 AM, said:

EnumWindows requires the checking of every window. ie, your (VB) code gets called for *every* top level window. FindWindowEx returns each window of a given class, and only that class, in sucession, exactly what you want.
(...)
I didn't used it because it wasn't suited for the purpose of writing the sources; I never intended to change the kind of API for comparisson reasons. The sources are not meant to be perfect, but rather meant as a reference/a working example, to show different things. See my comments in the sources... If it come across differently then I'm sorry...

shaneh, on Feb 18 2005, 02:37 AM, said:

Although the MS compiled VB code may initialise variables to zero, there could be other VB compilers (existing or will exist) that might not initialise variables. Thats why its good practice. Given that the code to do it is neglible to execute, its silly not to. Of course, few VB coders do, but seeing as you did a big write up of what should only be a few lines of code, it would appear you are trying to do it the 'right' way.
No, I'm not. I'm afraid you missed the point in why I did these sources though. It is to show how things are done in VB (internaly) and how you can use that to your benefits (eg: the loop routine that silverspeed first used, while VB already converts the strings in unicode). Also we _are_ talking about VB, and not about some wild and strange or non-existing compiler. The whole point of all my posts is because it is in VB, not any other language/compiler.

shaneh, on Feb 18 2005, 02:37 AM, said:

You want to emulate the plugin, because MS wrote it. And they are the ones that wrote MSN messenger. You want to emulate it exactly, because MSN messenger shouldnt be able to tell the difference between the WMP plugin or yours. They could change MSN messenger at any time, and its best to have the plugin operating exactly the same as the other "official" plugin.
I understand this, but again, the source (and VB) are operating exactly.

shaneh, on Feb 18 2005, 02:37 AM, said:

By "at a later date", I mean at a later vesion of MSN messenger. For example, a later version of MSN messenger may check that the structure is initialised to 0 for whatever reason, when you pass the msncommand=0, or that there are no trailing spaces after the NULL in the titles etc. Its quite common to use NULL deliminated strings terminated by a double-NULL, so the WMP plugin could pass extra data in this way in a later version.
Good point, although you could also try to compensate for the things you don't know which will happen in the futur. eg: maybe they will change the order of the strings and put artist first.... My point, there is no reason whats-o-ever to assume such things will happen. And by the time that it will happen the whole interface, class, whatever would have changed anyways.

Edit: btw, If so concearned about emulating it exactly and that messenger shouldn't be able to tell the difference, then why did you put "foo_care_not_for_drm" in wmcontentid instead of something else? Because you know it would have no (side)effect? That's almost the same as assuming that the data being send, will be of the same structure for a long time to come. If every program should be made so that futur things will not brake it, then the IT bussiness is up for a big shock.

Again, this wasn't about the "perfect" code at all, that's up to the people who make the actual stuff. I compiled the sources together as a working base to comment on seemingly trivial VB stuff. If someone wants to specifically fill every used fixed length string with zero's, then by all means do it, nothing in the comments tells you that you may not do it, the comments only tell why it is/isn't needed (in the current scope of the program) so that people can decide for themselfs if they want to make it or not. The sources are meant as a base to know what to do/not to do, not as a "here is your perfect code, copy/paste it"-source.


------------------------


Anyways, in reference to these previous posts, I made a final version (again NOT the perfect version, yet heavly commented again). After this, I probably will not look anymore to this thread. Those who want to know some things about VB or why some coders do something in a certain way can have a free look to all the files or look up some info on the web (msdn library is an excellent starting point).

Following attached file is also included in the previous package with the other sources:


------------------------
EDIT: Also see "Msn Messenger 7 Current Playing Song, the new code" for an alternative method with some benefits (longer strings possible for artist, album, etc...).

Attached File(s)


This post has been edited by CookieRevised: 27 March 2005 - 04:51 PM

0

#37 User is offline   tomalak

  • I'm getting there
  • Pip
  • Group: Members
  • Posts: 4
  • Joined: 11-April 05

Post icon  Posted 11 April 2005 - 01:08 PM

I'm using the C code posted above as a DLL to be used with the chat program mIRC, so that I can have a notification of songs playing in mIRC.

It compiles fine, the SendMessage statement is reached successfully. I can't see anything wrong anywhere and as far as both mIRC and the DLL are concerned, everything is fine.

Except MSN simply never shows the notification. Windows Media Player manages it, but when I close that and call SongOn it seems to have no effect.

Are there any thoughts as to what I may be missing??! Thanks...

#include <windows.h>
#include <stdio.h>

#define MAGIC_NUMBER 0x547
COPYDATASTRUCT msndata;

struct msnmsgstruct;
struct msnmsgstruct {
	int msncommand;
	WCHAR title[100];
	WCHAR artist[100];
	WCHAR album[100];
	WCHAR wmcontentid[40];
};

typedef struct {
	DWORD  mVersion;
	HWND   mHwnd;
	BOOL   mKeep;
} LOADINFO;



HWND msnui;


int __declspec(dllexport) __stdcall SongOn(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
	
	int i = 0;
	while (msnui = FindWindowEx(NULL, msnui, "MsnMsgrUIManager", NULL)) {
  msnmsgstruct msnpush;
  ZeroMemory(&msnpush, sizeof(msnpush));
  msnpush.msncommand = 1;

  lstrcpyW(msnpush.title, L"It Can't Come Quickly Enough");
  lstrcpyW(msnpush.artist, L"Scissor Sisters");
  lstrcpyW(msnpush.album, L"Scissor Sisters");
  lstrcpyW(msnpush.wmcontentid, L"123456");

  msndata.dwData = MAGIC_NUMBER;
  msndata.lpData = &msnpush;
  msndata.cbData = sizeof(msnpush);
  SendMessage(msnui, WM_COPYDATA, (WPARAM)(HWND) mWnd, (LPARAM)&msndata);
  //MessageBox(msnui, "Success", "DLL Call", 0x00000020L);

  i++;
	}

	if (i == 0) {
  MessageBox(mWnd, "Failure", "DLL Call", 0x00000020L);
	}

	return 1;
}

int __declspec(dllexport) __stdcall SongOff(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
	
	while (msnui = FindWindowEx(NULL, msnui, "MsnMsgrUIManager", NULL)) {
  msnmsgstruct msnpush;
  ZeroMemory(&msnpush, sizeof(msnpush));
  msnpush.msncommand = 0;

  lstrcpyW(msnpush.title, L"Title");
  lstrcpyW(msnpush.artist, L"Artist");
  lstrcpyW(msnpush.album, L"Album");
  lstrcpyW(msnpush.wmcontentid, L"WMContentID");

  msndata.dwData = MAGIC_NUMBER;
  msndata.lpData = &msnpush;
  msndata.cbData = sizeof(msnpush);
  SendMessage(msnui, WM_COPYDATA, (WPARAM)(HWND) mWnd, (LPARAM)&msndata);
	}

	return 3;
}


int __declspec(dllexport) __stdcall UnloadDll(int mTimeout) {
	return 0;
}

0

#38 User is offline   crock

  • One post hero!
  • Pip
  • Group: Members
  • Posts: 1
  • Joined: 21-April 05

Posted 21 April 2005 - 07:24 PM

It's not working anymore on the final version of msn...
0

#39 User is offline   tomalak

  • I'm getting there
  • Pip
  • Group: Members
  • Posts: 4
  • Joined: 11-April 05

Posted 21 April 2005 - 08:19 PM

crock, on Apr 21 2005, 08:24 PM, said:

It's not working anymore on the final version of msn...
View Post


Are any of the coders reading this thread able to shed any light on this development??
0

#40 User is offline   noroom

  • Because I Rock
  • Icon
  • Group: Valued Members
  • Posts: 3,477
  • Joined: 05-May 02
  • Location:Germany
  • Interests:Internet, Maths, Messenger, Programming, Music (Listening to and Playing), FileSharing, Computer / Software security... etc

Posted 21 April 2005 - 08:29 PM

Option Explicit

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal Hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long

Private Type COPYDATASTRUCT
  dwData As Long
  cbData As Long
  lpData As Long
End Type

Private Const WM_COPYDATA = &H4A
' eg: Call SetMusicInfo("artist", "album", "title")
' eg: Call SetMusicInfo("artist", "album", "title", "WMContentID")
' eg: Call SetMusicInfo("artist", "album", "title", , "{1} by {0}")
' eg: Call SetMusicInfo("", "", "", , , False)
Public Sub SetMusicInfo(ByRef r_sArtist As String, ByRef r_sAlbum As String, ByRef r_sTitle As String, Optional ByRef r_sWMContentID As String = vbNullString, Optional ByRef r_sFormat As String = "{0} - {1}", Optional ByRef r_bShow As Boolean = True)

   Dim udtData As COPYDATASTRUCT
   Dim sBuffer As String
   Dim hMSGRUI As Long
   
   'Total length can not be longer then 256 characters!
   'Any longer will simply be ignored by Messenger.
   sBuffer = "\0Music\0" & Abs(r_bShow) & "\0" & r_sFormat & "\0" & r_sArtist & "\0" & r_sTitle & "\0" & r_sAlbum & "\0" & r_sWMContentID & "\0" & vbNullChar
   
   udtData.dwData = &H547
   udtData.lpData = StrPtr(sBuffer)
   udtData.cbData = LenB(sBuffer)
   
   Do
       hMSGRUI = FindWindowEx(0&, hMSGRUI, "MsnMsgrUIManager", vbNullString)
       
       If (hMSGRUI > 0) Then
           Call SendMessage(hMSGRUI, WM_COPYDATA, 0, VarPtr(udtData))
       End If
       
   Loop Until (hMSGRUI = 0)

End Sub

Private Sub Command1_Click()
SetMusicInfo "Artist", "album", "title"
End Sub


Paste that in a form and add a commandbutton. Works fine for MSN Messenger 7.0.0777
0

  • (3 Pages)
  • +
  • 1
  • 2
  • 3
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users