桌面快捷方式的位置存储在哪里?

Where are positions of desktop shortcuts stored?

Windows 桌面快捷方式的位置存储在哪里?

我问的是图标的屏幕位置,而不是实际图标本身。我知道图标本身存储在各种 DLL、EXE 等中。这些位置显然存储在一些非易失性存储中,因为它们通过重新启动持续存在。

我的最终目标是编写一个应用程序来显示,并可选择在我的桌面上重新排列图标。
我知道这是可能的,因为许多可用的应用程序都可以这样做(例如,"WinTidy")。

我发现很多关于 "Windows Shell Bags" 的讨论。 http://williballethin.com.forensics/shellbags 中有一篇关于这些的有趣文章,但它只涉及 目录 而不是快捷方式。这些在不同地方的注册表中,包括

`HKEY_CURRENT_USER/Software/Microsoft/Windows/Shell/Bags/1/Desktop`  
`HKEY_USERS/.DEFAULT/Software/Microsoft/Windows/Shell/Bags/1/Desktop`  

我写了一个程序来提取这些,但是键值的格式无法理解。

有人知道它们存放在哪里以及如何存放吗?

20 年 6 月 3 日更新
++++++++++++++++++++++++++++++++++++++++
我刚换了一台Win10 64位的机器,发现下面的解决方法已经不行了。我相信是因为桌面内部结构发生了变化。我想出了如何做到这一点。请参阅此答案末尾的“WIN10 ADDENDUM”。
++++++++++++++++++++++++++++++++++++++++

我终于想出了如何做我想做的事(显示和重新排列桌面图标)。我最初的问题是关于定位、读取和写入存储图标信息的文件,但这是不是有用的方法。这是我学到的:

Explorer.exe 在覆盖整个桌面的巨大 ListView 中显示桌面项目,ListView 项目对应于每个可见图标。在启动时,Explorer 从一些神秘的文件中读取信息并填充 ListView。退出时,它会从 ListView 重新写入该文件。所以修改文件 没有 帮助,因为它会在退出时被覆盖。

操作桌面项的正确方法是直接操作ListView中的项。任何更改在更改时立即可见,并在退出时保存。要访问项目,我们可以使用几个 Windows 消息:LVM_GETITEM、LVM_GETITEMCOUNT、LVM_GETITEMPOSITION 和 LVM_SETITEMPOSITION。这些消息使用起来相当简单,但有一个复杂之处:有些需要指向参数结构的指针。这些结构必须在 Explorer 的 地址 space 而不是 我的应用程序的地址中,因此需要一些技巧。这是如何做的。我以LVM_GETITEMPOSITION为例,它需要一个指向POINT结构的指针。

  • 在您的应用中声明一个 POINT 结构。

  • 使用APIVirtualAllocEx()Explorer地址space分配一个镜像结构。

  • 将 LVM_GETITEMPOSITION 发送到资源管理器,指定指向此结构的指针。

  • 使用 APIReadProcessMemory() 将结果读回您的应用程序的 POINT。这个函数可以跨不同地址读取内存 spaces.

我已经对这些操作进行了原型设计,它们按我的意愿工作。我的代码很长,但我会在整理后立即post摘录。

2019 年 10 月 4 日更新 ----------------------------------

代码摘录

创建了一组常用的实用函数,使代码更加紧凑和可读。这些被命名为“exp*()”并包含在末尾。可以在 http://ramrodtechnology.com/explorer. Much of the basic technique herein was shamelessly stolen from https://www.codeproject.com/Articles/5570/Stealing-Program-s-Memory

找到参考

设置

// COMMONLY USED VARS
HANDLE hProcess;        // explorer process handle
HWND hWndLV;        // explorer main window

// SET UP CONVENIENCE VARS
hProcess = expGetProcessHandle();   // get explorer process handle
if( !hProcess ) exit( 1 );
hWndLV = expGetListView();      // get main ListView of Desktop
if( !hWndLV   ) exit( 1 );

