Link to home
Start Free TrialLog in
Avatar of andla
andla

asked on

Getting text from treeview from outside

Does anybody know how to get text from a treeview control that does not belong to my process ?

When i run this code i get an access violation in comctl32.dll

Please help me.

 int wfp=(int)(hWnd=(HWND)WindowFromPoint(pt));

HTREEITEM hti = TreeView_GetRoot(hWnd);
if(hti)
{
item.mask =TVIF_TEXT;
item.cchTextMax=199;
item.pszText=new char[300];
strcpy(item.pszText,"");
item.hItem = hti;
hti=(HTREEITEM)TreeView_GetNextItem(hWnd,&item,TVGN_CARET);

TreeView_GetItem(hWnd,&item);//This macro always fails.


int x=0;//Just to stop the debugger inside brackets
x=1;

}


Regards
Andla
ASKER CERTIFIED SOLUTION
Avatar of NickRepin
NickRepin

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of andla
andla

ASKER

Thankt there are hope :-)

How would this dll look like ?
How would i use the WM_COPY ?

I'm using windows 98.

/Andla
Note I use ListView_GetItem, because it was easier to find a listview on my computer.

//----------
// Main.cpp
//----------

//cl /LD /MD hook.cpp user32.lib kernel32.lib comctl32.lib
//cl main.cpp hook.lib user32.lib

#include <windows.h>
#include <stdlib.h>
#include <iostream.h>

HWND hTV;

bool setHook(HWND hTV,HWND w);

LRESULT CALLBACK wndProc(HWND w,UINT msg,WPARAM wp,LPARAM lp)
{
   switch(msg) {
   case WM_COPYDATA:
   {
      PCOPYDATASTRUCT c=(PCOPYDATASTRUCT)lp;
      LPSTR t=new CHAR[c->cbData];
      memcpy(t,c->lpData,c->cbData);                                    
      ReplyMessage(0);
      MessageBox(0,t,"Item text",MB_OK);
      delete[] t;
      SendMessage(w,WM_CLOSE,0,0);
      break;
   }
   case WM_USER:
      setHook(hTV,w);
      break;
   case WM_CREATE:
      PostMessage(w,WM_USER,0,0);
      return 0;
   case WM_RBUTTONDOWN:
   case WM_CLOSE:
      PostQuitMessage(0);
      DestroyWindow(w);
      return 0;
   }
   return DefWindowProc(w,msg,wp,lp);
}

//int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
void main(int argc,char* argv[])
{
   if(argc<2)
   {
      cout<<"Usage: main.exe <listview handle (eg: 0x123400)>"<<endl;
      return;
   }
   hTV=HWND(strtoul(argv[1],0,0));
   if(!hTV)
   {
      cout<<"Not a window"<<endl;
      return;
   }

   WNDCLASS cls;
   memset(&cls,0,sizeof(cls));
   cls.lpfnWndProc=wndProc;
   cls.hInstance=GetModuleHandle(0);
   cls.hIcon=0;
   cls.lpszClassName="test";
   cls.hbrBackground=HBRUSH(COLOR_WINDOW+1);
   RegisterClass(&cls);
   HWND w=CreateWindowEx(0,"test","test",
      WS_POPUP|WS_VISIBLE|WS_SYSMENU|WS_CAPTION,
      5,5,200,200,0,0,cls.hInstance,0);
   MSG msg;
   while(GetMessage(&msg,0,0,0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
}

//----------
// Hook.dll
//----------
#include <windows.h>
#include <commctrl.h>

#pragma data_seg(".shared")
HHOOK hHook=0;
HWND hTV=0;
HWND hW=0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:.shared,RWS")

HINSTANCE hDll;

LRESULT CALLBACK hookProc(int code,WPARAM wParam,LPARAM lParam)
{
   char buf[120];
   LVITEM t;
   t.mask=LVIF_TEXT;
   t.pszText=buf;
   t.cchTextMax=sizeof(buf);
   t.iItem=0;
   t.iSubItem=0;
   ListView_GetItem(hTV,&t);
   COPYDATASTRUCT c={0,strlen(buf)+1,buf};
   SendMessage(hW,WM_COPYDATA,WPARAM(hTV),LPARAM(&c));
   LRESULT r=CallNextHookEx(hHook,code,wParam,lParam);
   UnhookWindowsHookEx(hHook);
   return r;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID)
{
   if(fdwReason==DLL_PROCESS_ATTACH)
      hDll=hinstDLL;
   return TRUE;
}

_declspec(dllexport) bool setHook(HWND htv,HWND w)
{
   hTV=htv;
   hW=w;
   DWORD tid=GetWindowThreadProcessId(hTV,0);
   hHook=SetWindowsHookEx(WH_GETMESSAGE,hookProc,hDll,tid);
   PostThreadMessage(tid,WM_GETTEXTLENGTH,0,0);
   return hHook!=0;
}

_declspec(dllexport) void removeHook()
{
   if(hHook) UnhookWindowsHookEx(hHook);
}
Avatar of andla

ASKER

I see that you use a library instead of a dll.

I did try to make a dll project but i don't have much experience with this.

If i'm not entirely wrong I have read that LoadLibrary mostly returns 0x10000000 witch should be normal. But i don't understand way GetLastError return 120.

When i use the GetProcAddress it return NULL and GetLastError returns 126 because it could not find the module.

I then tried to use GetModuleHandle but this functions always returns 0 and GetLastError return 126. I tried both the filename and the whole path with the filename.

I think i have to try to make a library file instead and se if i have more luck with that :-)


Regards
Andreas.
Avatar of andla

ASKER

How do i avoid this ?


Compiling...
program.cpp
Linking...
LINK : warning LNK4075: ignoring /INCREMENTAL due to /FORCE specification
AndySLib.lib(prg.obj) : warning LNK4006: "struct HWND__ *  hTV" (?hTV@@3PAUHWND__@@A) already defined in program.obj; second definition ignored
AndySLib.lib(prg.obj) : warning LNK4006: "struct HWND__ *  hTV" (?hTV@@3PAUHWND__@@A) already defined in program.obj; second definition ignored
Avatar of andla

ASKER

Oops sorry i fixed it with the extern keyword.

Avatar of andla

ASKER

I did fix so your program worked.

I can now get the text from lisview items. I did some changes and added a timer procedure so that i just could point on the window.

void CALLBACK Timer(HWND hw,UINT msg,UINT ident,DWORD time)
{
     if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
     {
     MessageBeep(0xffffffff);

     POINT pt;
     GetCursorPos( &pt );
     hTV=(HWND)WindowFromPoint(pt);
     setHook(hTV,hWnd);

     }
     
}



Thanks for your help i think i can continue now. :-)

