How to keep the generated TLB file neat? - delphi

Hi I use interop to call C# code in Delphi.
C# code has a binary and in Delphi 5 Menu: Project-->Import Type Library
Click Add to add the tlb file: XXXX.tlb
Unit dir name: input the path where the delphi XXX_TLB.pas file is generated.
If the C# dll, tlb and delphi XXX_TLB.pas has already been there. Now I add one more function in C# code and hope Delphi can call this function as well. I need to recompile c# and regenerate delphi XXX_TLB.pas file. But by following the above steps, I see the newly generated XXX_TLB.pas includes my newly added function, but looks like the functions order in XXX_TLB.pas is totally different from before.
For example, in my C# binary I have function:
func1();
func2();
func3();
func4();//newly added
In the old XXX_TLB.pas, the function order is:
func1();
func2();
func3();
Now XXX_TLB.pas is like this:
func1();
func3();
func4();
func2();
The XXX_TLB.pas can still be used, looks like no functionality difference, but when I check in to tfs, I see it changes a lot from pervious version. Is there a way to keep this new XXX_TLB.pas the same order as before but add my function as well? How to do that? Thanks!

You cannot hope to keep the changes to a minimum unless you start writing the .pas file yourself. That sounds like a worse option.
Probably what is happening is that you have changed version of either one of your compilers since the last time you imported. Otherwise one would expect minimal differences.
Just check it in with a comment stating which versions of compiler and .ocx/.dll were used.

You can't, as far as I know.
I most cases, I'm able to consider them as a sort of "black box" anyway, and only occasionally have to peek in them to find a specific declaration.

Related

Delphi - unmangle names in BPL's

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.

*Sometimes* get an error when assigning to a constant in Delphi