打印所有项目名称的函数

//@ Process a list view window and print item names
int
printAllNames()
{
    int ok,icount,indx;
    LVITEM item;                    // point in app space
    LVITEM *_pitem;                 // point in exp space
    char text[512];
    char *_ptext;
    int nr,nwrite;              // count of bytes read/written
    
    printf( "\n" );

    // ALLOC ITEMS IN EXP SPACE
    _pitem = expAlloc( sizeof(LVITEM) );
    _ptext = expAlloc( sizeof(text  ) );


    printf( "  NAME\n" );
    printf( "  ==================================\n" );
    icount = expGetItemCount();
    for( indx=0; indx<icount; indx++ ) {            // for each item in LV

      // SETUP ITEM IN EXP SPACE
      memset( &item, 0, sizeof(LVITEM) );   // preclear
      item.iItem      = indx;       // index of item to read
      item.iSubItem   = 0;          // sub index (always 0)
      item.mask       = LVIF_TEXT;      // component to read
      item.pszText    = _ptext;     // buffer to recv text
      item.cchTextMax = sizeof(text);   // size of buffer

      // WRITE ITEM REQ TO EXP SPACE
      ok = WriteProcessMemory( hProcess, _pitem, &item, sizeof(LVITEM), &nwrite );

      // SEND MESSAGE TO GET ITEM INTO EXP SPACE
      ok = SendMessage( hWndLV, LVM_GETITEM, indx, (LPARAM)_pitem );

      // READ EXP TEXT INTO APP SPACE
      memset( &item, 0, sizeof(LVITEM) );
      ok = ReadProcessMemory( hProcess, _pitem, &item, sizeof(POINT), &nr );
      ok = ReadProcessMemory( hProcess, _ptext, &text, sizeof(text),  &nr );

      // PRINT RESULT
      printf( "  %s\n", text );
    }
    ok = expFree( _pitem );
    ok = expFree( _ptext );

    return( TRUE );
    //r Returns TRUE on success, FALSE on error
}  

打印所有项目位置的函数

//@ Process a list view window and print position
int
printAllPositions()
{
    int ok,icount,indx,nr;
    POINT pt;                   // point in app space
    POINT *_ppt;                    // point in exp space
    
    icount = expGetItemCount();

    _ppt = expAlloc( sizeof(POINT) );
    if( !_ppt ) return( FALSE );

    printf( "   X    Y\n" );
    printf( "---- ----\n" );
    for( indx=0; indx<icount; indx++ ) {        // for each item in LV
      ok = SendMessage( hWndLV, LVM_GETITEMPOSITION, indx, (LPARAM)_ppt );
      ok = ReadProcessMemory( hProcess, _ppt, &pt, sizeof(POINT), &nr );
      printf( "%4d %4d\n", pt.x, pt.y );
    }

    ok = expFree( _ppt );

    return( TRUE );
    //r Returns TRUE on success
}

移动项目的功能
请参阅下面的 'expSetItemPosition'。 19 年 10 月 6 日更新

Explorer 实用函数

// EXPLORER UTILITY FUNCTIONS

//@ Allocate a block of memory in explorer space
void *
expAlloc(
  int size)     // size of block
{
    void *p;

    p = VirtualAllocEx( 
        hProcess,
        NULL,
        size,
        MEM_COMMIT, 
        PAGE_READWRITE );
    return( p );
    //r Returns addr of memory in EXPLORER space or NULL on error
}

//@ Free virtual memory in EXPLORER space
int
expFree(
  void *p)  // pointer to free
{
    int ok;
    ok = VirtualFreeEx( hProcess, p, 0, MEM_RELEASE );
    return( ok );
    //r Returns TRUE on success, else FALSE
}

static int  aBiggest;       // biggest area so far
static HWND hWndBiggest;    // hWnd with biggest area

