How to create lnk files (icon shortcuts)

Just starting out? Need help? Post your questions and find answers here.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by neuro.


Hi to All

I need an urgent help. I would like to create an Icon on the desktop from an application. I have the API functions but I can not really use them . If anybody has any idea how to create an icon shortcut(a lnk file) from an appliation I would be very happay. It is urgent.

Bye

Neuro
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by TheBeck.

I am at work so I can't try this for you but it seems a link is a non-text binary file. Go into it with a hex editor and find the part that contains the link text. Copy all the binary data before the link text into one file and all the binary data after the link text to a second file. Now in you program first write the first binary data to disk then whatever link text and then the second binary data to disk and you just might have yourself a link. Their is most likely a much easier way such as an API call but you said you need urgent help and this is all I could come up with.

Nathan Beckstrand - Calling from the great Pacific Northwest -=USA=-
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by TheBeck.

/* link95.c

Following DOS 16 bit code more or less parses a *.lnk
file as generated by Win9x. I post it cause someone
asked about the format of *.lnk files, and I haven't
seen anything about this anywhere before. Likely someone
has done a more complete job and I just don't know about
it! Please let me know if this is useful, or you know
the complete solution.
[url]mailto:w_kranz@conknet.com[/url]

2/24/99 revisite, rename previous version link95a.c
Note see win95\resource.lzh:shortcut.exe, got somewhere
(probably Microsoft) dumps shortcuts

16 bit code for parsing win95 *.lnk files
Used MSC 6.0 (ie WORD => unsigned short => 2 bytes)

Much of data in file still unknown, of initial 0x4C bytes in
*.lnk file, much is constant but there is variability from
one file to the next. Can parse to the descriptive strings
at the end of the file with following:

On my machine, a dump at the beginning of the file always looks
the same up through offset 0x13:
00000: 4C 00 00 00 01 14 02 00 00 00 00 00 C0 00 00 00
00010: 00 00 00 46
Next byte in file varies, probably a bitmap per below.

Following the header at offset 0x4C in file is a two byte word
which is the length of 1st variable length block.
This length is # bytes to skip to reach next block or 0.
If zero block is empty? Skip bytes read to next length.
The block length includes the two bytes in the length word!

CAUTION, following seems to be true but always?
There are 3 such blocks (including zeros for empty blocks).

Then a string space starts (its organized slightly differently).
Each two byte (word) length is the number of bytes in string
which follows the word. There are no NUL terminators, if
desired you must add them. It appears this region is always
terminated by two words, both equal to zero. I think one
could read till found first string block of zero length.
This is the end of the useable data in the file.

The byte at offset 0x14 in file seems to identify
the individual blocks and strings included in file.
I define the following bitmaps, each bit indicates
a different function and associated block or string.

Look at low nibble for blocks of data as follows (probably):
1 => ? occurs but meaning escapes me, see com114~1.lnk
2 => target program, see compus~1.lnk with above
4 => ? have never even seen this
8 => path componets, see notepad.lnk & cdplay~1.lnk
note also forces an extra string!

In addition to bit 4 (mask value 8) of low nibble
Look at the high nibble for strings. It is a bitmap
of which strings are expected in the order shown below.
looking at magic # byte at 0x14

0x10 => working directory
0x20 => arguments for program
0x40 => icon file name
per above if low nibble & 8, 1st string is
The path to *.exe relative to current location of *.lnk

One expects any combination of bits above with 1 to 4
strings possible.
Interesting in that strings do not appear to be in unicode!

Now it gets pretty vague. I don't fully understand
the various blocks, and almost nothing in the header.
Not clear why strings should be arranged differently than
data blocks. In particular why do block length differently
and why have empty block if bit map tells us which ones
are there? The bit map concept is just a guess for the
data blocks, but seems to work for the strings so logical.
Maybe one is supposed to use bitmap info also, only thing
I've done for verification is:

If ( (low nibble of byte at 0x14) & 0x2)
shortcut.exe says there is a target, otherwise none

In general each block is made up from a number of sub
blocks. Each two byte word at the begining of a block defines
the sub block length. The blocks do NOT seem to occur in order
of their bit map value, target type 2 always last:
Type description
8 1st sub block is 0xE unknown bytes
Each additional sub block is one level in path spec.
ie 2nd is drive, remainder are nodes in path
or file name at end of block. Per above if this
exists, 1st string below seems to be relative path to
program from location of *.lnk file.

2 This contains the full path to the target program.
Starts with 0x2D unknown bytes, followed by a
null terminated path to the program at offset 0x2d.
WARNING in general above is true, but a couple of my
shortcuts just had "C:\" at this offset,
then another sub block, then the rest of the path
in a third sub block.
This occurs if the fifth word in block is 0x3
rather than 0x1. Seems to give local path and
network path. See code below.

I'd like this to be a little cleaner, I don't love Microsoft,
but to have two different methods of storing data in the same
little file seems odd. Likely I overcomplicated things somewhere,
but don't see it. If you do let me know!
*/

#include
#include
#include
#include

