?
Solved

Analysing a .LNK file...

Posted on 1999-07-21
17
Medium Priority
?
434 Views
Last Modified: 2010-04-04
Hi there!
How do I analyse a link file (.LNK)?
Suppose I want to know which is the real executable the .LNK file points to... how do I do that?
Thanks in advance!
Mauro.
0
Comment
Question by:mauromol
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
  • 3
  • +2
17 Comments
 
LVL 17

Accepted Solution

by:
inthe earned 400 total points
ID: 1390213
hi us something like so:

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure GetLinkInfo(ALnk: String;
       var AFile, APath, AWork: String);
  public
    { Public declarations }
  end;

{...}

procedure TForm1.Button1Click(Sender: TObject);
var
  s1,s2,s3:String;
begin
  GetLinkInfo('c:\windows\start menu\programs\windows explorer.lnk',
              s1,s2,s3);
  ShowMessage('File: '+s1+#13+'Target: '+s2+#13+'Working: '+s3);
end;

procedure TForm1.GetLinkInfo(ALnk: String;
                             var AFile, APath, AWork: String);
const
  // took this TGUID from OLE2.pas
  IID_IPersistFile: TGUID = (D1:$0000010B;
                             D2:$0000;
                             D3:$0000;
                             D4:($C0,$00,$00,$00,$00,$00,$00,$46));
  // Missing from Windows.pas
  MB_ERR_INVALID_CHARS = $00000008;
var
  fShellLink   : IShellLink;
  fPersistFile : IPersistFile;
  fFindData    : TWin32FindData;
  fWideBuf     : array[0..MAX_PATH] of WideChar;
  fBuf         : array[0..MAX_PATH] of Char;
  hResx        : hResult;
  WideLen      : Integer;
begin
  // get an interface pointer to the IShellLink interface
  hResx := CoCreateInstance(CLSID_ShellLink,
                            nil,
                            CLSCTX_INPROC_SERVER,
                            IID_IShellLinkA,
                            fShellLink);
  if SUCCEEDED(hResx) then
  begin
    // get an interface pointer to the IPersistFile interface
    // of IShellLink
    hResx := fShellLink.QueryInterface(IID_IPersistFile,
                                       fPersistFile);
    if SUCCEEDED(hResx) then
    begin
      // convert the link file pathname to a PWideChar
      WideLen := MultiByteToWideChar(CP_ACP,
                                     MB_ERR_INVALID_CHARS,
                                     pChar(ALnk),
                                     Length(ALnk),
                                     @fWideBuf,
                                     MAX_PATH);
      if WideLen = 0 then
      begin
        ShowMessage('MultiByteToWideChar FAILED');
        Exit;
      end;
      // terminate our widechar at the length returned
      fWideBuf[WideLen] := #0;
      // initialize the IShellLink object from its disk image
      hResx := fPersistFile.Load(@fWideBuf,
                                 STGM_READ or
                                 STGM_SHARE_DENY_NONE);
      if SUCCEEDED(hResx) then
      begin
        // get target file & path
        fShellLink.GetPath(@fBuf,
                           MAX_PATH,
                           fFindData,
                           0);
        AFile := fFindData.cFileName;
        APath := StrPas(fBuf);
        // get target directory
        fShellLink.GetWorkingDirectory(@fBuf,
                                       Max_Path);
        AWork := StrPas(fBuf);
      end;
    end;
  end;
end;


Regards Barry
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1390214
A bit more complicated than nessecary, but should work...  :-))
0
 
LVL 17

Expert Comment

by:inthe
ID: 1390215
well it dont :-(
but this one does :-)

is this one of yopur functions madshi?
i dunno where i got it but it looks
 like your code.


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls,shlobj,activex,comobj,commctrl,menus;

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }

  public
    { Public declarations }
  end;

var
  Form1: TForm1;
   NeedToUninitialize : boolean = false;
implementation

{$R *.DFM}

function LoadShellLink(shellLinkFile: string;
                       var description, linkedObj, params: string;
                       var pidl: PItemIDList; var findData: TWin32FindData;
                       var iconPath: string; var iconIndex: integer;
                       var workingDir, hotKey: string;
                       var showCmd: integer) : boolean;
var sl  : IShellLink;
    w1  : word;
    s1  : string;
    wfd : TWin32FindData;
    i1  : integer;
