Link to home
Start Free TrialLog in
Avatar of ivan_llanas
ivan_llanas

asked on

Desktop icons' layout location.


   Does anybody know where the Desktop icons' layout is saved/placed? If this layout is nowhere (Explorer memory), how can I get/set the icons' layout?

   I need to do all this programatically by myself. I cannot use EzDesk, *.reg files or any other software.

   Thanks in advance.
Avatar of DaFox
DaFox

Hi Ivan.

One of madshi's components are able to do this, but, as you mentioned, you don't want to use third party components...
Jeffrey Richter once showed in his book how to do this. His code example is called "DIPS". There are also some Delphi versions available, I just can't find them atm. One of them is called "NicoDIPS" by Nico Bendlin (1:1 translated from Jeffrey Richter, I guess).
I'll try to find one of these files tomorrow, if you're interested.

Markus
Avatar of ivan_llanas

ASKER


  Ups! Sorry, I didn't make it clear enough with "any other software"; of course a Delphi component (with source, of course) will be a valid solution as long as I can read the code and see how it is done (or even use the component if I like it).

   By the way I'm interested in anything that could give me a clue in this affair, thanks a lot!
Thanks Markus for refering to me...  :-)

Ivan, please have a look here:

http://help.madshi.net/ShellObjs.htm#Desktop

madShell is free for non-commercial usage (only). There's one demo contained which does exactly what you're looking for, namely saving and restoring the desktop's icon positions. In contrast to Jeffrey Richter's code my stuff even works without needing any self written dlls. Please use the latest beta version, only that contains the demo I was talking about:

http://madshi.bei.t-online.de/madCollectionBeta.exe

Regards, Madshi.

   Yes, that's what I need, but I need the source (at least) to get/set the position of each icon (methods Items[i].Position and Items[i]).SetPosition).
   I already have an Enumeration method (IShellFolder.EnumObjects and IEnumIdList.Next). I use PItemIDList instead of IIDList (never heard of it :P).

   The thing is: I have a list of PItemIDList's, and I need to get/set their positions. I can increase the amount of points if you consider the info worth of it...

   One more thing. The demo provided does not work with the system-icons. Looking at the desktop.txt file, I realized that these icons doesn't even figure there.
   
   I would like to talk with you about your madKernel package; I'm interested in discussing some processes-accessing methods, if you don't mind.
   
   All my programs are 100% freeware and recycled-bytes-made ;) . You can check it at www.geocities.com/ivan_llanas .

   Thanks.
   
Hi ivan,

>> Yes, that's what I need, but I need the source (at least) to get/set the position of each icon (methods Items[i].Position and Items[i]).SetPosition).

Why do you need the source? I can't give away the sources of my packages for free, I'm sorry. That's much too dangerous.

>> I use PItemIDList instead of IIDList (never heard of it :P).

Well, IIDList is part of madShell, it's no Microsoft interface.

>> One more thing. The demo provided does not work with the system-icons. Looking at the desktop.txt file, I realized that these icons doesn't even figure there.

Did you use the latest beta version I linked in my previous comment or did you use the current official version? The latest beta version should include the system icons. About which OS are we talking? And which system icons do you mean in detail?

>> I would like to talk with you about your madKernel package; I'm interested in discussing some processes-accessing methods, if you don't mind.

Okay, ask.

>>Did you use the latest beta version I linked in my previous comment or did you use the current official version? The latest beta version should include the system icons. About which OS are we talking? And which system icons do you mean in detail?

   I used the link you specified in your last comment (http://madshi.bei.t-online.de/madCollectionBeta.exe). My OS is WinXP Pro (with several sys-patches). The icons are all the system ones: "My Computer" renamed as "The World", "Recycle Bin" as "Stasis Cell" ;) , "My Docs" as "Data Crystals", "Net Neigh..." as "The Galaxy"... and so on, pretty stupid, isn't it? ;)

>>Why do you need the source? I can't give away the sources of my packages for free, I'm sorry. That's much too dangerous

   Well, I don't need the whole sources, of course, just a couple of lines (I presume; I'm very brave ;). The calls I need to get/set the position from a PItemIDList.

   About the madKernel; I would like to know the way you access to the current running processes. I'm using CreateToolhelp32Snapshot and Process32First-Next. But I haven't been able to get the memory used by the process, the owner-user, the app fullpath (only in NT, Win9x gets the whole path)... Well, I get PID, PPID, Priority, Threads count, CPU time used and exe-filename (without path).

