Old 17th September 2010, 00:15   #1
nithdi
Junior Member
 
Join Date: Sep 2010
Posts: 7
Finding the Media Library Handle - Problem

Hi.

I am currently developing an application for Winamp. It is an EXTERNAL application written in Delphi.

I already got many things to work like: basic controls, insert into playlist, etc...

That was pretty easy since the Winamp API is well documentated!

But now I'm stuck. I want to add m3u8 files to the Media Library Playlist Tree (just as you would drag them in). But I can't figure out how to get the Handle of the Media Library Window.

I already found this thread: http://forums.shoutcast.com/showthread.php?t=173940
But it didn't help much.

I tried to register a new IPC Call:
code:

hwndWinamp := FindWindow('Winamp v1.x', nil);
wa_ml_ipc := Sendmessage(hwndWinamp, WM_USER, wParam(PAnsiChar('LibraryGetWnd')), 65536);



That seems to work. wa_ml_ipc is 65570 after this.

But when I try to geht the Handle:
code:

hwndML := Sendmessage(hwndWinamp, WM_USER, 0, wa_ml_ipc);



it always returns 1.

Do you have any tips how I can retreive the handle for the Media Library?

Thanks in advance!

Georg B.
nithdi is offline   Reply With Quote
Old 17th September 2010, 08:40   #2
DrO
 
Join Date: Sep 2003
Posts: 27,873
PHP Code:
HWND GetMLHwnd(void){
static 
HWND mlwnd;
static 
LONG_PTR libhwndipc;
    if(!
IsWindow(mlwnd)){
        if(!
libhwndipc || libhwndipc <= 65535){
            
libhwndipc = (LONG_PTR)SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&"LibraryGetWnd",IPC_REGISTER_WINAMP_IPCMESSAGE);
        }
        
// passing wParam == -1 forces loading of the library database if not loaded
        // passing wParam != -1 will just return the HWND of the library for api access
        
mlwnd = (HWND)SendMessage(hwnd_winamp,WM_WA_IPC,-1,(LPARAM)libhwndipc);
    }
    return 
mlwnd;

this is the function i personally use (in C) in my plug-ins though it should work ok out of process. the only thing i can think off which might be the cause of your code not working is with the call to the IPC_REGISTER_WINAMP_IPCMESSAGE as i'm not sure how your delphi call equates to the C version.

-daz
DrO is offline   Reply With Quote
Old 17th September 2010, 10:00   #3
nithdi
Junior Member
 
Join Date: Sep 2010
Posts: 7
Thanks for posting your code!

What is meant by plugin.hwndParent? Is this the Handle of Winamps Main Window?
nithdi is offline   Reply With Quote
Old 17th September 2010, 10:31   #4
DrO
 
Join Date: Sep 2003
Posts: 27,873
yes, it's a member from the plugin structure of a general purpose plug-in. i'll change the example to have that as hwnd_winamp to avoid confusion for others.

-daz
DrO is offline   Reply With Quote
Old 17th September 2010, 13:52   #5
nithdi
Junior Member
 
Join Date: Sep 2010
Posts: 7
Hi again.

I've tried various things now, but still wasn't lucky.
I have tried to pass 'getWndLibrary' as a Pointer to a String and in a COPYDATASTRUCT.
Both seems to work, because I'm getting 65570 and 65571 returned as the new IPC Message.

But when I'm trying to get the Handle, the call always returns 1.

Seems to be difficult to get it working in Delphi, or am I missing something here??

Are there any Delphi Experts in this forum? :-)

Greets
nithdi is offline   Reply With Quote
Old 17th September 2010, 14:10   #6
DrO
 
Join Date: Sep 2003
Posts: 27,873
just had a moment of clarity as realised that you're getting a high return value from IPC_REGISTER_WINAMP_IPCMESSAGE which i wouldn't expect considering it's used internally.

so... it's not going to work as is with you working out of process as the code in Winamp isn't able to interpret the same string passed due to the cross-process nature. i need something like this in a project i'm working on so i'll have a look and see what i can come up with. it'll be in C but should be easy enough to port to Delphi though there's not many delphi people around here nowadays (or ever really).

-daz
DrO is offline   Reply With Quote
Old 17th September 2010, 15:25   #7
nithdi
Junior Member
 
Join Date: Sep 2010
Posts: 7
Perfect, thanks.

I was surprised too when the high value was returned. But IPC Calls like IPC_GETPLAYLISTTITLE and IPC_GETPLAYLISTFILE (and the WideString variants) are also working from outer process.
Maybe Delphi hooks into the the Winamp Process and Winamp is thinking that the commands come from inner-process - don't know...

So I'm looking forward to hear from you again :-)

Greets

Edit: Does the Media Library window have a unique classname? I was able to find it when I switched to Winamp Modern SKin (so not Bento) and used:
code:
FindWindow('BaseWindow_RootWnd', 'Medienbibliothek'); //Using german version

But this didn't work with Bento Skin, maybe because the ML window is embedded there?
nithdi is offline   Reply With Quote
Old 17th September 2010, 15:44   #8
DrO
 
Join Date: Sep 2003
Posts: 27,873
that's weird as someone i know who was dabbling with delphi wasn't able to use IPC_GETPLAYLISTTITLE and IPC_GETPLAYLISTFILE and other string based api's without some remote process code.