I would be happy if you could stay in this posting if you got some time over to aid me on my way. If you want i can post my solution with treeview laiter.

Regards
Andreas.
Avatar of andla

ASKER



I can now get the text from lisview items. I did some changes and added a timer procedure so that I just could point on the window.

void CALLBACK Timer(HWND hw,UINT msg,UINT ident,DWORD time)
{
     if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
     {
     MessageBeep(0xffffffff);

     POINT pt;
     GetCursorPos( &pt );
     hTV=(HWND)WindowFromPoint(pt);
     setHook(hTV,hWnd);

     }
     
}



Thanks for your help i think i can continue now. :-)

I would be happy if you could stay in this posting if you got some time over to aid me on my way. If you want i can post my solution with treeview laiter.

Regards
Andreas.I did fix so your program worked.
>>I see that you use a library instead of a dll.
It must be a DLL and not LIB. The main program must be linked with the import library (LIB) of the DLL.

So you compile and link the DLL code, and get DLL and LIB files. Then you compile the main code and link it with LIB. No need to load library explicitly, it will be done automatically.
Avatar of andla

ASKER

Thanks :-)

Here is what i did and it worked.

I created a lib project and added the code.
I create a win32 project and added the .lib file in
the project. I added your code and did some small changes and added the timer procedure.

If you don't believe me i can send you the project :-D

From above i had some problem with dll's and thats because Microsoft for some reason forgot to add    extern "C" {}. So now i succeded with a dll (hurray). But i have trouble understand what dllimport is used for.

Regards
Andreas
The solution provided above by Nick is fantastic.  I only ran into one problem: you can't call PostThreadMessage() with the WM_GETTEXTLENGTH message; the MSDN page for WM_GETTEXTLENGTH says the message should only be sent, not posted.  On Win2k this caused the call to PostThreadMessage() to fail, so the hook procedure was never called and the messagebox never came up.

I'm not sure what the best alternative is, but calling it with WM_USER seems to work just fine.  From there, you can easily build on Nick's example code to accomplish whatever you need.
Avatar of andla

ASKER

geissomatic >>I'm glad to se something happening on old qestions even if it is one year old. What are you trying to accomplish ? I'm Curious.

Yours sincerely
Andla
WM_USER is not good, because it might cause some undesired action in the destination window procedure.

WM_NULL must be better.
Nick - Thanks!  I'll try using WM_NULL instead.

Andla - I'm working on running intensively animated graphics (i.e. hardware-accelerate stuff) as your wallpaper (behind the icons) without using overlays.  But, there's really no way to do this without recreating an articial desktop... so I need to query the desktop's listview child window for all of its icon positions, text labels, and PIDL's (which, it turns out, seem to be pointed to by the 32-bit userdata parameter for each item text).  It should be posted, with most of the source code, at http://www.nullsoft.com/free/vms/ within a week or so.  For a beta demo, go to http://www.geisswerks.com/ryan/whatsnew-graphics.html and download the 3 files in the 10/20/02 entry. (Requires Winamp 2.x)

This is great stuff!  Thanks for everyone's help!
I've read that from NickRepin :
" It doesn't work because you pass to TreeView_GetItem() a memory buffer which is in the address space of another (your) process and therefore inaccessible ".

I'm not sure to understand ...
To get a text from a window, I do that (which is working) :

char t[500];
SendMessage(H0, WM_GETTEXT, (WPARAM) 500, (LPARAM) &t);

Why that works, and not the same thing for TListView, please ?

char t[1000];
LV_ITEM p;
p.mask = LVIF_TEXT;
p.pszText = t;

for (int i = 0 ; i < ListView_GetItemCount(H0) ; i++) {
     p.iItem = i;
     p.iSubItem = 0;
     p.cchTextMax = 1000;
     SendMessage(H0, LVM_GETITEM, 0, (LPARAM) (LV_ITEM FAR *) &p);
     ListBox1->Items->Add(t);
     }

Thanks
Mathieu