>> The icons are all the system ones: "My Computer"

Ah, I'm sorry, I just noticed that I didn't recompile the demo. It's still compiled with the last official version, not with the latest beta version. Please load that demo project in Delphi and recompile it, then it will also give you the position of those system icons.

>> Well, I don't need the whole sources, of course, just a couple of lines (I presume; I'm very brave ;). The calls I need to get/set the position from a PItemIDList.

It's more than "a couple of" lines. You didn't answer on *why* you need the sources. You can compile your programs without having the sources, or not? Anyway, as I already said, I can't the sources to you, also not just the part which deals with the desktop icon position. Sorry.

>> About the madKernel; I would like to know the way you access to the current running processes. I'm using CreateToolhelp32Snapshot and Process32First-Next.

That won't work in NT4.

>> But I haven't been able to get the memory used by the process, the owner-user, the app fullpath (only in NT, Win9x gets the whole path)

Use PsApi to get the full path. You'll find sources for that (even for Delphi) on the net.

About memory: Perhaps VirtualQueryEx helps? The owner user? Look at GetKernelObjectSecurity.

   Man you're fast!

>> You didn't answer on *why* you need the sources.

   Sorry, I didn't think this was that important. I need the sources for two reasons: I change easily from one version of Delphi to another, and dcu's are not compatibles between versions; but the most important reason is that I want to know What and How I am doing things. It's not a matter of not relying on others' work, but a feeling of curiosity and a willing of knowing (uf, my English, is not as good as I would need to say what I think :P but surely you get the point, don't you?).

>>It's more than "a couple of" lines.

   Ops, pitty. Well, can you tell me if you access to this information using the Explorer process and the listview HWND or are you using the IShellFolder and fellows' interfaces? I do not want a code; I really prefer a help or a guide to get the answer by myself than getting the code that solves my "problem".

>>That won't work in NT4.

   Sure? It works with 9x/2000/XP.

   Thanks a lot for the clues for the processes' functions... I'll investigate them very soon (if I had the time :P )... I'm developing more than 12 applications simultaneously... I'm (as my friends say) getting insane...

   Thanks again.
ASKER CERTIFIED SOLUTION
Avatar of Madshi
Madshi

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
> ... inject it into the explorer by misusing SetWindowsHookEx

Not everybody knows how to do that with CreateRemoteThread(), especially on Win9x. SetWindowsHookEx() is definitly the easiest way to do it and it works reliable, although I agree that CreateRemoteThread() is more professional ;-)

Madshi, why don't you need dlls? I didn't get it in your last comment! :-)

Markus
>> Madshi, why don't you need dlls? I didn't get it in your last comment! :-)

That's probably because I didn't say it...   =:-P
> That's probably because I didn't say it...   =:-P

How do you want to get the 300 points then? Kretzschmar is gone if you don't take your chance! ;-p
Just kidding...
In fact, this is not really important to me. I just wondered how to do this. I won't develop my own dll-less icon positioner ;-)

Markus
I won't get meikl, anyway, he's just too fast for me...   :-)

The secret is that the list view control wants to write to a buffer/pointer which is valid in its own process. So you have to give him one. But that's all I'll say about this, ok?
Sure, thanks! ;-)
If Ivan doesn't give you the points (but I don't think so), I will...

Markus

>> Okay, you can use the ListView_GetItemPosition

   All right; so it is a matter of involving the Explorer's ListView. Great. About "sending a pointer" to another process, well, there's no problem, they created the "fabulous" :P MemoryMaps or even the Read/WriteProcessMemory functions.

>>Yes, I'm sure. It works everywhere, but not in NT4. NT4 simply doesn't support the toolhelp functions.

   Ups, thanks for the info; I dind't know it. The only NT4 available to me was retired a few months ago and I cannot use one of my machines to hold a NT4 just for compatibility testing... By the other hand I used to assume that NT4 is a dead OS (at least for the desktop ;).

   Markus is right about the 300 points, but the guides you gave were enough to get a satisfactory solution, I have what I wanted; and I'm feeling really good this morning (I've had a very bad feeling about the whole world from a few days ago) and you have contributed a bit :P (moreover, I will not let those points get lost in cyber-space...) Points fly to you, MadShi.

   Of course you can add any comment to this question to make a better solution... ;)

   Here is the piece of the (test) code :

