2

The question is related to my previous question:

access violation at address in module ntdll.dll - RtlEnterCriticalSection with TCanvas.Lock

Apparently there is a bug in Delphi's code (see QC 64898: Access violation in FreeDeviceContexts). This bug goes all the way until D2010, AFAIK.

The suggested workaround worked fine so far. Now I have a dilemma.

I don't like the idea of using a private copy of Controls.pas in my project - I'm not sure it is safe. The Controls unit is a very low level unit, and I really feel it's a drastic move, considering that my huge application works fine, except for the mentioned problem. I'm also not sure if/how to rebuild all components/units that rely on the Controls unit in my project.

Is it possible to patch TControlCanvas.CreateHandle(), which uses an internal CanvasList and private members?

NOTE: I will be using the patch for this project only (Delphi 5). I don't mind hard-coding the offsets. AFAIK, patching privates always uses hard-coded offsets, based on the compiler version. I might be able to deal with privates myself (without class helpers), but I have no clue how to handle CanvasList and FreeDeviceContext(), which are declared in the implementation section of the Controls unit.

Community
  • 1
  • 1
zig
  • 4,524
  • 1
  • 24
  • 68
  • 1
    Not easy in D5. How will you access the privates? Or `CanvasList`? No helpers to crack the privates. And no easy way to get at `CanvasList`. It could be done with some tricksy reverse engineering. But pointless. Use your own `Controls` unit and it's fine. There's no problem with that. What would not be safe? – David Heffernan Dec 07 '16 at 15:49
  • Although there are no "class helpers" in D5, you can still use an overlay class to (relatively) easily access the private members for any class for which you have the source (http://www.deltics.co.nz/blog/posts/825). Whether that can help or is the right thing to do in this case is a separate matter. A fixed (customized) *Controls* unit is probably the way to go. – Deltics Dec 07 '16 at 19:24
  • @Deltics Yes you can do that. Not massively convenient. How do you get the address of the global variable defined in that unit though. – David Heffernan Dec 07 '16 at 19:29
  • Yep, there are other issues in the specifics in this case, just addressing the notion that without class helpers there is no way to fumble with a *classes* privates. oo-er missus. :). In this case, there is also an implementation variable involved, but even formalised class helpers can't help with that, so the lack of them or not is irrelevant. In this case, a patched copy of the entire unit is the simplest and arguably safest solution. – Deltics Dec 07 '16 at 19:46
  • @Deltics well, I didn't say that. I just said it wasn't easy. You seem to agree with me. – David Heffernan Dec 07 '16 at 20:10
  • @DavidHeffernan - I shall refer you to your original comment. The sentence "*No helpers to crack the privates.*" was *not* qualified and came after asking rhetorically "*How will you access the privates ?*". The "no easy way" qualifier was applied *specifically*, to the `CanvasList` variable. So: No. Happily however, it does appear that *you* are now agreeing with me. ;) – Deltics Dec 07 '16 at 20:29
  • I said it wasnt easy. You have to hard code the offsets, or use your overlay technique. But that only gets you access to the members. How are you going to call methods? Comical that you claim to have been saying this all along. I saw your original comment remember, before you edited it. Your answer reaches the exact same conclusion as my comment. – David Heffernan Dec 07 '16 at 20:33
  • @DavidHeffernan, wouldn't I need to rebuild all components that are related to my project? `Controls` is a very low level unit and I really feel it's a drastic move, considering that my huge application works fine, except for the mentioned problem. in any case I am interested to know if such patch is doable. I know it's not easy. I will need it only for D5. so using constant offsets is fine with me! – zig Dec 08 '16 at 09:42
  • In my view by far the simplest and cleanest and safest approach is to use your own `Controls` unit. You won't need to rebuild anything, at least assuming that you don't use runtime packages. Runtime packages would change changes. But if you don't use runtime packages, you change `Controls` in the implementation section only, and all other code that uses it picks up your version instead of the defective Borland version. – David Heffernan Dec 08 '16 at 11:13
  • I also remember (99% sure) seeing such patch somewhere on SO. but I can't find it! – zig Dec 08 '16 at 11:13
  • Do it that way if you insist. You asked for our opinion. I've been using replacement units for years and years. But if you don't want to do that, then do it your way. – David Heffernan Dec 08 '16 at 11:15
  • @DavidHeffernan, I don't use runtime packages thank god! I usually delete all my DCU before I rebuild my project. will Delphi rebuild all the RTL based on the private Controls? maybe I fail to understand this point... – zig Dec 08 '16 at 11:16
  • 1
    It doesn't need to rebuild the whole of the RTL. Because you only change the implementation of the unit, the RTL does not need to be rebuilt. The interface section of `Controls` is unchanged, and the linker just links to your unit instead of the Borland unit. – David Heffernan Dec 08 '16 at 11:18
  • 1
    @DavidHeffernan, I might choose to do it your way. I don't insist. my question was and is: `Is it possible to patch TControlCanvas.CreateHandle?` I'm just curious if it's doable. that's all. – zig Dec 08 '16 at 11:18
  • @DavidHeffernan, so the key here is the fact that the change is in the implementation? what would have happened if I changed interface section? will Delphi somehow rebuild it differently? – zig Dec 08 '16 at 11:19
  • Yes it is possible to do it your way. I'm quite sure I could do it. However, I cannot be bother to work it out for you because it's not something I personally would ever do. As I said, I've been replacing defective modules for years now. It's a respectable and well understood technique. It's not as if it's ever going to stop working since your compiler is fixed. – David Heffernan Dec 08 '16 at 11:19
  • 2
    If you change the interface then the compiler objects. It tells you that the units that use `Controls` were compiled against a different version of the unit. Try it and see. Add a do nothing method to `TControl`. – David Heffernan Dec 08 '16 at 11:20
  • @DavidHeffernan, I see! you are correct about the interface change, and I never was aware of this, and that is the **key**. If you can make your comments an answer I'll accept! BTW: `"Yes it is possible to do it you way. I'm quite sure I could do it"`. - if I'll ask a new question "How to access a variable/function in the implementation section of RTL unit?" will you answer it? :) – zig Dec 08 '16 at 12:29
  • You can accept Deltics answer I think. He and I are on the same page. – David Heffernan Dec 08 '16 at 13:19