begin
  NeedToUninitialize:=NeedToUninitialize or succeeded(CoInitialize(nil));
  sl:=CreateComObject(CLSID_ShellLink) as IShellLink;
  result:=(sl as IPersistFile).Load(PWideChar(wideString(ShellLinkFile)),0)=S_OK;
  if not result then exit;
  SetLength(description,MAX_PATH+1);
  if sl.GetDescription(PChar(description),MAX_PATH)=NOERROR then description:=string(PChar(description))
  else                                                           description:='';
  SetLength(s1,MAX_PATH+1);
  if sl.GetPath(PChar(s1),MAX_PATH,wfd,0)=NOERROR then begin
    linkedObj:=string(PChar(s1));
    findData :=wfd;
  end else begin
    linkedObj:='';
    ZeroMemory(@findData,sizeOf(TWin32FindData));
  end;
  SetLength(params,MAX_PATH+1);
  if sl.GetArguments(PChar(params),MAX_PATH)=NOERROR then params:=string(PChar(params))
  else                                                    params:='';
  if sl.GetIDList(pidl)<>NOERROR then pidl:=nil;
  SetLength(s1,MAX_PATH+1);
  if sl.GetIconLocation(pchar(s1),MAX_PATH,i1)=NOERROR then begin
    iconPath :=string(pchar(s1));
    iconIndex:=i1;
  end else begin
    iconPath :='';
    iconIndex:=-1;
  end;
  SetLength(workingDir,MAX_PATH+1);
  if sl.GetWorkingDirectory(PChar(workingDir),MAX_PATH)=NOERROR then workingDir:=string(PChar(workingDir))
  else                                                               workingDir:='';
  sl.GetHotKey(w1);
  if w1 and (HOTKEYF_ALT     shl 8)<>0 then w1:=(w1 and (not (HOTKEYF_ALT     shl 8))) or scAlt;
  if w1 and (HOTKEYF_CONTROL shl 8)<>0 then w1:=(w1 and (not (HOTKEYF_CONTROL shl 8))) or scCtrl;
  if w1 and (HOTKEYF_SHIFT   shl 8)<>0 then w1:=(w1 and (not (HOTKEYF_SHIFT   shl 8))) or scShift;
  hotKey:=ShortCutToText(w1);
  if sl.GetShowCmd(showCmd)<>NOERROR then showCmd:=-1;
end;

procedure FreePidl(var pidl: PItemIDList);
var malloc : IMalloc;
begin
  if (pidl<>nil) and (SHGetMalloc(malloc)=NOERROR) then begin
    malloc.Free(pidl);
    pidl:=nil;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  pi:PItemIDList;
  s1,Desc,s2:String;
  icon,workdir,htkey:string;
  Data: TWin32FindData;
  i,ii : integer;
begin
  opendialog1.execute;
  loadshelllink(opendialog1.filename,desc,s1,s2,pi,Data,icon,i,workdir,htkey,ii);
  ShowMessage('File ShortCut : '+opendialog1.filename+#13#10+'File TruePath : '+s1+#13#10+'Working Dir : '+workdir+#13#10
              +'HotKey : '+htkey+#13#10+'Show Command : '+inttostr(ii));
 FreePidl(pi);
end;


initialization
finalization
  if NeedToUninitialize then CoUninitialize;

end.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 20

Expert Comment

by:Madshi
ID: 1390216
:-))

Yes, it *IS* my code. It's quite complicated, too. But it gets much simpler if you delete everything that you don't need.

BTW, Barry, with "winObj" you would do this:

  executableFileStr:=LoadShortCut('c:\test.lnk').path;

:-))

Regards, Madshi.
0
 
LVL 17

Expert Comment

by:inthe
ID: 1390217
yeh i knew how with winobj but wasnt too sure without it ;-)

0
 
LVL 10

Expert Comment

by:Lischke
ID: 1390218
Just out of curiosity, what is WinObj? I didn't find it on my HD...

Ciao, Mike
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1390219
Hi Mike...  :-)

Well, "winObj" is a set of some units I've written. These units encapsulate of a lot of win32 APIs in Delphi3/4 interfaces (I love them). E.g. processes, threads, windows, shellLinks, shellFolders and much more. If you are interested I can send it to you. But you must not use it for commercial purposes without asking me before...  :-)

Regards, Madshi.
0
 
LVL 10

Expert Comment

by:Lischke
ID: 1390220
Thank you Madshi. I'd love to see your code. Perhaps we can exchange a "licence" of your code and some of our inhouse code (are you looking for something special, maybe the image processing code I asked my last question about)?

Ciao, Mike
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1390221
Would you give me your eMail address? Or write to "madshi@gmx.net". We can talk about exchanging "licences" later - if you like my code...   :-))
I must admit, it is sparely commented and there's no help file available yet. So you will have to dig into the interface part of the unit for know.
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 1390222
observing   :o)
0
 

Author Comment

by:mauromol
ID: 1390223
Hi there,
thanks to both inthe and Madshi.
I'm new to the OLE programming, so I would have some questions:
1) what is a "list of item identifiers" for a shell link object? Can I find something like that if I look at the properties of a .LNK file in my Start Menu?
2) is NeedToUninitialize useful? To do what?
3) can someone explain me how this code works when it converts a hot key from Word type to TShortCut type? How can I do the reverse operation so that I can implement it in my MakeLinkFile function?
Thanks in advance,
Mauro.
0
 