//------------------------------------------------------------------------------
procedure TForm1.Button1Click (Sender: TObject);
var ListView_WND  : HWND;
    Explorer_PID  : THandle;
    ItemInfo      : TLVITEM;
    IconPos       : TPoint;
    Buffer        : array [0..MAX_PATH] of Char;
    sh_ItemInfo   : TLVITEM;
    sh_IconPos    : TPoint;
    sh_Buffer     : array [0..MAX_PATH] of Char;
    res           : LRESULT;
    i, MaxIcon    : integer;
    BytesCount    : DWORD;
begin
   ListView_WND := GetListViewHandle;
   // Get the icons count on the desktop
   MaxIcon := SendMessage (ListView_WND, LVM_GETITEMCOUNT, 0, 0)-1;
   if MaxIcon<1 then Exit; // Nothing to do... :(
   Explorer_PID := GetExlorerPID (ListView_WND); // Get the ListView's owner PID
   for i:=0 to MaxIcon do
   begin
      // Get the icon position.
      res := SendMessage (ListView_WND, LVM_GETITEMPOSITION, i, integer(@sh_IconPos));
      if res=0 then raise Exception.Create ('Unable to get the icon position.');

      // Get the info from the "shared" memory
      if not ReadProcessMemory (Explorer_PID, @sh_IconPos, @IconPos, sizeof(TPoint), BytesCount) then
         raise Exception.Create ('ReadProcessMemory error');

      // Get the icon label.
      ItemInfo.iSubItem   := 0;
      ItemInfo.cchTextMax := MAX_PATH;
      ItemInfo.mask       := LVIF_TEXT;
      ItemInfo.pszText    := @sh_Buffer;
      if not WriteProcessMemory (Explorer_PID, @sh_ItemInfo, @ItemInfo, sizeof(TLVITEM), BytesCount) then
         raise Exception.Create ('WriteProcessMemory error');
      res := SendMessage (ListView_WND, LVM_GETITEMTEXT, i, integer(@sh_ItemInfo));
      if res<0 then raise Exception.Create ('Unable to get the icon label.');
      if not ReadProcessMemory (Explorer_PID, @sh_Buffer, @Buffer, sizeof(Buffer), BytesCount) then
         raise Exception.Create ('ReadProcessMemory error');

      // Display current icon info
      ListBox1.Items.Add (Format ('%s : %d,%d - %d', [StrPas (Buffer), IconPos.x, IconPos.y, i]));
    end;
end;
{
Note : All sh_* vars can be substituted by the * vars; the dual vars is to
       keep it clearer.
}
//------------------------------------------------------------------------------

   This procedure is to GET the positions. To set them it is the same idea but finding in a seved list the icon's caption and using the LVM_SETITEMPOSITION.

   Of course it's not 100% done by me. I tranlated a portion of a C++ source I found in the web; but I don't like it too much; dealing with Read/WriteProcessMemory must not be very good idea... But it works and this saves me to use an external-dll hook more complex to code, also.

   I wonder if this code works for a non-admin user. I'll try it when I have the time...

   Thanks.

>> Okay, you can use the ListView_GetItemPosition

   All right; so it is a matter of involving the Explorer's ListView. Great. About "sending a pointer" to another process, well, there's no problem, they created the "fabulous" :P MemoryMaps or even the Read/WriteProcessMemory functions.

>>Yes, I'm sure. It works everywhere, but not in NT4. NT4 simply doesn't support the toolhelp functions.

   Ups, thanks for the info; I dind't know it. The only NT4 available to me was retired a few months ago and I cannot use one of my machines to hold a NT4 just for compatibility testing... By the other hand I used to assume that NT4 is a dead OS (at least for the desktop ;).

   Markus is right about the 300 points, but the guides you gave were enough to get a satisfactory solution, I have what I wanted; and I'm feeling really good this morning (I've had a very bad feeling about the whole world from a few days ago) and you have contributed a bit :P (moreover, I will not let those points get lost in cyber-space...) Points fly to you, MadShi.

   Of course you can add any comment to this question to make a better solution... ;)

   Here is the piece of the (test) code :