I am using Delphi 2007 with all patches and updates.
I have a file which is used by two different projects. In that file is a procedure, which I will simplify as follows:
procedure MyProcedure;
const
UniqueValue: integer = 0;
begin
//some code
Inc(UniqueValue);
//some more code
end;
The Inc() command should fail, because you cannot assign to a constant. In one project, I get an error to that effect (I'll call that project "Accurate"). In the other project, I don't get any errors (I'll call it "Bogus"). I also don't get any warnings. I can't figure out why the compiler lets this incorrect code through.
Here's what I've tried in project Bogus:
1 - Introduce an obvious error, like typing "slkdjflskdjf" in the middle of a line
Result: I get an error,which proves that it is really trying to compile this file.
2 - Delete the .DCU, and rebuild the project
Result: The .DCU is re-generated, again proving that the project is truly compiling this erroneous code.
Does anyone have thoughts on why this behavior would occur? And more specifically, why it would occur in one project but not another? Is there some obscure compiler option to permit assigning to constants?
One final note: Both projects are converted from Delphi 5. Under Delphi 5 with similar code, they both compile fine.
Edit: Thanks for all your help. After changing the assignable typed constants directive, I can get consistent behavior across both projects. I learned something new today...
There is an option for this called "Assignable typed constants" in Compiler Options. It can also be enabled with "{$J+}" in your code.
Could it be that those projects differ in the setting of the $J compiler directive?
I'd suggest to check the Project Options, section Compiler and see if you can spot any differences. Maybe you have option Assignable typed constants enabled in the Bogus project.
As others have said it's almost certainly the {$J+} directive.
If you have a diff tool you can compare the project files to see how they differ - they're just text files - to solve similar problems in the future.

wintypes.dcu not found and code completion stops working

I'm getting the error: wintypes.dcu not found several times a day in the Delphi 2009 IDE, after this error code completion stops working, also I can't open any unit's source code with Ctrl + Click, then I have to reopen the IDE to fix it.
Anyone has a clue about what can be causing this?
The only IDE extension I have installed is GExperts.
wintypes.pas and winprocs.pas has been replaced with windows.pas (since a long time). You should use Unit Aliases to replace all wintypes with windows.
You must be still using the ancient (pre-Delphi 2) names for what is now the Windows unit. I recommend that you search and replace all your uses lists and replace WinTypes and WinProcs with Windows.
Alternatively, you can make sure you have "WinTypes=Windows" and "WinProcs=Windows" in the Unit Aliases section of your project options, but still, after all these decades, I'd move on to the 32-bit world! (16-bit being the prime limitation that meant WinTypes and WinProcs were two separate units.)
Open Project - Options, Delphi Compiler ,into Unit aliases insert:
WinTypes=Windows;WinProcs=Windows;DbiProcs=BDE;DbiTypes=BDE;DbiErrs=BDE
Do you use the Decision Cube component? So look for references on the unit MXQEDCOM.pas in your sources.
On BDS 2006, is the only reference for Wintypes and Winprocs I found.
By the way, make sure that the Unit Aliases included the values that Barry Kelly wrote.
Why wintypes.dcu cannot be found any longer, I do not know. But the other two are obvious follow up errors: if Delphi cannot compile the code due to syntax errors (and a missing file is considered a syntax error), it stops code completion and cannot locate source code any longer using Ctrl + Click.

How to tell what types are defined in a Delphi DCU?

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.

Replace function units

I am writing a unit test infrastructure for a large Delphi code base. I would like to link calls to pure functions in SysUtils.FileExists for example to a "MockSysUtils.FileExists" instead.
Creating a SysUtils unit with the same interface is not appreciated by the compiler.
What I am thinking of is to hook in my mock function at runtime. Is this possible nowadays?
Any other suggestions?
Regards,
Peter
Replacing a function at runtime is difficult but usually technically possible. "All" you need to do is:
take the address of the function in question
disassemble the first 5 bytes or so (to check for a RET instruction - very small routines may abut another routine, preventing you from replacing it)
change its page protection (with VirtualProtect) to be writable
rewrite the first 5 bytes with a JMP rel32 instruction (i.e. E9 <offset-to-your-func>)
implement your version function as normal, making sure it has the same arguments and calling convention as the function you are mocking
An easier approach would be to link against a different version of SysUtils.pas. That will require you to also recompile all the units in the RTL and VCL that depend on SysUtils.pas, but it is likely quite a bit easier than the function intrumentation approach described above.
The easiest approach is the language-level one, where either you don't directly rely on SysUtils at all (and so can switch at a higher level), or you modify the uses declaration to conditionally refer to a different unit.
You can do it with MadCodeHook. Use the HookCode function, give it the address of the function you want to replace and the address of the function you want to be called instead. It will give you back a function pointer that you can use for calling the original and for unhooking afterward. In essence, it implements the middle three steps of Barry's description.
I think MadCodeHook is free for personal use. If you're looking for something freer than that, you can try to find an old version of the Tnt Unicode controls. It used the same hooking technique to inject Unicode support into some of the VCL's code. You'll need an old version because more recent releases aren't free anymore. Find the OverwriteProcedure function in TntSystem.pas, which is also where you'll find examples of how to use it.
Code-hooking is nice because it doesn't require you to recompile the RTL and VCL, and it doesn't involve conditional compilation to control which functions are in scope. You can hook the code from your unit-test setup procedure, and the original code will never know the difference. It will think it's calling the original FileExists function (because it is), but when it gets there, it will immediately jump to your mocked version instead.
You could also just add a unit that only contains the functions you want to mock to the test unit's uses clause. Delphi will always use the function from the unit that is listed last. Unfortunately this would require you to change the unit you want to test.
Your Mock-Sysutils unit:
unit MockSysutils;
interface
function FileExists(...) ...
...
end.
Your unit, you want to test:
unit UnitTotest;
interface
uses
Sysutils,
MockSysUtils;
...
if FileExists(...) then
FileExists will now call the version from MockSysutils rather than from Sysutils.
Thanks,
yes, it would be great to have TSysUtils class for example instead that I could inherit with my MockSysUtils. But, that is not the case and the code base huge. It will be replaced bit by bit, but I wondered if there was a quick-start solution.
The first approach is ok for one function perhaps, but not in this case I guess.
I will go for the second approach.
This is slightly way out there but here is another alternative.
When building your unit tests and your main codebase to go with it, you could grep all the functions you wish to replace and specify the unit to use
Instead of
fileexists(MyFilename);
you could grep fileexists and replace with
MockTests.fileexists(MyFileName);
If you did this at build time (using automated build tools) it could easily be done and would provide you with the greatest flexibility. You could simply have a config file that listed all the functions to be replaced.

Resources