//@ Find main list view of explorer
HWND
expGetListView()
{
    //n Approach: Enumerate all child windows of desktop and find largest.
    //n This will be the main explorer window.

    HWND hWndDesktop;
    hWndDesktop = GetDesktopWindow();
    if( !hWndDesktop ) return( NULL );

    aBiggest    = -1;       // init
    hWndBiggest = NULL;     // init
    EnumChildWindows( hWndDesktop, CallbackDesktopChild, 0 );
    
    return( hWndBiggest );
    //r Returns hWnd of largest explorer list view
}

//@ Callback for EnumChildWindows
BOOL CALLBACK CallbackDesktopChild(
  HWND hWnd,
  LPARAM dwUser)
{
    //n Get size of child. If biggest, save hWnd.

    int i,w,h,a;
    char classname[MAXPATH+1];
    RECT rect;

    i = GetClassName( hWnd, classname, MAXPATH );   // get class
    if( stricmp( classname, "SysListView32" ) ) {   // not a list view?
      return( TRUE );               // skip it
    }

    // CALC SIZE
    i = GetWindowRect( hWnd, &rect );
    w = rect.right - rect.left;
    h = rect.bottom - rect.top;

    // CHECK IF BIGGEST
    a = w * h;
    if( a > aBiggest ) {    // is biggest?
      aBiggest    = a;
      hWndBiggest = hWnd;
    }

    return( TRUE );     // TRUE to continue enumeration
}


//@ Get process handle of explorer.exe
HANDLE
expGetProcessHandle()
{
    //n Approach: take process snapshot and loop through to find "explorer.exe"
    //n Needs tlhelp32.h and comctl32.lib
    
    int i,stat;
    PROCESSENTRY32 pe;
    HANDLE hSnapshot;
    char *name;
    HANDLE h;

    // TAKE A SNAPSHOT
    hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    if( !hSnapshot ) return( NULL );

    // LOOP THROUGH PROCESSES AND FIND "explorer.exe"
    for( i=0;;i++ ) {
      pe.dwSize = sizeof( PROCESSENTRY32 );
      if( i == 0 ) stat = Process32First( hSnapshot, &pe );
      else         stat = Process32Next ( hSnapshot, &pe );
      if( !stat ) break;                // done or error?
      name = pe.szExeFile;
      if( !stricmp( name, "explorer.exe" ) ) {  // matches?
        h = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID ); 
        return( h );
      }
    }

    return( NULL );
    //r Returns explorer process handle or NULL on error
}

//@ Get count of items in explorer list view
int
expGetItemCount()
{
    int count;

    count = SendMessage( hWndLV, LVM_GETITEMCOUNT, 0, 0 );
    return( count );
    //r Returns count of item
}

//@ Get position of list view icon by index
int
expGetItemPosition(
  int indx, // index of item
  int *x,   // ptr to int to recv x
  int *y)   // ptr to int to recv y
{
    int i,ok,icount;
    char classname[MAXPATH+1];
    POINT pt;                   // point in app space
    POINT *_ppt;                    // point in exp space
    int nr;                     // count of bytes read
    //int w,h;

    i = GetClassName( hWndLV, classname, MAXPATH );

    // GET COUNT OF ITEMS IN LIST VIEW
    icount = expGetItemCount();
    if( indx < 0 || indx >= icount ) return( FALSE );

    // ALLOC POINT IN EXP SPACE
    _ppt = expAlloc( sizeof(POINT) );
    if( !_ppt ) return( FALSE );

    // SEND MESSAGE TO GET POS INTO EXP SPACE POINT
    ok = SendMessage( hWndLV, LVM_GETITEMPOSITION, indx, (LPARAM)_ppt );
    if( !ok ) return( FALSE );

    // READ EXP SPACE POINT INTO APP SPACE POINT
    ok = ReadProcessMemory( hProcess, _ppt, &pt, sizeof(POINT), &nr );
    if( !ok ) return( FALSE );

    ok = expFree( _ppt );
    if( !ok ) return( FALSE );

    if( x ) *x = pt.x;
    if( y ) *y = pt.y;

    //r Returns TRUE on success
    return( TRUE );  
}  