//------------------------------------------------------------------------------
procedure TForm1.Button1Click (Sender: TObject);
var ListView_WND  : HWND;
    Explorer_PID  : THandle;
    ItemInfo      : TLVITEM;
    IconPos       : TPoint;
    Buffer        : array [0..MAX_PATH] of Char;
    sh_ItemInfo   : TLVITEM;
    sh_IconPos    : TPoint;
    sh_Buffer     : array [0..MAX_PATH] of Char;
    res           : LRESULT;
    i, MaxIcon    : integer;
    BytesCount    : DWORD;
begin
   ListView_WND := GetListViewHandle;
   // Get the icons count on the desktop
   MaxIcon := SendMessage (ListView_WND, LVM_GETITEMCOUNT, 0, 0)-1;
   if MaxIcon<1 then Exit; // Nothing to do... :(
   Explorer_PID := GetExlorerPID (ListView_WND); // Get the ListView's owner PID
   for i:=0 to MaxIcon do
   begin
      // Get the icon position.
      res := SendMessage (ListView_WND, LVM_GETITEMPOSITION, i, integer(@sh_IconPos));
      if res=0 then raise Exception.Create ('Unable to get the icon position.');

      // Get the info from the "shared" memory
      if not ReadProcessMemory (Explorer_PID, @sh_IconPos, @IconPos, sizeof(TPoint), BytesCount) then
         raise Exception.Create ('ReadProcessMemory error');

      // Get the icon label.
      ItemInfo.iSubItem   := 0;
      ItemInfo.cchTextMax := MAX_PATH;
      ItemInfo.mask       := LVIF_TEXT;
      ItemInfo.pszText    := @sh_Buffer;
      if not WriteProcessMemory (Explorer_PID, @sh_ItemInfo, @ItemInfo, sizeof(TLVITEM), BytesCount) then
         raise Exception.Create ('WriteProcessMemory error');
      res := SendMessage (ListView_WND, LVM_GETITEMTEXT, i, integer(@sh_ItemInfo));
      if res<0 then raise Exception.Create ('Unable to get the icon label.');
      if not ReadProcessMemory (Explorer_PID, @sh_Buffer, @Buffer, sizeof(Buffer), BytesCount) then
         raise Exception.Create ('ReadProcessMemory error');

      // Display current icon info
      ListBox1.Items.Add (Format ('%s : %d,%d - %d', [StrPas (Buffer), IconPos.x, IconPos.y, i]));
    end;
end;
{
Note : All sh_* vars can be substituted by the * vars; the dual vars is to
       keep it clearer.
}
//------------------------------------------------------------------------------

   This procedure is to GET the positions. To set them it is the same idea but finding in a seved list the icon's caption and using the LVM_SETITEMPOSITION.

   Of course it's not 100% done by me. I tranlated a portion of a C++ source I found in the web; but I don't like it too much; dealing with Read/WriteProcessMemory must not be very good idea... But it works and this saves me to use an external-dll hook more complex to code, also.

   I wonder if this code works for a non-admin user. I'll try it when I have the time...

   Thanks.
I gathered much information from this article. I thought I would contribute back. I believe the last code example you gave has a serious problem in it. When you call

res := SendMessage (ListView_WND, LVM_GETITEMPOSITION, i, integer(@sh_IconPos));

I believe you are making explorer.exe overwrite its own memory with the icon position. The process works, but I believe it may cause explorer to become unstable or crash. I'm a C/C++ guy and I am not totally familiar with Delphi (but I learned Pascal on Borland’s Turbo Pascal). It looks to me like you are passing a pointer to sh_IconPos. This pointer will not be valid inside the explorer.exe process space. As a result, explorer.exe could (and probably will) corrupt itself. You need to allocate memory inside explorer.exe and then pass a pointer to that. When the call to ListView_GetItemPosition (or the direct SendMessage call) is done, you can then read the point back using ReadProcessMemory. How is this feat of remote process memory allocation done? A call to the Win32 function VirtualAllocEx. This requires a more work, since you have to open the process, but I believe it is safe. Sorry Madshi, but I don’t believe in holding back information to ensure people will pay for/use my library. If you are interested in more detail, let me know.