I need to patch a method in Classes.pas
(TReader.ReadString - I want to force it to use a specified codepage, not the system default).
If I copy Classes.pas into my project,I will end up having to rebuild the entire VCL. Is there any (easy) way to patch a method at runtime?
Modifying the implementation side of Classes.pas will not require recompiling everything. Delphi figures out if a unit needs to be recompiled by an algorithm that looks roughly like this:
If DCU found:
Is DCU format out of date (old version of compiler)? If so, need source to recompile or compile-time error.
Is the source on the path? If so, if it's newer than the DCU, recompile
For each used unit:
Repeat analysis when loading
For each used symbol ("import": type, variable, routine, initialized constant etc.) from that unit:
Is symbol version of import different to symbol found in used unit? If so, recompile needed.
If DCU is not found, source will need to be found and compiled, otherwise compile-time error
The important concept is that of symbol version. When saving a DCU, Delphi calculates a hash based on the interface declaration of the symbol and associates it with the symbol. Other units that use the symbol also store the symbol version. In this way, link-time conflicts caused by stale symbols are avoided, unlike most C linkers.
The upshot of this is that you should be able to add Classes.pas to your project and modify its implementation section almost to your heart's content, and still be able to statically link with the rest of the RTL and VCL and third-party libraries, even those provided in object format only.
Things to be careful of:
Inlined routines; the body of inlined routines are part of the symbol version
Generics; the implementation side of generic types and methods are part of the respective symbol versions
I found VCLFixPack:
https://www.idefixpack.de/blog/bugfix-units/vclfixpack-10/
I used the techniques from this to replace the method I wanted to patch at runtime.
Related
I want to define a precompile string variable and use it in {$include} directive in delphi, for example:
{$define FILE_NAME "lockfile"}
{$include FILE_NAME'.txt.1'}
{$include FILE_NAME'.txt.2'}
...
For security reasons (this is part of our licensing system), we don't want to use normal strings and file reading functions. Is there any capability for this purpose in Delphi?
The $INCLUDE directive does not support indirection on the file name. So, the following code:
const
someconst = 'foo';
{$INCLUDE someconst}
leads to the following error:
F1026 File not found: 'someconst.pas'
If you must use an include file, you must apply the indirection by some other means. One way could be to use the fact that the compiler will search for the included file by looking on the search path. So, if you place each client specific include file in a different directory, then you can add the client specific directory to the search path as part of your build process.
FWIW, I find it hard to believe that this will make your program more immune to hacking. I think that a more likely outcome is that your program will be just as susceptible to hacking, but that it will become much more difficult and error prone for you to build and distribute the program.
You requirement may be better satisfied by the proper use of a VCS system. You need "branches" for every customer where customer-specific files contains customer-specific data. This will avoid to litter your code with complex directive to manage each customer - file names stays the same, just their content is different in each branch. Adding a new customer just requires to create a new branch and update files there.
Then you just need get each branch and compile it for each customer to get the final executable(s) with customer specific data built in.
Where I work we're still using Delphi 2009. I happened to be looking at the Forms unit in the VCL and stumbled upon:
[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
function DisableTaskWindows(ActiveWindow: HWnd): TTaskWindowList;
This attribute is clearly the CLR class UIPermissionAttribute but unlike other references to the CLR this attribute is not wrapped in conditional compilation directives.
This surprised me because, AFAIK, in Delphi Win32 versions prior to 2010 brackets were only used for index notation in arrays and collection types, defining sets and assigning GUIDs to interfaces. This doesn't appear to be the case.
I did a regex search and found dozens of examples throughout the RTL/VCL. Some were attributes on types and some on methods.
Are these simply ignored by the compiler or do they serve some purpose in Win32?
I also found syntax that looked like:
[!UnitName]
[!InterfaceName]
Which appears to have something to do with generating source files from a template in an IDE wizard but these weren't in the RTL source folder. They were in the object repository folder.
I had hoped that perhaps attributes were an undocumented feature similar to how class helpers were available for years before they were documented but that doesn't appear to be the case.
I tried a simple test and added attribute notation before a class definition. The compiler didn't choke on it but it did issue a warning that custom attributes aren't supported.
When I use Delphi directives in code, like:
{$IFDEF something}
.
.
.
{$ENDIF}
Where do I assign the word 'something' in the project? I tried in some places in project options but it didn't work. Guess I didn't find the correct one.
It's in the Conditional Defines slot under Project | Options, which looks like this on D2010:
Other answers have pointed you at the places to define symbols and the scope implications of the different approaches.
However, what no-one has yet mentioned is that if you change the DEFINE symbols you MUST do a FULL BUILD of your project for them to have any effect on your code.
When you "Compile" the Delphi compiler will only compile units which have themselves changed since the previous compile. If you change DEFINE symbols this doesn't change any project units, so if the units are not re-compiled then the change in DEFINE symbols will not have ANY effect in those units.
To FORCE changes in DEFINE symbols to be applied in ALL units, you MUST "build", not compile.
This may explain why your attempt to set defines did not appear to work previously
You can also define them in {$DEFINE <symbol>} directives. What changes is the scope. When you define a <symbol> under conditional defines in the project options, the scope is global to the whole project. $DEFINE directives are valid only from the point they are declared to the end of the current module, or until an $UNDEF directive using the same <symbol> is encountered. What to use depends on your needs, and what the IFDEF does.
There are two places where you can put conditional defines that are used in all units of a project:
in the project options (as David Heffernan already said)
in an include file that is included in all of these units
Why do I mention the second option? Because it allows specialized processing based on the VERxxx conditional define and other conditional defines given in 1. See jedi.inc (from the Jedi JCL) for an example.
Also, as Deltics said: When it determines which units to recompile, the compiler only checks whether the unit itself has changed, not whether the conditional defines or any include files have changed. So if you change conditional defines, you must do a rebuild, not just a recompile. Since the Delphi compiler is very fast, this fortunately does not make much of a difference for compile times.
You can define global symbols in external file with .inc extension.
Create a new text file, put in it all you defines and save it as for instance Predefines.inc:
--- Content of the file Predefines.inc ---
{$DEFINE Symbol}
{$IFDEF Symbol}
{$DEFINE AnotherSymbol}
{$ENDIF}
In you Delphi modules, where you need to check are symbols defined, put this code in interface section:
interface
{$I Predefines.inc}
uses ...
// Check you defines
{$IFDEF Symbol}
...
{$ENDIF}
Is it possible to unmangle names like these in Delphi?
If so, where do I get more information?
Example of an error message where it cannot find a certain entry in the dbrtl100.bpl
I want to know which exact function it cannot find (unit, class, name, parameters, etc).
---------------------------
myApp.exe - Entry Point Not Found
---------------------------
The procedure entry point #Dbcommon#GetTableNameFromSQLEx$qqrx17System#WideString25Dbcommon#IDENTIFIEROption could not be located in the dynamic link library dbrtl100.bpl.
---------------------------
OK
---------------------------
I know it is the method GetTableNameFromSQLEx in the Dbcommon unit (I have Delphi with the RTL/VCL sources), but sometimes I bump into apps where not all code is available for (yes, clients should always buy all the source code for 3rd party stuff, but sometimes they don't).
But say this is an example for which I do not have the code, or only the interface files (BDE.INT anyone?)
What parameters does it have (i.e. which potential overload)?
What return type does it have?
Is this mangling the same for any Delphi version?
--jeroen
Edit 1:
Thanks to Rob Kennedy: tdump -e dbrtl100.bpl does the trick. No need for -um at all:
C:\WINDOWS\system32>tdump -e dbrtl100.bpl | grep GetTableNameFromSQLEx
File STDIN:
00026050 1385 04AC __fastcall Dbcommon::GetTableNameFromSQLEx(const System::WideString, Dbcommon::IDENTIFIEROption)
Edit 2:
Thanks to TOndrej who found this German EDN article (English Google Translation).
That article describes the format pretty accurately, and it should be possible to create some Delphi code to unmangle this.
Pitty that the website the author mentions (and the email) are now dead, but good to know this info.
--jeroen
There is no function provided with Delphi that will unmangle function names, and I'm not aware of it being documented anywhere. Delphi in a Nutshell mentions that the "tdump" utility has a -um switch to make it unmangle symbols it finds. I've never tried it.
tdump -um -e dbrtl100.bpl
If that doesn't work, then it doesn't look like a very complicated scheme to unmangle yourself. Evidently, the name starts with "#" and is followed by the unit name and function name, separated by another "#" sign. That function name is followed by "$qqrx" and then the parameter types.
The parameter types are encoded using the character count of the type name followed by the same "#"-delimited format from before.
The "$" is necessary to mark the end of the function name and the start of the parameter types. The remaining mystery is the "qqrx" part. That's revealed by the article Tondrej found. The "qqr" indicates the calling convention, which in this case is register, a.k.a. fastcall. The "x" applies to the parameter and means that it's constant.
The return type doesn't need to be encoded in the mangled function name because overloading doesn't consider return types anyway.
Also see this article (in German).
I guess the mangling is probably backward-compatible, and new mangling schemes are introduced in later Delphi versions for new language features.
If you have C++Builder, check out $(BDS)\source\cpprtl\Source\misc\unmangle.c - it contains the source code for the unmangling mechanism used by TDUMP, the debugger and the linker. (C++Builder and Delphi use the same mangling scheme.)
From the Delphi 2007 source files:
function GetTableNameFromSQLEx(const SQL: WideString; IdOption: IDENTIFIEROption): WideString;
This seems to be the same version, since I also have the same .BPL in my Windows\System32 folder.
Source can be found in [Program Files folders]\CodeGear\RAD Studio\5.0\source\Win32\db
Borland/Codegear/Embarcadero has used this encoding for a while now and never gave many details about the .BPL format. I've never been very interested in them since I hate using runtime libraries in my projects. I prefer to compile them into my projects, although this will result in much bigger executables.
I have a set of compiled Delphi dcu files, without source. Is there a way to determine what types are defined inside that dcu?
To find out what's in a unit named FooUnit, type the following in your editor:
unit Test;
interface
uses FooUnit;
var
x: FooUnit.
Press Ctrl+Space at the end, and the IDE will present a list of possible completion values, which should consist primarily, if not exclusively, of type names.
You could have a look at DCU32INT, a Delphi DCU decompiler. It generates an .int file that is somehow readable but not compilable, but if you only want to determine the types defined, this could be enough.
The DCU format is undocumented, last I checked. However, there is a tool I found that might give you some basic info called DCUtoPAS. It's not very well rated on the site, but it might at least extract the types for you. There is also DCU32INT, which might help as well.
Otherwise, you might just have to open the file with a hex editor and dig around for strings.