PopString and PushString methods in the assembled dll - nsis

During the execution of the dll the installer crashes on these methods
The methods are taken from the file nsis.pas
function PopString(): string;
var
th: pstack_t;
begin
if integer(g_stacktop^) <> 0 then begin
th := g_stacktop^;
Result := PChar(#th.text);
g_stacktop^ := th.next;
GlobalFree(HGLOBAL(th));
end;
end;
procedure PushString(const str: string='');
var
th: pstack_t;
begin
if integer(g_stacktop) <> 0 then begin
th := pstack_t(GlobalAlloc(GPTR, SizeOf(stack_t) + g_stringsize));
lstrcpyn(#th.text, PChar(str), g_stringsize);
th.next := g_stacktop^;
g_stacktop^ := th;
end;
end;

It looks like nsis.pas is perhaps not compatible with every Delphi version when compiling as Unicode. You did not specify your version so I don't know if your string type is AnsiString or WideString or UnicodeString.
I don't really know Pascal/Delphi anymore but I was able to get it working in FreePascal with this version. I'll try to fix it for the next release.
Make sure UNICODE is defined when using this in a Unicode plug-in.

The problem was very easy to solve
It was necessary to call Init() from nsis.pas
and then everything worked

Related

Compare two strings inno setup

I would like to check MD5 of a file with this code:
[Code]
var
MD5Comp: string;
procedure ExitProcess(uExitCode:UINT);
external 'ExitProcess#kernel32.dll stdcall';
procedure CurStepChanged(CurStep: TSetupStep);
begin
MD5Comp := '32297BCBF4D802298349D06AF5E28059';
if CurStep = ssInstall then
begin
if not MD5Comp=GetMD5OfFile(ExpandConstant('{app}\cg.npa')) then
begin
MsgBox('A patched version detected. Setup will now exit.', mbInformation, MB_OK);
ExitProcess(1);
end;
end;
end;
But I get a "Type Mismatch" error when comparing the two string, so I am assuming that this is not how you compare them.
Edit: I've tried if not CompareText(MD5Comp,GetMD5OfFile(ExpandConstant('{app}\cg.npa')))=0 but it never execute what's inside the if.
This seems to be an exception of the Pascal Script compiler. You were expecting an expression like this (assume S1 and S2 are string variables):
if not (S1 = S2) then
But the compiler treated it like this instead:
if (not S1) = S2 then
Well, I would personally expect a compiler error instead of runtime one here. At least you have simple way to workaround this issue if you explicitly enclose that comparison into parentheses like:
if not (MD5Comp = GetMD5OfFile(ExpandConstant('{app}\cg.npa'))) then
Or optionally write more literally:
if MD5Comp <> GetMD5OfFile(ExpandConstant('{app}\cg.npa')) then
Do note, that in this case the parentheses are not needed because with the <> operator it becomes a single boolean expression.

Conditional DisableProgramGroupPage in Inno Setup

I'm trying to create a single installer for both normal and portable installs. For the portable install I am disabling all icons and uninstaller creation.
The only problem I am stuck at is how to disable program group page when running the portable install. Am I misunderstanding something here?
[Setup]
;This works as expected
Uninstallable=not IsPortable()
;This does NOT work, can't compile (DisableProgramGroupPage=yes alone compiles fine)
DisableProgramGroupPage=yes IsPortable()
Compilation fails with error
Value of [Setup] section directive ... is invalid.
This is the IsPortable() function:
function IsPortable(): Boolean;
begin
if(StandardRadioButton.Checked = True) then
Result := False
else
Result := True;
end;
(Elaborating on #TLama's comment)
The DisableProgramGroupPage does not support "boolean expression":
[Setup]: DisableProgramGroupPage
Valid values: auto, yes, or no
Contrary to Uninstallable:
[Setup]: Uninstallable
Valid values: yes or no, or a boolean expression
You can use ShouldSkipPage event function instead:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID = wpSelectProgramGroup then
begin
Result := IsPortable;
end;
end;

OleVariant and String assignments in Delphi 5

I have a Delphi 5 legacy application and there's a part in which a "string" value is been assigned to an "OleVariant" variable. Something like this:
var
X: OleVariant;
S: string;
Begin
S:= ‘This string should contain 200 characters as per design’;
X:= S;
End;
If the length of “S” is greater than 128, then the value of “X” gets truncated and it only holds a maximum of 128 characters.
Is there a way to overcome this?
I believe there is a way, because if I create my own demo application from scratch (in the same PC, with the same Delphi 5), it allows me to pass longer string values and no truncating is done.
Maybe it is something about the project settings or compiler directives. I have played around with this idea, but I have no workaround yet.
Any help is appreciated. Thanks.
Demo:
procedure TForm1.Button1Click(Sender: TObject);
var
X: OleVariant;
S: string;
begin
//in the Edit I pass a string of 240 chars, let's say;
S:= Edit1.Text;
X:= S;
ShowMessage(IntToStr(Length(X)) + ' : ' + IntToStr(Length(S)));
//this showmessage shows "128 : 240"
end;
Try this OleVariantToString and StringToOleVariant functions at http://www.foxbase.ru/delphi/vzaimnye-preobrazovaniya-olevariant-i-string.htm
They work perfectly for me.
uses Classes, Variants;
function OleVariantToString(const Value: OleVariant): string;
var ss: TStringStream;
Size: integer;
Data: PByteArray;
begin
Result:='';
if Length(Value) = 0 then Exit;
ss:=TStringStream.Create;
try
Size := VarArrayHighBound (Value, 1) - VarArrayLowBound(Value, 1) + 1;
Data := VarArrayLock(Value);
try
ss.Position := 0;
ss.WriteBuffer(Data^, Size);
ss.Position := 0;
Result:=ss.DataString;
finally
VarArrayUnlock(Value);
end;
finally
ss.Free;
end;
end;
function StringToOleVariant(const Value: string): OleVariant;
var Data: PByteArray;
ss: TStringStream;
begin
Result:=null;
if Value='' then Exit;
ss:=TStringStream.Create(Value);
try
Result := VarArrayCreate ([0, ss.Size - 1], varByte);
Data := VarArrayLock(Result);
try
ss.Position := 0;
ss.ReadBuffer(Data^, ss.Size);
finally
VarArrayUnlock(Result);
end;
finally
ss.Free;
end;
end;
One explanation is that OleVariant holds the entire string but that you are looking at the debugger tooltip. In older Delphi versions the debugger tooltip truncates at 128 characters for strings held in a variant. Note that the debugger tooltip for a plain string does not truncate at this length. Try showing the variant in a dialog box and you will see that the entire string is present.
I checked this out on Delphi 6 and there was no truncation with your code (other than the debugger tooltip). Andreas did likewise on Delphi 4 and Rodrigo did so with Delphi 5. I cannot imagine that it could really be the case that strings in a Delphi 5 OleVariant are truncated at 128 characters.
If you really are seeing what you are report then I can think of the following explanations:
Your code is erroneously truncating the string, but you have not yet found the code that does this. Only you can debug that.
You have a local bug private to your Delphi installation. Are you by any chance compiling your own RTL?
I made this work. Summary: instead of filling an “OleVariant” with a “string”; I filled a “Variant” and then typecasted that “Variant” to “OleVariant”. Take a look at the code below so that you can get the idea.
procedure TForm1.Button1Click(Sender: TObject);
var
//X: OleVariant;
X: Variant;
S: string;
begin
//Let's say in the Edit1 I pass a string of 240 chars,
S:= Edit1.Text;
X:= S;
//ShowMessage(IntToStr(Length(X)) + ' : ' + IntToStr(Length(S)));
ShowMessage(IntToStr(Length(OleVariant(X))) + ' : ' + IntToStr(Length(S)));
//This ShowMessage shows "128 : 240"
end;
Honestly, I don’t know for sure why this makes a difference, but it does. It works ok now.
Thanks a lot for your help folks!

Is this the correct way to set a thread's name in Delphi 6?

I want to have a nice user friendly name appear in the thread list window while in the Delphi 6 IDE. I found the code below on the web for doing this in Delphi 6 since as far as I know that version does not have SetThreadName() implemented natively. I call it from my thread's Execute() method. I know it's being called because the IDE pops up when the Exception is raised. However, when I look in the thread list (Ctrl + Alt + T), I don't see the name I set. I just see the usual Thread Id, State, Status, and Location columns, nothing else.
What do I need to do differently to get the thread names to appear? Also, does anyone have an idea on how to stop the IDE from pausing on the RaiseException line? I have a lot of threads in the program and it's annoying to have the IDE popping up N times every time I run the program.
I know I can disable the IDE from stopping on Delphi Exceptions, but I want that normally and I'd prefer not to have to toggle that off and on every time a new set of threads is created.
Named threads in Delphi - what is that for?
procedure SetThreadName_delphi(const Name: string);
type
TThreadNameInfo =
record
RecType: LongWord;
Name: PChar;
ThreadID: LongWord;
Flags: LongWord;
end;
var
info:TThreadNameInfo;
begin
// This code is extremely strange, but it's the documented way of doing it!
info.RecType := $1000;
info.Name := PChar(Name);
info.ThreadID := $FFFFFFFF;
info.Flags := 0;
try
RaiseException($406D1388, 0,
SizeOf(info) div SizeOf(LongWord), PDWord(#info));
except
end;
end;
I have found the original code
It is an application-specific exception (that means it is specific for Visual C++ compiler). I see no reason why Delphi should support this strange feature (though it is possible).
Edit : BUT IT WORKS! (Thanks to Remy Lebeau)
Just tested on Delphi XE (I see 'Wow!' in the debugger 'Thread status' window):
unit NameTest;
interface
uses
Windows, Classes;
type
TTestThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;
implementation
{ TTestThread }
procedure SetThreadName_delphi(const Name: string);
type
TThreadNameInfo =
record
RecType: LongWord;
Name: PAnsiChar;
ThreadID: LongWord;
Flags: LongWord;
end;
var
info:TThreadNameInfo;
AnsiName: AnsiString;
begin
AnsiName:= Name;
info.RecType := $1000;
info.Name := PAnsiChar(AnsiName);
info.ThreadID := $FFFFFFFF;
info.Flags := 0;
try
RaiseException($406D1388, 0,
SizeOf(info) div SizeOf(LongWord), PDWord(#info));
except
end;
end;
procedure TTestThread.Execute;
begin
SetThreadName_delphi('Wow!');
while not Terminated do
Sleep(1000);
end;
end.
In C++Builder 6 and Delphi 7 onwards, the File > New > Other > Thread Object wizard has an option for naming the new thread in the debugger. The wizard generates a stub TThread descendant class with the necessary RaiseException() implementation at the top of its Execute() method.
That is not any help for Delphi 6 though, which did not support thread naming yet.

BeginThread Structure - Delphi

I've got a almost completed app now and the next feature I want to implement is threading. I chose to go with BeginThread(), although am aware of TThread in delphi. The problem I'm coming across is the structure of BeginThread() call. Normally the line in the program that would call the function I want to be threaded is
CompareFiles(form1.Edit3.Text,Form1.Edit4.Text,Form1.StringGrid2,op);
op is a integer.
The line I've switched it out for to create a thread from it is
BeginThread(nil,0,CompareFiles,Addr('form1.Edit3.Text,Form1.Edit4.Text,Form1.StringGrid2,op'),0,x);
From the little amount of infromation I can find on how to actually use BeginThread() this should be a fine call, however on compling all I get is complier errors regarding the structure of my BeginThread() statement paramenters.
EDIT FOR INFORMATION.
The current procedure that calls CompareFiles is
procedure TForm1.Panel29Click(Sender: TObject);
var
op,x : integer;
begin
if (Form1.Edit3.Text <> '') AND (Form1.Edit4.Text <> '') then
begin
op := 3;
if RadioButton7.Checked = True then op := 0;
if RadioButton3.Checked = True then op := 1;
if RadioButton4.Checked = True then op := 2;
if RadioButton5.Checked = True then op := 3;
if RadioButton6.Checked = True then op := 4;
CompareFiles(form1.Edit3.Text,Form1.Edit4.Text,Form1.StringGrid2,op);
end;
end;
If I was to use TThread as suggested by a couple of people, and as displayed by Rob below, I'm confused at how a) I would pass op,Edit3/4.Text and StringGrid2 to the CompareFiles. Guessing from the example of TThread I've seen I thought I would replace the code above with TCompareFilesThread.Executeand the put the current code from Panel29Click into TCompareFilesThread.Create and then add
FEdit3Text := Edit3Text;
FEdit4Text := Edit4Text;
FGrid := Grid;
to this
FEdit3Text := Form1.Edit3.Text;
FEdit4Text := Form1.Edit4.Text;
FGrid := Form1.StringGrid2;
But I've got this nagging feeling that is totally off the mark.
That's not at all the way to use BeginThread. That function expects a pointer to a function that takes one parameter, but the function you're trying to call wants four. The one parameter you're giving to BeginThread for it to forward to the thread procedure is a string, but you evidently hope that some sort of magic will turn that string of characters into the values that those variables contain.
That's not how Delphi works, and even for the languages that can do something like that, it's generally discouraged to actually do it.
To pass multiple parameters to BeginThread, define a record with all the values you'll need, and also define a record pointer:
type
PCompareFilesParams = ^TCompareFilesParams;
TCompareFilesParams = record
Edit3Text,
Edit4Text: string;
Grid: TStringGrid;
Op: Integer;
end;
Change CompareFiles to accept a pointer to that record:
function CompareFiles(Params: PCompareFilesParams): Integer;
To start the thread, you'll need to allocate an instance of that record and populate its fields:
var
Params: PCompareFilesParams;
begin
New(Params);
Params.Edit3Text := Edit3.Text;
Params.Edit4Text := Edit4.Text;
Params.Grid := StringGrid2;
Params.Op := op;
BeginThread(nil, 0, #CompareFiles, Params, 0, x);
Implement CompareFiles like this so that the record will get freed before the thread terminates:
function CompareFiles(Params: PCompareFilesParams): Integer;
begin
try
// <Normal implementation goes here.>
finally
Dispose(Params);
end;
end;
You can make it all a lot easier if you just use TThread, though. You can make your descendant class have as many parameters as you want in its constructor, so you don't have to mess around with dynamically allocating and freeing a special record.
type
TCompareFilesThread = class(TThread)
private
FEdit3Text,
FEdit4Text: string;
FGrid: TStringGrid;
FOp: Integer;
procedure Execute; override;
public
constructor Create(const Edit3Text, Edit4Text: string; Grid: TStringGrid; Op: Integer);
property ReturnValue;
end;
constructor TCompareFilesThread.Create;
begin
inherited Create(False);
FEdit3Text := Edit3Text;
FEdit4Text := Edit4Text;
FGrid := Grid;
FOp := Op;
end;
procedure TCompareFilesThread.Execute;
begin
ReturnValue := CompareFiles(FEdit3Text, FEdit4Text, FGrid, FOp);
end;
Instead of calling BeginThread, you just instantiate the class and let it run:
var
ThreadRef: TThread;
ThreadRef := TCompareFilesThread.Create(Edit3.Text, Edit4.Text, StringGrid2, Op);
There's more to using threads, such as knowing when the thread has finished running, but I think you have enough to get started. One last thing to beware of, though, is that TStringGrid is a VCL control. You mustn't do anything with it from this new thread you create (regardless of how you end up creating it). Eveything you do with the grid control need to be done from the main thread. Use TThread.Synchronize and TThread.Queue to shift any VCL operations onto the main thread. Your file-comparing thread will wait for the synchronized operation to complete, but it will keep running without waiting for a queued operation to complete.

Resources