Lazarus (freepascal) Reading large output from TProcess - process

I' m reading large process output data in Lazarus using the TProcess and the suggestions from this freepascal wiki page.
The wiki page suggests to create a loop to read the process output data like this:
// ... If you want to read output from an external process, this is the code you should adapt for production use.
while True do
begin
MemStream.SetSize(BytesRead + 2024); // make sure we have room
NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
if NumBytes > 0
then begin
Inc(BytesRead, NumBytes);
Write('.') //Output progress to screen.
end else
BREAK // Program has finished execution.
end;
// "Then read the MemStream to do your job"
The wiki page also mentions that the calling program should read from the output pipe to prevent it from getting full.
So, how much data makes the output pipe full?
Why we should use a MemStream (TMemoryStream) and not directly read from OurProcess.Output stream (using the bytesAvailable, etc) in the above loop?
I'm reading 80MB of wav data from a process and I have noticed that both MemStream and OurProcess.Output streams have the same amount of data! The memory usage gets doubled. So the suggested method from the wiki cannot be considered as efficient or optimized. Or there is something I'm missing?

Afaik output/input streams are a stream form of a pipe, not memory streams. The values you see are retrieved from the OS handle, not from memory allocated to the FPC app per se.
It is just like you can ask for the .size of a file on disk without reading the whole file.

procedure RunExternalAppInMemo(DosApp:String;AMemo:TMemo);
const READ_BYTES = 2048;
var
aProcess: TProcess; //TProcess is crossplatform is best way
MemStream: TMemoryStream;
NumBytes: LongInt;
BytesRead: LongInt;
Lines: TStringList;
begin
// A temp Memorystream is used to buffer the output
MemStream := TMemoryStream.Create;
Lines :=TStringList.Create;
BytesRead := 0;
aProcess := TProcess.Create(nil);
aProcess.CommandLine := DosApp;
aprocess.ShowWindow := swoHIDE;
AProcess.Options := AProcess.Options + [poUsePipes];
aProcess.Execute;
while aProcess.Running do
begin
// make sure we have room
MemStream.SetSize(BytesRead + READ_BYTES);
// try reading it
NumBytes := aProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
if NumBytes > 0 // All read() calls will block, except the final one.
then Inc(BytesRead, NumBytes)
else
BREAK // Program has finished execution.
end;
MemStream.SetSize(BytesRead);
Lines.LoadFromStream(MemStream);
AMemo.lines.AddStrings(Lines);
aProcess.Free;
Lines.Free;
MemStream.Free;
end;