#define BUFSZ 512 // global working buffer in case needs to be big
char buf[BUFSZ];
#define HEADSZ 0x4c // don't know what is in here! only identified two bytes

main(int argc, char *argv[])
{
unsigned char tndx,tmask,head[HEADSZ];
long flen,len=0L;
char *type,*str;
int i,fp,rd,cnt=0,scnt=0;
unsigned *uptr,sz=0x4c; /* fixed size of initial region */
if(argc \ndisplays link info");
exit (1);
}
if((fp = open(argv[1],O_RDONLY|O_BINARY)) == EOF)
{
printf("\nfailed to open: %s",argv[1]);
exit(1);
}
else if (read(fp,head,HEADSZ) != HEADSZ)
{
printf("\nfailed to read a header of length %x (hex) bytes)",
HEADSZ);
exit(1);
}
len = HEADSZ;
flen = filelength(fp);
while(cnt = 0)
{
if(rd == 0)
len+= sizeof(unsigned); // empty block
else
len += rd; // size of block, includes unsigned
str = "None";
type = NULL;
switch(++cnt)
{
/* hopefully I'll know what other blocks are some day...
my experience has been that if there is a 3rd data block
which isn't empty, its the Target block associated
with bit 0x2. Maybe there is an id embedded in the
blocks, but I don't see it...
*/
case 3:
uptr = (unsigned *) &buf[0];
// should I be checking for an 0x2 in bitmap?
type = "Target";
if(rd > 0x2d) // 0x2d == *(uptr+6)
str = buf+0x2d; // display absolute path string
// above works for me but could be variable offset ie
*(uptr+6)
printf("\n%s: %s",type,str);
// CAUTION not always as simple as above
if(*(uptr+4) >= 3 && *(uptr+12) > 4; // high nibble of byte
// check all three possible states
for(tndx = 1;len msz)
return(-2);
sz = *uptr - sizeof(unsigned); // additional to read in
if(sz > 0 && read(fp,buf+sizeof(unsigned),sz) != sz)
return(-1);
else
return(*uptr); // bytes read
}

/* file ends with a series of strings, logic is similar to
above, but length is # chars in string, and they aren't nul
terminated, I append a NUL to make printing easier if it fits
Note I'm NOT putting len in buffer this time, want a string
*/
read_string(int fp,unsigned char *buf,unsigned msz)
{
int rd,sz;
if(read(fp,&sz,sizeof(unsigned)) != sizeof(unsigned))
return(-1);
else if(sz > msz)
return(-2);
if(sz > 0 && read(fp,buf,sz) != sz)
return(-1);
else
{
if(sz >= 0 && sz < msz)
buf[sz] = 0;
return(sz); // bytes read into string buffer, ie its length
}
}
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by TheBeck.

Here is some more info on links from MSDN:

http://msdn.microsoft.com/library/defau ... ortcut.asp

Sounds links are pretty complex and you need to use API to access them...
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ricardo.

This GREAT code is not mine, its Danilo's but i have it in my snippets.
It let you create links anywhere!

Code: Select all

Procedure CreateLink(PATH$, LINK$, Argument$, DESCRIPTION$, WorkingDirectory$, ShowCommand.l, HotKey.l, IconFile$, IconIndexInFile.l)
   ;
   ; IShellLInk CONSTANTS for CallCOM
   ;
   #ShellLink_QueryInterface        =  0
   #ShellLink_AddRef                =  4
   #ShellLink_Release               =  8
   #ShellLink_GetPath               =  12
   #ShellLink_GetIDList             =  16
   #ShellLink_SetIDList             =  20
   #ShellLink_GetDescription        =  24
   #ShellLink_SetDescription        =  28
   #ShellLink_GetWorkingDirectory   =  32
   #ShellLink_SetWorkingDirectory   =  36
   #ShellLink_GetArguments          =  40
   #ShellLink_SetArguments          =  44
   #ShellLink_GetHotkey             =  48
   #ShellLink_SetHotkey             =  52
   #ShellLink_GetShowCmd            =  56
   #ShellLink_SetShowCmd            =  60
   #ShellLink_GetIconLocation       =  64
   #ShellLink_SetIconLocation       =  68
   #ShellLink_SetRelativePath       =  72
   #ShellLink_Resolve               =  76
   #ShellLink_SetPath               =  80
   
   ;
   ; Interface IPersistFile : IPersist
   ;
   #PersistFile_Release             = 8
   #PersistFile_IsDirty             = 16
   #PersistFile_Load                = 20
   #PersistFile_Save                = 24
   #PersistFile_SaveCompleted       = 28
   #PersistFile_GetCurFile          = 32
   
   result = 0
   CoInitialize_(0)
   If CoCreateInstance_(?CLSID_ShellLink,0,1,?IID_IShellLink,@psl) = 0
      
   Set_ShellLink_preferences:
      
      ; The file TO which is linked ( = target for the Link )
      ;
      CallCOM(#ShellLink_SetPath, psl, PATH$)
      
      ; Arguments for the Target
      ;
      CallCOM(#ShellLink_SetArguments, psl, Argument$)
      
      ; Working Directory
      ;
      CallCOM(#ShellLink_SetWorkingDirectory, psl, WorkingDirectory$)
      
      ; Description ( also used as Tooltip for the Link )
      ; 
      CallCOM(#ShellLink_SetDescription, psl, DESCRIPTION$)
      
      ; Show command:
      ;               SW_SHOWNORMAL    = Default
      ;               SW_SHOWMAXIMIZED = aehmm... Maximized
      ;               SW_SHOWMINIMIZED = play Unreal Tournament
      CallCOM(#ShellLink_SetShowCmd, psl, ShowCommand)
      
      ; Hotkey:
      ; The virtual key code is in the low-order byte,
      ; and the modifier flags are in the high-order byte.
      ; The modifier flags can be a combination of the following values:
      ;
      ;         HOTKEYF_ALT     = ALT key
      ;         HOTKEYF_CONTROL = CTRL key
      ;         HOTKEYF_EXT     = Extended key
      ;         HOTKEYF_SHIFT   = SHIFT key
      ; 
      CallCOM(#ShellLink_SetHotkey, psl, HotKey)
      
      ; Set Icon for the Link:
      ; There can be more than 1 icons in an icon resource file,
      ; so you have to specify the index.
      ;
      CallCOM(#ShellLink_SetIconLocation, psl, IconFile$, IconIndexInFile)
      
      
   ShellLink_SAVE:
      ; Query IShellLink For the IPersistFile interface For saving the 
      ; shortcut in persistent storage. 
      If CallCOM(#ShellLink_QueryInterface,psl,?IID_IPersistFile, @ppf) = 0 
             ; Ensure that the string is Unicode.
             mem.s = Space(1000) ;AllocateMemory(1,1000)
             MultiByteToWideChar_(#CP_ACP, 0, LINK$, -1, mem, 1000)
             ;Save the link by calling IPersistFile::Save. 
             ;hres = ppf->Save(wsz, TRUE); 
             hres = CallCOM(#PersistFile_SAVE,ppf,mem,#TRUE)
             result = 1
             CallCOM(#PersistFile_Release,ppf)
             ;FreeMemory_()
      EndIf 
      CallCOM(#ShellLink_Release,psl) 
   EndIf
   CoUninitialize_()
   ProcedureReturn result
   
   DataSection
     CLSID_ShellLink:
       ; 00021401-0000-0000-C000-000000000046
       Data.l $00021401
       Data.w $0000,$0000
       Data.b $C0,$00,$00,$00,$00,$00,$00,$46
     IID_IShellLink:
       ; DEFINE_SHLGUID(IID_IShellLinkA,         0x000214EEL, 0, 0);
       ; C000-000000000046
       Data.l $000214EE
       Data.w $0000,$0000
       Data.b $C0,$00,$00,$00,$00,$00,$00,$46
     IID_IPersistFile:
       ; 0000010b-0000-0000-C000-000000000046
       Data.l $0000010b
       Data.w $0000,$0000
       Data.b $C0,$00,$00,$00,$00,$00,$00,$46
   EndDataSection
   
EndProcedure
 
 
 
; CreateLink
;             - TARGET$ for the Link ("c:\PureBasic\purebasic.exe")
;             - LINK$ - name of the Link ("c:\pb.lnk")
;             - Argument$ for the target  ("%1")
;             - Description$ = Description and Tooltip ("Start PureBasic")
;             - Working Directory ("c:\PureBasic\")
;             - Show command: #SW_SHOWNORMAL or #SW_SHOWMAXIMIZED or #SW_SHOWMINIMIZED
;             - HotKey - no need to use this :)
;             - IconFile + Index ( "c:\PureBasic\purebasic.exe" , 1 )
 
 
If CreateLink("D:\BASIC\PureBasic\purebasic.exe","c:\PB.lnk","","Pure FUN","D:\BASIC\PureBasic\",#SW_SHOWMAXIMIZED,0,"%SystemRoot%\system32\SHELL32.dll",12)
  beep_(800,100)
EndIf
  
WinDir$ = Space(100): GetSystemDirectory_(WinDir$,100)
If CreateLink(WinDir$+"\calc.exe","c:\CALC.lnk","","Calculator","",0,0,"%SystemRoot%\system32\SHELL32.dll",23)
  beep_(800,100)
EndIf
Best Regards

Ricardo

Dont cry for me Argentina...
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by TheBeck.

Wow, all that just to make a link? I thought it would've been an API call or two.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Danilo.

TheBeck, most of my code are constant definitions and
comments which dont get included into to .EXE.
And it uses OOP interface. At the end you only call
the function CreateLink() which should be easy. :)

cya,
...Danilo
(registered PureBasic user)
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by PB.

> If anybody has any idea how to create an icon shortcut(a lnk file)
> from an appliation I would be very happay.

Look for 'How do I create a shortcut to a file' at this post:

viewtopic.php?t=4876
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by neuro.

Hi

Thanks for your help. I will try the code.

bye

neuro
Post Reply