LVL 17

Expert Comment

by:inthe
ID: 1390224
1: madshi ??

2:NeedToUninitialize useful? CoUninitialize?
  it is for initializing/uninitializing com objects ,and i  suspect you'll get an error if it's not used.

3:look at TextToShortCut(String) in delphi help.
(type it in delphi select it then press f1)
0
 

Author Comment

by:mauromol
ID: 1390225
1) Madshi?? :-)))))

2) Hmmm... I don't get any errors if I don't use them... so? Moreover, they are not used within the LoadShellLink function...

3) But TShortCut is different from Word! Otherwise in the LoadShellLink you should need a simple ShortCutToTest(w1), not those three lines of shls, and/ors, etc.

Anohter little question: do I always have to allocate a buffer of lentgh MAX_PATH or MAX_PATH + 1 to safely store path strings? Microsoft says "at least MAX_PATH" in the Win32 help files, while this code always uses MAX_PATH + 1...
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 1390226
Item Identifiers are internal representations of objects in the shell namespace: Drives, Files, folders, printer, Control Panel Items, etc.

With the SHGetPathFromIDList function you can get the path when the object is a file or folder.

In the .LNK file it is just the 'path' to the object the shortcut refers to.

Eps.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1390227
Hi guys, just got up...   :-)

>> 1) what is a "list of item identifiers" for a shell link object? Can I find something like that if I look at the properties of a .LNK file in my Start Menu?

Eps is right. For shortcuts that refer to files/directories ("c:\test" or "\\server\share\test") you don't need that item identifier list, there you can use the path. But you can also make links to other objects like "My Computer". So what path would "My Computer" have? There is no filesystem path for it, so here we need to use the pidl. Windows has a pidl for every object, for system objects like "My Computer" and for files and paths, too.

>> 2) is NeedToUninitialize useful? To do what?

Well, I didn't do that in Delphi3 and it worked without that. I'm quite sure, Delphi3 called CoInitialize somewhere in the initialization part of a unit. But in Delphi4 it didn't work without calling CoInitialize before. If you want to know what it is good for, look at the documentation. It MUST be called somewhere, before we're using COM objects.

>> 3) can someone explain me how this code works when it converts a hot key from Word type to TShortCut type? How can I do the reverse operation so that I can implement it in my MakeLinkFile function?

var w1 : word;
begin
  w1:=TextToShortCut(hotKey);
  if w1 and scAlt  <>0 then w1:=(w1 and (not scAlt  )) or (HOTKEYF_ALT     shl 8);
  if w1 and scCtrl <>0 then w1:=(w1 and (not scCtrl )) or (HOTKEYF_CONTROL shl 8);
  if w1 and scShift<>0 then w1:=(w1 and (not scShift)) or (HOTKEYF_SHIFT   shl 8);
  result:=IShellLink_.SetHotKey(w1)=NO_ERROR;

>> Anohter little question: do I always have to allocate a buffer of lentgh MAX_PATH or MAX_PATH + 1 to safely store path strings? Microsoft says "at least MAX_PATH" in the Win32 help files, while this code always uses MAX_PATH + 1...

Where does Microsoft say "at least MAX_PATH"?
Well, paths can normally not be any longer than MAX_PATH, that's the sense of this constant and that's the reason why I've chosen this value. I'm not sure about the terminating 0 char (you know, Windows strings (=pchar) always have a terminating 0 char). So to go the secure way I reserved MAX_PATH+1 chars. But perhaps you missed, that after getting the characters, the string is reallocated to the exact length?

  params:=string(PChar(params));

This expression looks for the terminating 0 char and reallocates the string to the exact length.

Regards, Madshi.
0
 

Author Comment

by:mauromol
ID: 1390228
1) Thank you Madshi, I understood perfectly now!

2) I'm using Delphi 4, I'm not calling CoInitialize and the code works well (absolutely no crashes or exceptions)

3) Thanks for the code, although I still don't understand properly the relationship between TShortCut and Word types ;-)

4) For instance, in Microsoft SDK under GetSystemDirectory, the uSize parameter is explained in this way: "Specifies the maximum size of the buffer, in characters. This value should be set to at least MAX_PATH." So I was wondering if MAX_PATH is "the size of the longest path possible, including space for the terminating null" or "the size of the longest path possible, excluding space for the terminating null".
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1390229
2) Then someone else (another component or unit) calls it for you...   :-)
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
In this brief tutorial Pawel from AdRem Software explains how you can quickly find out which services are running on your network, or what are the IP addresses of servers responsible for each service. Software used is freeware NetCrunch Tools (https…
In this video, Percona Solution Engineer Rick Golba discuss how (and why) you implement high availability in a database environment. To discuss how Percona Consulting can help with your design and architecture needs for your database and infrastr…
Suggested Courses

719 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question