I was dealing with this problem today, I've modified Georgescu answer, as I wanted Memo to display output stream on the fly
procedure RunExternalAppInMemo(DosApp:String;AMemo:TMemo);
const READ_BYTES = 2048;
var
aProcess: TProcess; //TProcess is crossplatform is best way
NumBytes: LongInt;
Buffer: array of byte;
begin
// set the size of your buffer
SetLength(Buffer,READ_BYTES);
aProcess := TProcess.Create(nil);
aProcess.CommandLine := DosApp;
aprocess.ShowWindow := swoHIDE;
AProcess.Options := AProcess.Options + [poUsePipes];
aProcess.Execute;
while aProcess.Running do
begin
// try reading it
NumBytes := aProcess.Output.Read(Buffer[0], length(buffer)*sizeof(byte)); // I usually do it that way, so I can change Buffer size on if needed
AProcess.Suspend; //I have no experience with pipes, but it seems way I won loose eny output?
if NumBytes > 0 then // All read() calls will block, except the final one.
begin
AMemo.Lines.Add(Pchar(Buffer);
application.ProcessMessages;
AProcess.Resume;
end
else
BREAK; // Program has finished execution.
end;
setlength(Buffer,0);
aProcess.Free;
end;

Related

InnoSetup v6 ARC Extraction, Minimize Broken

InnoSetup v6
Extracting from a FreeArc 0.67 (March 15 2014) Archive using temporary file unarc.exe, during the extraction process the minimize window button is broken.
Using FreeARC because it's the best compression software I can find to date.
During the archive decompression, the GUI window with the status message can't be minimized.
Clicking the window makes windows respond with a deny sound. How do I resolve this?
Something to do with "ProgressPage" or executing the arc command line?
Even with this page disabled, it does the same thing, so probably the setup waiting for the process to finish?
I am constantly working on improving and getting this fix because I got files that are in the 100 GB range that need to be compressed to save on disk space. Making a setup program for the extraction process makes it easier to install those files vs relying on the application that I must install first in order to do an extraction.
EDIT: I switch the extraction program from arc.exe to unarc.exe because arc.exe was crashing due to RAM issues.
#define ArcArchive "Test.arc"
[Setup]
AppName=Test App
DefaultDirName=Test App
AppVerName=Test App
WizardStyle=modern
Compression=lzma2
SolidCompression=yes
Uninstallable=no
DisableProgramGroupPage=yes
[Files]
Source: unarc.exe; Flags: dontcopy
[Code]
var
ProgressPage: TOutputProgressWizardPage;
ProgressFileName: string;
procedure ExtractArc;
var
ArcExtracterPath: string;
ArcArchivePath: string;
TempPath: string;
CommandLine: string;
ResultCode: Integer;
S: AnsiString;
Message: string;
begin
ExtractTemporaryFile('unarc.exe');
ProgressPage := CreateOutputProgressPage('Decompression', 'Decompressing archive...please wait');
ProgressPage.Show;
try
TempPath := ExpandConstant('{tmp}');
ArcExtracterPath := TempPath + '\unarc.exe';
ArcArchivePath := ExpandConstant('{src}\{#ArcArchive}');
ProgressFileName := ExpandConstant('{tmp}\progress.txt');
Log(Format('Expecting progress in %s', [ProgressFileName]));
CommandLine :=
Format('"%s" x -o+ -dp"%s" "%s" > "%s"', [
ArcExtracterPath, ExpandConstant('{app}'), ArcArchivePath, ProgressFileName]);
Log(Format('Executing: %s', [CommandLine]));
CommandLine := Format('/C "%s"', [CommandLine]);
if not Exec(ExpandConstant('{cmd}'), CommandLine, '', SW_HIDE,
ewWaitUntilTerminated, ResultCode) then
begin
RaiseException('Cannot start extracter');
end
else
if ResultCode <> 0 then
begin
LoadStringFromFile(ProgressFileName, S);
Message := Format('Arc extraction failed failed with code %d', [ResultCode]);
Log(Message);
Log('Output: ' + S);
RaiseException(Message);
end
else
begin
Log('Arc extraction done');
end;
finally
{ Clean up }
Log('Arc extraction cleanup');
ProgressPage.Hide;
DeleteFile(ProgressFileName);
end;
Log('Arc extraction end');
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
ExtractArc;
end;
end;

An alternative to sleep () function in Delphi7

IUfMessung = interface
['{6C258E04-BCC9-4349-912B-57A38F103570}']
function MacheMessung(Ifl,Ufl: double): double;
end;
TUFMessungMitHalten = class(TInterfacedObject,IUfMessung)
private
SWZeitHalten: double;
public
constructor Create(ASWZeitHalten: double); // Time to keep
destructor Destroy; override;
function MacheMessung(Ifl,Ufl: double): double; // do measuring
end; // measuring with holding
TUFMessungMitPause = class(TInterfacedObject,IUfMessung)
private
SWZeitPause: double;
IfMin: double;
public
constructor Create(ASWZeitPause: double; AIfMin: double); // Time to keep + I[A]
destructor Destroy; override;
function MacheMessung(Ifl,Ufl: double): double;
end; // measuring with Pause
TUFMessung = class(TInterfacedObject)
private
//...
Messungsart: IUfMessung;
public
procedure SetMessungsart(art: IUfMessung); // set measuring type
procedure MessungsArtAswahl; // measuring type choice
//...
end; // do measuring
{ TUFMessung }
procedure TUFMessung.MessungsArtAswahl;
begin
if not FMitHalten and not FMitPause then // Uf simple measuring
begin
SetMessungsart(TUFMessungEinfach.Create);
end;
if FMitHalten and not FMitPause then // Uf with holding
begin
SetMessungsart(TUFMessungMitHalten.Create(FSWZeitHalten));
end;
if not FMitHalten and FMitPause then // Uf with pause
begin
SetMessungsart(TUFMessungMitPause.Create(FSWZeitPause, FStartIf));
end;
end;
procedure TUFMessung.Start(StartIf, EndIf, StepIf, Uleer: double);
begin
//...
while not FIstFertig and FUfKannStart do
begin
Uf:= Messungsart.MacheMessung(Ifl, FUleer); // <= CALL the measuring
//...
end;
end;
{ TUFMessungMitHalten }
function TUFMessungMitHalten.MacheMessung(Ifl, Ufl: double): double;
var i_Zeit: integer;
begin // Messvorgang
hole_Uf(true, Ifl, Ufl); // set value
i_Zeit:= Trunc(SWZeitHalten*1000);
Application.ProcessMessages;
Sleep(i_Zeit); // wait Time ms.
result:= hole_Uf(false, Ifl, Ufl); // measuring
end;
{ TUFMessungMitPause }
function TUFMessungMitPause.MacheMessung(Ifl, Ufl: double): double;
var i_Zeit: integer;
begin // Messvorgang
result:= hole_Uf(false, Ifl, Ufl); // measuring
hole_Uf(true, IfMin, Ufl); // set value
i_Zeit:= Trunc(SWZeitPause*1000);
Application.ProcessMessages;
Sleep(i_Zeit); // wait Time ms.
end;
I need to wait between the measuring processes for a time from 0 to 5 seconds. The solution with sleep () works well only till 1 second because I have in the program an RS232 communication and some timers.
Is there an alternative to sleep () function so that the program precisely at this point a certain time is waiting for? Thank you in advance.
I can't tell from your source, but if you're combining RS232 and waiting, Sleep sounds like a bad idea. The best you could do is have the system respond to you as soon as data comes in, not blindly wait for it. Depending on what you use to do RS232 communication, you should look for something like SetCommTimeouts and fine-tune how read operations behave: if the data is not in yet, stall for the receive timeout, and after that respond that zero bytes were received. This is best done from a dedicated thread (which might take a little learning to get a hang of). Another option is using asynchronous calls (which also take a little learning to get a hang of).
As David says, there may be more elegant solutions for an ideal world, or if you are willing to get your hands dirty with low level device I/O and threading. But until you have perhaps identified a more elegant solution, another approach would be to create your own time-out loop (a so called "busy loop") around Application.ProcessMessages to incorporate a "time-out" behaviour to return control to the caller after a specified time, processing messages in the meantime.
This might look something similar to this:
procedure ProcessMessagesFor(aTimeOut: Integer);
var
start: Int64;
elapsed: Integer;
begin
start := Trunc(Now * 24 * 60 * 60 * 1000);
elapsed := 0;
while elapsed < aTimeout do
begin
Application.ProcessMessages;
elapsed := Trunc(Now * 24 * 60 * 60 * 1000) - start;
end;
end;
This is also less than ideal however since Application.ProcessMessages will itself not return until any and all messages have been processed. It would be better to check for the elapsed timeout after each message so that we can get back into the normal message loop as soon as possible.
Application.ProcessMessages simply calls a ProcessMessage function, but this is private to the TApplication class, so we cannot call this directly.
Fortunately, in Delphi 7, the ProcessMessage function is itself relatively simple and can be easily replicated within the timeout loop of a custom message processor.
Note that to do this we need to change a couple of private references (fOnMessage event handler for example) to the public equivalents and a handful of TApplication protected methods are involved which we obtain access to using a local sub-class and type-casting (a primitive pre-cursor to "class helpers" but which works in all versions of Delphi):
type
// Creates a sub-class in scope which we can use in a typecast
// to gain access to protected members of the target superclass
TApplicationHelper = class(TApplication);
procedure ProcessMessagesFor(aTimeOut: Integer);
var
start: Int64;
elapsed: Integer;
wait: Boolean;
function ProcessMessage: Boolean;
var
msg: TMsg;
handled: Boolean;
app: TApplicationHelper;
begin
app := TApplicationHelper(Application);
result := False;
if PeekMessage(msg, 0, 0, 0, PM_REMOVE) then
begin
result := True;
if msg.Message <> WM_QUIT then
begin
handled := False;
if Assigned(Application.OnMessage) then
Application.OnMessage(msg, handled);
if not app.IsHintMsg(msg)
and not handled
and not app.IsMDIMsg(msg)
and not app.IsKeyMsg(msg)
and not app.IsDlgMsg(msg) then
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
end
else
PostQuitMessage(msg.wParam);
end;
end;
begin
wait := FALSE; // We will not wait for messages initially
start := Trunc(Now * 24 * 60 * 60 * 1000);
SetTimer(0, 0, aTimeout, NIL); // Makes sure we get a message (WM_TIMER) at the end of the timeout period
repeat
if wait then
WaitMessage;
wait := NOT ProcessMessage; // If there was no message then we will wait for one next time around
elapsed := Trunc(Now * 24 * 60 * 60 * 1000) - start;
until (elapsed >= aTimeout);
end;
I have used a crude multiplier and truncation of a Now date/time to obtain a millisecond precision elapsed time counter without having to deal with the wrap around (potential) issue with GetTickCount. You may want to modify this to use a HPC or simply deal with the GetTickCount wrap-around.
We incorporate a WaitMessage mechanism so that if there are no messages to be processed then the code simply waits (efficiently) for any new messages. To ensure that we are not waiting for messages beyond the timeout period, we initially set a timer event for the specified timeout. This guarantees that a WM_TIMER message will arrive to signal the expiry of the timeout, which will "wake up" our message loop if it is still waiting when the timeout has expired.
Another thing to note is that WM_QUIT messages are re-posted to the message queue. This is to ensure that these will be handled correctly when the ProcessMessagesFor() loop has timed out and messages are once again being handled by the main application message loop.
This function does not (strictly speaking) call Application.ProcessMessages, nor does it involve Sleep(), but it is still not an ideal solution being vulnerable to (potential) re-entrancy problems that having "inline" message loops always creates. These can be managed by controlling user interaction with parts of the UI that might cause such re-entrancy problems (i.e. disable forms or controls while processing is completed).
But even without such refinements it may keep you going with your current problem unless and until a more ideal solution is found.
Create you own sleep function:
procedure MySleep (const uSec: UInt);
var uStart : UInt;
begin
uStart := GetTickCount;
while (GetTickCount < (uStart + (uSec * 1000))) do
begin
Sleep (250);
Application.ProcessMessages;
end
end;

Delphi memory stream to server

I am working on creating a chat program. But for some reason my stream won't come through. Could someone check my code and tell me what I am doing wrong?
Client side:
procedure TForm1.Button1Click(Sender: TObject);
var
myStream : TMemoryStream;
chat : String;
begin
//Creating a stream
chat := 'bladibla';
myStream := TMemoryStream.Create();
myStream.Size := Length(Chat);
myStream.WriteBuffer(Pointer(Chat)^, Length(Chat));
//Resetting the stream position
myStream.Position := 0;
//Sending the stream
TcpClient1.Active := true;
TcpClient1.SendStream(myStream);
TcpClient1.Active := false;
//Free the stream
myStream.Free;
end;
Server Side:
procedure TForm1.TcpServer1Accept(Sender: TObject;
ClientSocket: TCustomIpClient);
var
chat : string;
begin
//Receives the message from the client
ClientSocket.ReceiveBuf(Pointer(Chat)^,Length(Chat),0);
memo1.Lines.Add(chat);
memo1.Lines.Add('------');
end;
If you're using D2009 or later, then when sending, you're cutting the data in half. Also, given you will ultimately be reading the data with ReceiveBuf, it would probably be sensible to prepend a length marker. Less substantively, you also don't need to set the memory stream's Size up front, and should wrap the stream usage in a try/finally block:
procedure TForm1.Button1Click(Sender: TObject);
var
myStream : TMemoryStream;
chat : String;
Len: Int32;
begin
//Creating a stream
chat := 'bladibla';
myStream := TMemoryStream.Create();
try
Len := Length(Chat);
myStream.WriteBuffer(Len, SizeOf(Len));
myStream.WriteBuffer(Pointer(Chat)^, Len * SizeOf(Char));
//Resetting the stream position
myStream.Position := 0;
//Sending the stream
TcpClient1.Active := true;
TcpClient1.SendStream(myStream);
TcpClient1.Active := false;
finally
//Free the stream
myStream.Free;
end;
end;
In the case of the receiving code, you haven't pre-allocated the Chat buffer. As such, Length(Chat) will be 0. Given my suggested code above, we can read off the length marker first:
procedure TForm1.TcpServer1Accept(Sender: TObject;
ClientSocket: TCustomIpClient);
var
chat : string;
Len: Int32;
begin
//Receives the message from the client
ClientSocket.ReceiveBuf(Len, SizeOf(Len),0);
SetLength(Chat, Len);
ClientSocket.ReceiveBuf(Pointer(Chat)^,Len * SizeOf(Char),0);
memo1.Lines.Add(chat);
memo1.Lines.Add('------');
end;
Lastly... the components used in this code are deprecated, so it might be an idea to investigate Indy instead.

Communication between two programs using file

I want two separate programs written in Pascal communicate between themselves using additional text file. It works fine for the first 2-3 messages but then it gives run-time error in either sender.pas or receiver.pas Do you know where my mistake is or do you have any suggestion?
First program receiver.pas
var
f : text;
s : string;
begin
{I-}
Assign(f,'main.in');
while true do
begin
reset(f);
while IOResult<>0 do //
begin // Wait until the file
close(f); // is closed by sender
reset(f); //
end; //
if eof(f) then
close(f)
else
begin
readln(f,s);
close(f);
rewrite(f);
close(f);
writeln(s);
end;
end;
{I+}
end.
second program sender.pas
var
f : text;
s : string;
begin
{I-}
Assign(f,'main.in');
while true do
begin
readln(s);
rewrite(f); //
while IOResult<>0 do //
begin // Wait until the file
close(f); // is closed by receiver
rewrite(f); //
end; //
writeln(f,s);
close(f);
end;
{I+}
end.
Some things I noticed:
If rewrite fails, afaik the file was not opened and you don't have to close it? Closing an unopened file might cause runtime errors (though I assume assign will init it safely)
depending on how you use these, there might be in the logic. Namely that after closing a file is directly ready for opening by other apps. In general closed filehandles linger several 100ms till several seconds (depending on filesystem busines). This can cause starvation problems in such schemes. (while it would work in plain dos, which didn't linger, at least not that much)
The reader will crash if the file doesn't exist.
I assume that the {I-}/{I+} is a typo and that your sourcecode really reads {$I-} and {$I+} (note the dollar?)
In Windows pascal versions, read only access is not always locking. Put filemode:=2 as first line in everything.
What compiler is this? Delphi, Free Pascal? Which version?
My new (Free Pascal) receiver code becomes:
uses sysutils;
var
f : text;
s : string;
begin
filemode := 2; // read-only
{$I-}
Assign(f,'main.in');
while true do
begin
reset(f);
while IOResult<>0 do //
begin // Wait until the file
close(f); // is closed by sender
sleep(1000);
reset(f);
end;
if eof(f) then
begin
close(f);
sleep(1000);
end
else
begin
readln(f,s);
close(f);
rewrite(f);
close(f);
writeln(s);
end;
end;
{$I+}
end.
The new sender code is:
uses sysutils;
var
f : text;
s : string;
begin
filemode := 2; // read-only
{$I-}
Assign(f,'main.in');
while true do
begin
reset(f);
while IOResult<>0 do //
begin // Wait until the file
close(f); // is closed by sender
sleep(1000);
reset(f);
end;
if eof(f) then
begin
close(f);
sleep(1000);
end
else
begin
readln(f,s);
close(f);
rewrite(f);
close(f);
writeln(s);
end;
end;
{$I+}
end.
A couple of things: Make sure you check IOResult after every file operation, not just rewrite/reset - don't call close if your reset/rewrite failed - and you probably want something like a Sleep(250) in those retry loops.

Is it possible to 'Pin to start menu' using Inno Setup?

I'm using the excellent Inno Setup installer and I notice that some Applications (often from Microsoft) get installed with their launch icon already highly visible ('pinned?') in the start menu (in Windows 7). Am I totally reliant on the most-recently-used algorithm for my icon to be 'large' in the start menu, or is there a way of promoting my application from the installer please?
It is possible to pin programs, but not officially. Based on a code posted in this thread (which uses the same way as described in the article linked by #Mark Redman) I wrote the following:
[Code]
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
const
// these constants are not defined in Windows
SHELL32_STRING_ID_PIN_TO_TASKBAR = 5386;
SHELL32_STRING_ID_PIN_TO_STARTMENU = 5381;
SHELL32_STRING_ID_UNPIN_FROM_TASKBAR = 5387;
SHELL32_STRING_ID_UNPIN_FROM_STARTMENU = 5382;
type
HINSTANCE = THandle;
HMODULE = HINSTANCE;
TPinDest = (
pdTaskbar,
pdStartMenu
);
function LoadLibrary(lpFileName: string): HMODULE;
external 'LoadLibrary{#AW}#kernel32.dll stdcall';
function FreeLibrary(hModule: HMODULE): BOOL;
external 'FreeLibrary#kernel32.dll stdcall';
function LoadString(hInstance: HINSTANCE; uID: UINT;
lpBuffer: string; nBufferMax: Integer): Integer;
external 'LoadString{#AW}#user32.dll stdcall';
function TryGetVerbName(ID: UINT; out VerbName: string): Boolean;
var
Buffer: string;
BufLen: Integer;
Handle: HMODULE;
begin
Result := False;
Handle := LoadLibrary(ExpandConstant('{sys}\Shell32.dll'));
if Handle <> 0 then
try
SetLength(Buffer, 255);
BufLen := LoadString(Handle, ID, Buffer, Length(Buffer));
if BufLen <> 0 then
begin
Result := True;
VerbName := Copy(Buffer, 1, BufLen);
end;
finally
FreeLibrary(Handle);
end;
end;
function ExecVerb(const FileName, VerbName: string): Boolean;
var
I: Integer;
Shell: Variant;
Folder: Variant;
FolderItem: Variant;
begin
Result := False;
Shell := CreateOleObject('Shell.Application');
Folder := Shell.NameSpace(ExtractFilePath(FileName));
FolderItem := Folder.ParseName(ExtractFileName(FileName));
for I := 1 to FolderItem.Verbs.Count do
begin
if FolderItem.Verbs.Item(I).Name = VerbName then
begin
FolderItem.Verbs.Item(I).DoIt;
Result := True;
Exit;
end;
end;
end;
function PinAppTo(const FileName: string; PinDest: TPinDest): Boolean;
var
ResStrID: UINT;
VerbName: string;
begin
case PinDest of
pdTaskbar: ResStrID := SHELL32_STRING_ID_PIN_TO_TASKBAR;
pdStartMenu: ResStrID := SHELL32_STRING_ID_PIN_TO_STARTMENU;
end;
Result := TryGetVerbName(ResStrID, VerbName) and ExecVerb(FileName, VerbName);
end;
function UnpinAppFrom(const FileName: string; PinDest: TPinDest): Boolean;
var
ResStrID: UINT;
VerbName: string;
begin
case PinDest of
pdTaskbar: ResStrID := SHELL32_STRING_ID_UNPIN_FROM_TASKBAR;
pdStartMenu: ResStrID := SHELL32_STRING_ID_UNPIN_FROM_STARTMENU;
end;
Result := TryGetVerbName(ResStrID, VerbName) and ExecVerb(FileName, VerbName);
end;
The above code first reads the caption of the menu item for pinning or unpinning applications from the string table of the Shell32.dll library. Then connects to the Windows Shell, and for the target app. path creates the Folder object, then obtains the FolderItem object and on this object iterates all the available verbs and checks if their name matches to the one read from the Shell32.dll library string table. If so, it invokes the verb item action by calling the DoIt method and exits the iteration.
Here is a possible usage of the above code, for pinning:
if PinAppTo(ExpandConstant('{sys}\calc.exe'), pdTaskbar) then
MsgBox('Calc has been pinned to the taskbar.', mbInformation, MB_OK);
if PinAppTo(ExpandConstant('{sys}\calc.exe'), pdStartMenu) then
MsgBox('Calc has been pinned to the start menu.', mbInformation, MB_OK);
And for unpinning:
if UnpinAppFrom(ExpandConstant('{sys}\calc.exe'), pdTaskbar) then
MsgBox('Calc is not pinned to the taskbar anymore.', mbInformation, MB_OK);
if UnpinAppFrom(ExpandConstant('{sys}\calc.exe'), pdStartMenu) then
MsgBox('Calc is not pinned to the start menu anymore.', mbInformation, MB_OK);
Please note that even though this code works on Windows 7 (and taskbar pinning also on Windows 8.1 where I've tested it), it is really hacky way, since there is no official way to programatically pin programs to taskbar, nor start menu. That's what the users should do by their own choice.
There's a reason there's no programmatic way to pin things to the taskbar/start menu. In my experience, I have seen the start menu highlight newly-created shortcuts, and that's designed to handle exactly this situation. When you see a newly-installed program show up on the start menu, it's probably because of that algorithm and not because the installer placed it there.
That said, if a new shortcut does not appear highlighted, it may be because the installer extracts a pre-existing shortcut and preserves an old timestamp on it, rather than using the API function to create a shortcut in the start menu.
Have a look at: http://blogs.technet.com/deploymentguys/archive/2009/04/08/pin-items-to-the-start-menu-or-windows-7-taskbar-via-script.aspx