1 Answers1

2

As discussed in the comments, it is possible to access the private and protected members of classes, even in older versions of Delphi without "class helpers".

However, the problem in this case revolves around the details of a particular method implementation, not just being able to access or modify private member variables. Further, the implementation of a particular method which makes use of an implementation variable in the unit involved. Specifically the CanvasList variable that you have noted.

Even with the benefit of class helpers, there is no simple way to access that implementation variable.

Your current solution is the simplest and safest approach: Using a copy of the entire unit with a modification applied to the specific method required to solve the issue.

Rest assured, this is not an uncommon practise. :)

Your only problem with this approach is to be sure to manage the fact that you are relying on this "privatised" copy of the unit when standing up new development environments or upgrading to new versions of the IDE.

In the case of new development environments, careful project configuration should take care of things (and of course, your modified Controls.pas unit is part of your version controlled project).

In the case of upgrading to newer Delphi versions, you simply have to remember to revisit the modified Controls unit in each new version, updating the private copy in your project and re-applying the modifications you have made as appropriate. In most if not all cases this should be straightforward.

But I Really Want to Access the CanvasList Variable

As I say above, there is no simple way to access the implementation variable used in that unit (which will be necessary if you were to somehow contrive to "patch" the code at runtime, rather than replacing it with a modified copy at compile time).

But that implies that there is a **non-**simple way. And there is.

Like any data in your application, that variable resides at some memory address in your process. It's only the compiler scoping rules which prevent you from addressing it directly in source. There is nothing stopping you figuring out how to find that location at runtime and addressing that memory location via a pointer as you would any other "raw" memory address to which you have access.

I don't have a worked up demonstration of how to do that and strongly recommend that trying to implement such a solution is a waste of time and effort, given that an easier solution exists (copying and modifying the unit).

Apart from anything else, depending upon how reliable the method is for determining the memory location involved, direct access to that memory location could prove potentially vulnerable not only to differences between compiler versions but even to changes arising from compiler settings.

In terms of the end result, it is no better than copying the unit but is certainly far harder and far less reliable.

Deltics
  • 22,162
  • 2
  • 42
  • 70
  • 1
    "there is no **simple** way to access that implementation variable" - is there an way? if yes, then how? – zig Dec 08 '16 at 11:10
  • I extended the answer to outline the "how". This isn't a demonstration of working code (I don't have the time), but an outline of the approach you would need to take (and the reasons why you really don't want to go there). Stick to using a copy of the unit. :) – Deltics Dec 08 '16 at 19:47
  • 1
    The most robust way to do it is to disassemble code at runtime to find the address. But it's a mugs game – David Heffernan Dec 09 '16 at 06:12