anyway, i've now got something which will work which involves using using a remote memory buffer in Winamp to hold the "LibraryGetWnd" string which is then passed in the IPC_REGISTER_WINAMP_IPCMESSAGE call. only issue is that the whole code is in C (never seen the delphi port i know was done of it from a few months back despite asking for it *shrugs* ).

you can have a look at specificially the GetMediaLibraryHWND(..) function in http://winampminimagic.svn.sourcefor...c.cpp?view=log which shows how i've got it working. i am meaning to finish off the documentation, etc so i can release the plug-in the project creates but it's not quite finished otherwise i'd include a pre-compiled dll for use (which would probably save some of the issues). i am aiming to get that done by the end of next week however if that's of any use.

as for finding the window in the way mentioned in your edit, modern skins embed the media library window in a two level along with the existing classic container frame so it's not simple to find it (hence the "LibraryGetWnd" implemented for the internal plug-ins needing to access the window) as the title will and does vary between skins. this is why i've got the variety of checks in the GetWinampHWND(..) function in the code (which also helps to detect if it's a true Winamp window or a fake one as other apps tend to create).

-daz
DrO is offline   Reply With Quote
Old 19th September 2010, 15:15   #9
nithdi
Junior Member
 
Join Date: Sep 2010
Posts: 7
Hi.

Finally my Code is working and I'm getting a valid Media Library Handle.
For all Delphi users having the same problem, here's the code you're probably looking for:
code:
//Allocates Memory in Winamp's Address Space
function AllocateMem(Handle: THandle; Size: Integer): Pointer;
var
dwPid: DWORD;
Proc: THandle;
Addr: Pointer;
begin
//check Handle
if Handle <> 0 then
begin
//get Proecss ID
GetWindowThreadProcessId(Handle, @dwPid);
//open Process
Proc := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or
PROCESS_VM_WRITE, FALSE, dwPid);
if Proc <> 0 then //=everything is fine
begin
//Allcoate Memory
Addr := VirtualAllocEx(Proc, nil, Size, MEM_COMMIT, PAGE_READWRITE);
//Result = Pointer to the allocated Address
Result := Addr;
end
else
begin
//result = 0 if something went wrong
Result := 0;
end;
end
else
begin
Result := 0;
end;
end;

//Writes an AnsiString into allocated Memory
function WriteStringToWinamp(Handle: THandle; Msg: AnsiString; Addr: Pointer;
Size: Integer): Integer;
var
dWinamp: DWORD;
tmpHandle: THandle;
num: Cardinal;
begin
//check Handle
if Handle <> 0 then
begin
//Get Process ID
GetWindowThreadProcessId(Handle, @dWinamp);
//Open Process for Writing
tmpHandle := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE, FALSE,
dWinamp);
if tmpHandle <> 0 then
begin
//Write AnsiString into Winamps Memory Space
WriteProcessmemory(tmpHandle, Addr, PAnsiChar(Msg), Size, num);
end;
end;
//Result = Bytes written
Result := num;
end;

//Frees allocated Memory
procedure FreeMem(Handle: THandle; where: Pointer);
var
dwPid: DWORD;
Proc: THandle;
Addr: Pointer;
begin
//check Handle
if Handle <> 0 then
begin
//get Process ID
GetWindowThreadProcessId(Handle, @dwPid);
//open Process for freeing Memory Allocation
Proc := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or
PROCESS_VM_WRITE, FALSE, dwPid);
//Free Memory
VirtualFreeEx(dwPid, where, 0, MEM_RELEASE);
end;
end;



Usage:
code:

procedure TForm2.Button2Click(Sender: TObject);
var
hwndWinamp : THandle;
hwndMediaLibrary : THandle;
WM_ML_IPC : Integer;
wa_new_ipc_command : DWORD;
addressPointer : Pointer;
Text : AnsiString;
BytesWritten : Integer;
begin
//What we want to write?
Text := 'LibraryGetWnd';

//Get Winamp Handle
hwndWinamp := FindWindow('Winamp v1.x', nil);

//Allocate Memory and get Address
addressPointer := AllocateMem(hwndWinamp, Length(Text));

//Write AnsiString and save number of bytes written
BytesWritten := WriteStringToWinamp(hwndWinamp, Text, addressPointer, Length(Text)+1);

//Register new IPC Message
wa_new_ipc_command := SendMessage(hwndWinamp, WM_USER, wParam(addressPointer), IPC_REGISTER_WINAMP_IPCMESSAGE);

//Free Memory
FreeMem(hwndWinamp, addressPointer);

//Get Handle to Winamp's Media Library
hwndMediaLibrary := SendMessage(hwndWinamp, WM_USER, 0, lParam(wa_new_ipc_command));

ListBox1.Items.Add(inttostr(hwndMediaLibrary));
end;



Greets
nithdi is offline   Reply With Quote
Old 20th September 2010, 09:13   #10
DrO
 
Join Date: Sep 2003
Posts: 27,873
that's good to know you got it working now

-daz
DrO is offline   Reply With Quote
Old 20th September 2010, 19:12   #11
nithdi
Junior Member
 
Join Date: Sep 2010
Posts: 7
Yes

Thanks for your help again!!!
nithdi is offline   Reply With Quote
Reply
Go Back   Winamp & Shoutcast Forums > Developer Center > Winamp Development

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump