Remote debugging with XE2 - display of strings - string

I'm remote debugging a large app between DElphi XE2 (update #4) and a Windows XP target. PAServer is running on the target and the application works fine and stops at breakpoints (you would NOT believe how hard just that achievement was - hint - delete your DPROJ and start again if it has been through any IDE prior to XE).
I notice that the display of local variables and watches show my strings in a strange format compared to the usual display of the 'some string' format that one sees when debugging locally. I see:
Can anyone tell me why the strings are displayed this way? I also get quite a bit of { NULL } and garbage between {}'s on output variables that are not yet assigned.
Thanks.
. I see that this format indicates wide strings. I tried a simple app on Windows 7 and got the following result. My App at a breakpoint:
The displayed local string variables:
Note the truncated 'Hello'. It would seem that XE2 has a problem with remote unicode strings at times. My PaServer is version 1.0.2. Can anyone check that this is the latest? 'Twas taken from Update #4...

I'm not entirely sure why you have the {} around the string values, - my hunch is that it is to demonstrate that the values are coming from a remote execution - but I know for a fact that S is being truncated due to optimization...
{$O-} // Disable Optimization
var
S: AnsiString;
S2: UnicodeString;
begin
S := 'Hello';
S2 := 'Hello2';
ShowMessage(S2);
end;
{$O+} // Enable Optimization
You'll now note that the value 'Hello' (of variable S) remains intact when debugging. Likewise if you make use of the value assigned to S:
var
S: AnsiString;
S2: UnicodeString;
begin
S := 'Hello';
S2 := 'Hello2';
ShowMessage(S + S2);
end;
Delphi's optimization now identifies that S is being used within its valid scope, and so the value is preserved.
So, what you're calling a "bug" is in fact a "compiler feature" exactly as Borland/Inprise/Codegear/Embarcadero intended.

(I am copying code from #Dave)
var
S1: AnsiString;
S2: UnicodeString;
begin
S1 := 'Foo';
S2 := 'Bar';
ShowMessage(Format('%s!', S2));
end;
I am guessing local var S1 here is optimized because it is not used anywhere, so the value is not relvant anymore.
Try run this on a local machine, can you see S1?

I am not sure if it pertains, but I am aware of System.AnsiStrings containing specialized commands such as "Format", etc... Using something like the following may resolve your issue:
var
S1: AnsiString;
S2: UnicodeString;
begin
S1 := 'Foo';
S2 := 'Bar';
ShowMessage(Format('%s!', S2));
end;
Also there are a few open bugs, just to rule out any of those, what specific versions of the os and tools are you using i.e. Win7 x64 Ultimate etc.?

Project -> Options -> Delphi Compiler -> Linking -> Include remote debug symbols = true

Related

How to know console background and text colors?

* NOT A DUPLICATE * as it refers to C++ and Windows only.
(Currently for Windows, but a solution also for Linux is preferred.)
I have an application that uses TextColor and/or TextBackground to highlight some text. Because a user may alter the console defaults, I would like to be able to read those colors so I can adjust the color of my highlights to not blend with the user's colors.
For example, if the user has selected yellow text, I don't want my highlight to also be yellow, or if the user's background is red I don't want the highlight to be red and disappear.
I checked the RTL.PDF and couldn't find anything. But that manual is really long (2K+ pages), and I may have missed it.
My question: Are there equivalent functions to TextColor and TextBackground that instead of setting the colors, return the currently set values? (Or, if not specific functions, maybe some other method like getting all console parameters that also contains this color information.)
UPDATE:
After being 'pushed' to deal with C++, and go study the Windows API, I went and did all that :)
Luckily, I found an unrelated FreePascal example of a console Windows API call in the Lazarus forum, and putting all this together, I came up with this which I post as I'm sure it will be useful to others:
{$mode fpc}
Unit Console;
Interface
uses Windows, SysUtils;
function GetTextColor: Byte;
function GetTextBackground: Byte;
Implementation
////////////////////////////////////////////////////////////////////////////////
function GetColors: Byte;
var
vHandle :THandle;
vScreenBufInfo :TConsoleScreenBufferInfo;
begin
vHandle := GetStdHandle(STD_OUTPUT_HANDLE);
if vHandle = INVALID_HANDLE_VALUE then RaiseLastOSError;
GetConsoleScreenBufferInfo(vHandle,vScreenBufInfo);
GetColors := vScreenBufInfo.wAttributes;
end;
////////////////////////////////////////////////////////////////////////////////
function GetTextColor: Byte;
begin
GetTextColor := GetColors and $0F;
end;
////////////////////////////////////////////////////////////////////////////////
function GetTextBackground: Byte;
begin
GetTextBackground := GetColors shr 4 and $0F;
end;
////////////////////////////////////////////////////////////////////////////////
begin
//Writeln('TextColor : ',GetTextColor);
//Writeln('BackgroundColor: ',GetTextBackground);
end.
I'm still looking for a Linux solution, however. Anyone?

Why do I need Sharemem in my Delphi dll which only exposes a function with WideString parameters?

I have a dll and a test application written in Delphi. The test application uses multiple threads to call the function exported by the dll. The exported function has a trivial thread safe implementation. When running the test application various errors (access violation, invalid pointer operation, stack overflow etc) happens or the application freezes. In some cases the application finishes without errors.
Note that these errors only happen (surface) when using multiple threads. When calling the function from the main thread only then everything works fine.
I have found that adding ShareMem to both the dll and the application stops all these kind of errors. But I don't understand why. To my knowledge ShareMem is only needed when passing long strings between the dll and the application. As far as I know WideString is not a long string.
Also according to this post ShareMem should not be required:
Why can Delphi DLLs use WideString without using ShareMem?
Here is the source of the dll:
library External;
uses
Winapi.Windows;
type
TMyType = class
private
FText: string;
end;
function DoSomething(input: WideString; out output: WideString): Bool; stdcall;
var
x: TObject;
begin
x := TMyType.Create;
try
output := x.ClassName;
finally
x.Free;
end;
Result := True;
end;
exports
DoSomething;
begin
end.
Here is the test application:
program ConsoleTest;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Winapi.Windows,
OtlParallel;
function DoSomething(input: WideString; out output: WideString): Bool; stdcall; external 'External.dll' name 'DoSomething';
var
sResult: WideString;
begin
try
Parallel.&For(0, 500).Execute(procedure(value: Integer)
var
sResult: WideString;
begin
DoSomething('hhh', sResult);
end);
WriteLn('Done');
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Why ShareMem makes the bugs go away and is there another way to fix these bugs?
I am using Delphi XE2 and OmniThread 3.07.5.
Update
- Same issue when run from a VCL application's button's on click event handler
- If DoSomething uses a critical section inside then runs fine
- If FText field is removed from TMyClass then no errors are reported but the application randomly freezes
For the standard memory manager (FastMM) to support multi threading, you need to set the IsMultiThread flag.
When you use RTL for threading, this flag is automatically set. As revealed in the comments to the question, OTL also use RTL to start its threads. So the memory manager in your executable is aware of threading, but the distinct memory manager in the dll causes errors. When you use "sharemem", there is only one memory manager which is aware of threading because of OTL, so you encounter no errors.
An alternative solution, apart from using a shared memory manager, would be to set the flag also for the memory manager in the dll.

Inno Setup Change AppName based on component(s) selected