//@ Move item
int
expSetItemPosition(
  char *name,   // icon name
  int x,        // new x coord
  int y)        // new y coord
{
    int ok,indx;
    LPARAM lParam;

    indx = expGetItemIndex( name );
    if( indx < 0 ) return( FALSE );

    lParam = MAKELPARAM( x, y );
    ok = SendMessage( hWndLV, LVM_SETITEMPOSITION, indx, lParam );
    if( !ok ) return( FALSE );

    return( TRUE );
    //r Returns TRUE on success
}

WIN10 附录 20 年 6 月 19 日
++++++++++++++++++++++++++++++++++++

在Win10下,解决方法就复杂多了。您必须使用各种 COM 对象和接口,例如IShellWindows,等等(上帝,我讨厌 COM)。我没有创建库,而是在下面提供了一个完整的工作程序。我使用 MSVC 2019 编译了它。为清楚起见,省略了错误检查(但你应该这样做)。

//  icons.cpp - Display (and optionally move) desktop icons

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <ShlObj.h>
#include <atlbase.h>

int
main(int argc,char** argv)
{
    CComPtr<IShellWindows> spShellWindows;
    CComPtr<IShellBrowser> spBrowser;
    CComPtr<IDispatch> spDispatch;
    CComPtr<IShellView> spShellView;
    CComPtr<IFolderView>  spView;
    CComPtr<IShellFolder> spFolder;
    CComPtr<IEnumIDList>  spEnum;
    CComHeapPtr<ITEMID_CHILD> spidl;
    CComVariant vtLoc(CLSID_ShellWindows);
    CComVariant vtEmpty;
    STRRET str;

    int count=0;
    HRESULT hr;
    long lhWnd;

    // INITIALIZE COM
    CoInitialize(NULL);
    
    // GET ShellWindows INTERFACE
    hr = spShellWindows.CoCreateInstance(CLSID_ShellWindows);

    // FIND WINDOW
    hr = spShellWindows->FindWindowSW(
        &vtLoc, &vtEmpty, SWC_DESKTOP, &lhWnd, SWFO_NEEDDISPATCH, &spDispatch);

    // GET DISPATCH INTERFACE
    CComQIPtr<IServiceProvider>(spDispatch)->
      QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));

    spBrowser->QueryActiveShellView(&spShellView);
    spShellView->QueryInterface(IID_PPV_ARGS(&spView) );

    hr = spView->GetFolder(IID_PPV_ARGS(&spFolder));

    // GET ENUMERATOR
    spView->Items(SVGIO_ALLVIEW, IID_PPV_ARGS(&spEnum));    // get enumerator

    // ENUMERATE ALL DESKTOP ITEMS
    for (; spEnum->Next(1, &spidl, nullptr) == S_OK; spidl.Free()) {
      // GET/PRINT ICON NAME AND POSITION
      char* name;
      POINT pt;
      spFolder->GetDisplayNameOf(spidl, SHGDN_NORMAL, &str);
      StrRetToStr(&str, spidl, &name);
      spView->GetItemPosition(spidl, &pt);
      printf("%5d %5d \"%s\"\n", pt.x, pt.y, name);

#define MOVE_ICON
#ifdef MOVE_ICON
      // OPTIONAL: MOVE *SINGLE* SELECTED ITEM
      {
        if( !_stricmp(name, "ICON_NAME_TO_MOVE") ) {
        PCITEMID_CHILD apidl[1] = { spidl };
        int numitems = 1;
        // SET pt TO NEW POSITION HERE
        hr = spView->SelectAndPositionItems(numitems, apidl, &pt, 0);
        }
      }
#endif

      count++;
    }
    CoUninitialize();           // release COM

    fprintf(stderr, "enumerated %d desktop icons\n", count);
    fprintf(stderr, "Press any key to exit...\n");
    _getch();
    exit(0 );
}