I need the installer to show different AppName based on (un)selected components. I tried this:
[Setup]
AppName={code:GetAppName}
AppVersion=1.0
AppVerName=Dagon Video Tools
AppId=Dagon Video Tools
DefaultDirName={sd}\Games\Dagon Video Tools
[Code]
function GetAppName(Value: string): string;
var
CurPageID: Integer;
Begin
Result := 'Dagon Video Tools'
if (CurPageID=wpSelectComponents) and IsComponentSelected('Slasher') and not IsComponentSelected('Frankenstein') then
begin
Result := 'Dagon Slasher';
end;
if (CurPageID=wpSelectComponents) and IsComponentSelected('Frankenstein') and not IsComponentSelected('Slasher') then
begin
Result := 'Dagon Frankenstein';
end;
if (CurPageID=wpSelectComponents) and IsComponentSelected('Slasher') and IsComponentSelected('Frankenstein') then
begin
Result := 'Dagon Video Tools';
end;
End;
But, as you can guess, this doesn't work. Is this script incomplete or should it be done in a different way altogether?
The AppName directive value is resolved (= your GetAppName is called) immediately after the InitializeSetup (if any) finishes. That is a long before the user is able to change the components.
So you cannot make AppName depend on the selected components.
Some uses of the AppName could be overridden with a custom value though, but not all. See below.
Though, as I know that your question is actually about a setup type, you can do this:
Create custom "type" page (like a menu) as the very first one.
Once the user selects the "type", restart the installer with a custom switch (e.g. /APPTYPE=slasher) and exit.
Once the installer is (re-)run with the /APPTYPE, you know from the beginning, what component/type you are installing and hence you can set the AppName normally.
Of course, you skip the custom "type" page.
This is actually a way simpler to implement. The only drawback is that the setup window is "recreated" after the user selects the "type".
This is the original response in case you do not want to use the above solution.
First, your implementation of the GetAppName is wrong. You are using an uninitialized variable CurPageID. And anyway, as mentioned already, the GetAppName is called even before the wizard window is created, so "current page" is irrelevant here.
The correct implementation would be like:
function GetAppName(Value: string): string;
begin
if IsComponentSelected('Slasher') and not IsComponentSelected('Frankenstein') then
begin
Result := 'Dagon Slasher';
end
else
if IsComponentSelected('Frankenstein') and not IsComponentSelected('Slasher') then
begin
Result := 'Dagon Frankenstein';
end
else
begin
Result := 'Dagon Video Tools';
end;
end;
But this still won't make it working in the AppName directive. We will use it in other contexts though later.
Also note that for your specific installer, you should better use the WizardSetupType(false) function instead of the IsComponentSelected.
FinishedLabel
Just override the Inno Setup default text in CurPageChanged(wpFinished):
procedure CurPageChanged(CurPageID: Integer);
var
S: string;
begin
if CurPageID = wpFinished then
begin
S := SetupMessage(msgFinishedHeadingLabel);
StringChange(S, '[name]', GetAppName(''));
WizardForm.FinishedHeadingLabel.Caption := S;
WizardForm.AdjustLabelHeight(WizardForm.FinishedHeadingLabel);
{ Ideally we should shift the FinishedLabel up or down here, }
{ if the height of the header changed. }
{ Note that other messages (msgFinishedLabelNoIcons or msgFinishedRestartLabel) }
{ are used in special situations, so this is not a complete solution. }
S := SetupMessage(msgFinishedLabel);
StringChange(S, '[name]', GetAppName(''));
WizardForm.FinishedLabel.Caption := S;
WizardForm.AdjustLabelHeight(WizardForm.FinishedLabel);
end;
end;
Add/Remove Programs
That's easy. There's the UninstallDisplayName directive for this, which is resolved only during the actual installation, when we already know the selected components. So we can use your (fixed) GetAppName here:
[Setup]
UninstallDisplayName={code:GetAppName}
Are you sure you want to completely remove AppName and all of its components?
You cannot change that. You better use some generic name in the AppName so that this message works for any component.
Or make the message not mention the application name at all:
[Messages]
ConfirmUninstall=Are you sure you want to completely remove this game?
Alternatively remove the message completely:
Replace or customize modal uninstallation windows in Inno Setup
Please wait while AppName is removed from your computer
The same solution as for the WizardForm.FinishedLabel. Just use the UninstallProgressForm.PageDescriptionLabel from the InitializeUninstallProgressForm.
AppName was successfully removed from your computer
Similar as with the "Are you sure you want to completely remove AppName and all of its components?"
Either make the AppName generic. Or disable the message with "silent" mode and implement your own message in the CurUninstallStepChanged(usPostUninstall).
Again, see Replace or customize modal uninstallation windows in Inno Setup.
For a similar discussion, see also Changing AppName and AppDir depending on language in Inno Setup.

Why doesn't TPageProducer remove quotation marks from strings?

I'm trying to debug behaviour that has only appeared when my large app - working fine in XE3 - is run after compiling with XE4. The issue seems to cause some quoted strings (eg "MyString") to retain their quotes even after having been 'de-quoted' by TPageProducer in Web.HTTPProd. For example, consider the code below which is small extract from this Delphi source unit Web.HTTPApp:
procedure ExtractHeaderFields(Separators, _WhiteSpace: TSysCharSet; Content: PChar;
Strings: TStrings; Decode: Boolean; StripQuotes: Boolean = False);
{$ENDIF NEXTGEN}
var
Head, Tail: PChar;
EOS, InQuote, LeadQuote: Boolean;
QuoteChar: Char;
ExtractedField: string;
{$IFNDEF NEXTGEN}
WhiteSpaceWithCRLF: TSysCharSet;
SeparatorsWithCRLF: TSysCharSet;
{$ENDIF !NEXTGEN}
function DoStripQuotes(const S: string): string;
var
I: Integer;
InStripQuote: Boolean;
StripQuoteChar: Char;
begin
Result := S;
InStripQuote := False;
StripQuoteChar := #0;
if StripQuotes then
begin
for I := Result.Length - 1 downto 0 do
if Result.Chars[I].IsInArray(['''', '"']) then
if InStripQuote and (StripQuoteChar = Result.Chars[I]) then
begin
Result.Remove(I, 1);
InStripQuote := False;
end
else if not InStripQuote then
begin
StripQuoteChar := Result.Chars[I];
InStripQuote := True;
Result.Remove(I, 1);
end
end;
end;
I see this called when I use TPageProducer and I can see my good source string go into the ExtractHeaderFields routine above and then into the 'DoStripQuotes' function. Stepping into DoStripQuotes and watching 'Result' shows that it does not change, even when Result.Remove is called (to strip the quote). When I take this 'DoStripQuotes' routine out to a simple test app, it wont compile, telling me that 'Result.anything' is not allowed. I assume then that Result, although it is defined as 'string' must be another type of string in the context of Web.HTTPProd.
So I get to thinking maybe this is something to do with the 'Immutable strings' that I've heard about. I read this SO question about that and although I get the gist, I could do with more practical advice.
Specifically, I would like answers to the following questions:
What type of 'string' is 'Result' if the notation Result.Length is allowed?
Is there a way in which I can tell the compiler to use 'XE3' compatibility for a unit? (THis might allow me to see where the problem is originiating). Ive ttried {$ZEROBASEDSTRINGS ON} / OFF but this seems to cause even more chaos and I don't know what I'm doing!
Thanks for any help.
LATER EDIT: As noted in the accepted answer below this is a bug in the VCL unit Web.HTTPApp.pas which should read "Result := Result.Remove(I,1)" in two places around line 2645 and not "Result.Remove(I,1)"
What type of 'string' is 'Result' if the notation Result.Length is allowed?
It's just the same old string, aliased to UnicodeString, that you've been using since Delphi 2009. The difference is that this code uses the new record helper (specifically SysUtils.TStringHelper). That's what lets you use . notation on a string variable.
Is there a way in which I can tell the compiler to use 'XE3' compatibility for a unit?
No. The code in question is a library unit and it is designed to be compiled in a particular mode. What's more, you can't readily re-compile it unless you take on compiling the RTL/VCL yourself. Even if there was such a mode, it would not help since the code is simply wrong (see below). No amount of mode switching can fix this particular piece of code.
I get to thinking maybe this is something to do with the Immutable strings that I've heard about.
It's not. None of the Delphi compilers have immutable strings yet. The concept of immutable strings is just something that has been floated as a future change. And if the change is made, expect it to be made in the mobile compilers first.
The problem is in fact just a rather simple bug in the code that you posted which has clearly had no testing at all. The use of Remove is wrong. That method does not modify the string in-place. Instead it returns a new string that has the character removed. The code should read:
Result := Result.Remove(I, 1);
The reason that the developer who coded ExtractHeaderFields has made this mistake is that whoever designed the string helper code named the Remove method incorrectly. Since Remove is a verb you would expect it to operate in-place. A method that does not modify the subject, and returns a new instance, as this method does, should be given a name that is a noun. So this method should be named something like Remnants. It looks to me as though the RTL designers copied the .net naming where the same flaw also exists.
You should submit a QC report, if one does not already exist. I know that XE4 update 1 has just been released. It's plausible that it contains a fix.
Your other options, as I see them, are:
Stick with XE3 until XE4 is sufficiently debugged.
Include a copy of the Web.HTTPApp unit in your project and fix the bugs yourself.

Is there any problem to using this code in a Thread ? (Delphi)

i use this code in a thread (through Indy Onexecute event) . is there any problem ?
function TFrmMain.ShellExecute_AndWait(FileName, Params: string): bool;
var
exInfo: TShellExecuteInfo;
Ph: DWORD;
begin
FillChar(exInfo, SizeOf(exInfo), 0);
with exInfo do
begin
cbSize := SizeOf(exInfo);
fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
Wnd := GetActiveWindow();
exInfo.lpVerb := 'open';
exInfo.lpParameters := PChar(Params);
lpFile := PChar(FileName);
nShow := SW_NORMAL;
end;
if ShellExecuteEx(#exInfo) then
Ph := exInfo.hProcess
else
begin
Result := true;
exit;
end;
while WaitForSingleObject(exInfo.hProcess, 50) <> WAIT_OBJECT_0 do
begin
end;
CloseHandle(Ph);
Result := true;
end;
MSDN has this advice:
Because ShellExecuteEx can delegate execution to Shell extensions (data sources, context menu handlers, verb implementations) that are activated using Component Object Model (COM), COM should be initialized before ShellExecuteEx is called. Some Shell extensions require the COM single-threaded apartment (STA) type. In that case, COM should be initialized as shown here:
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
There are instances where ShellExecuteEx does not use one of these types of Shell extension and those instances would not require COM to be initialized at all. Nonetheless, it is good practice to always initalize COM before using this function.
(In Delphi, you'd of course replace the first parameter with nil and use or for the bitwise operation.)
Raymond Chen recently wrote about the consequences of getting this wrong. The specific example was that the function might fail with the Error_Access_Denied error code.
That's the only potential multithreading issue I see in your code. Below are further things that occurred to me when I read your code, although they have nothing to do with multithreading (and not even much to do with Indy).
You have a peculiar way of waiting for the program to stop running. You repeatedly wait for 50 milliseconds at a time, but if the process isn't finished yet, you do nothing but wait again. Describe your intention more accurately by specifying Infinite for the timeout.
The function always returns True. If there's no useful return value, then you should just make it a procedure so there's no return value at all. Don't confuse the caller with useless information. If you're going to keep it as a function, then use the Delphi native type Boolean instead of the Windows compatibility type Bool for the return type.
I'm a little wary about the idea of a server executing user-interactive programs upon receipt of network messages.
Notice when MSDN says you might not get a process handle. There are cases when ShellExecuteEx can service your request without creating a new process, so you'll have nothing to wait on.
The user might end up using the program awhile, and your server will be stuck waiting all that time. I wonder whether it really needs to wait at all. Is the client going to be waiting for a response from the server, too?

Resources