Notifying about events from dll to main app - delphi

I am developing application which intend to be cross platform. I used to use Windows Messages but now I am dropping it out. I replaced messages with callbacks but regardless I can use different technologies I am not aware of different possibilites when not using windows messages.
Well I have main exe aplication and some dll plugins. I have some objects and threads in dll and I would like to notify main application about some changes that DLL made to data structure.
As I said I am currently working with some callbacks. To provide compatibility with different languages (C++, VB, C#) I have non-object type of callback. I am not sure if other languages supports callback of object.
So my questions are:
What are the alternatives (cross-platform) to windows messages? Can callbacks replace messages?
Do other languages support callback of object?
I guess other languages have different technologies as alternative to messages?

You can certainly use callback functions instead of messages. You can't use callback methods because only Delphi and C++ Builder understand how to invoke Delphi method pointers. However, you can use callback objects with any language that supports COM. Here's an example for a plug-in to notify the application that the data structure has changed:
Define an interface.
type
IDataStructureChanged = interface
['{GUID}']
procedure Call; stdcall;
end;
You could add some parameters to the method so the plug-in can tell how the data structure changed, or pass some value indicating which plug-in is making the notification.
Implement it in the application.
type
TDataStructureChangedListener = class(TInterfacedObject, IDataStructureChanged)
private
FForm: TForm;
procedure Call; stdcall;
public
constructor Create(Form: TForm);
end;
When you instantiate that class, you can pass it a reference to your program's main form, or whatever other information your program will need to be able to take action when a plug-in eventually calls the Call method. Implement Call to make your application do whatever it needs to do when a data structure changes.
Pass a reference to each of the plug-ins when you initialize them.
ChangeListener := TDataStructureChangedListener.Create(Self);
for i := 0 to Pred(PlugIns.Count) do
PlugIns[i].Init(ChangeListener);
The plug-in should store a reference to the listener object, and when the data structure changes, it can call the Call method to notify your application.
What I've described here is what's generally known as an event sink. You can have more than one in your program. If there are multiple events to handle, you could have a separate interface for each kind of event, or you could group them all into a single interface and have a different method for each event. You could have a different sink object for each plug-in, or you could give each plug-in a reference to the same sink object, and then pass a plug-in-ID parameter.

I would definately use callbacks. The main app could give a callback function to the DLL to call when needed, and then the callback function itself can send window messages to the app if it needs to.

I agree with Remy, (!). A straightforward callback allows the handler to implement any kind of further communication it chooses - it might post a message, it may push a parameter onto a queue, whatever it wants. If you want to be cross-platform, you are going to have to resort to passing in, and out, simple types. It's usual to pass in a 'user context' pointer when callbacks are set up. The callback passes this pointer into the handler. This allows callers to pass in a context object as a pointer/int and to recover it in the handler, (by casting the pointer/int back to an object). The handler can then call methods on the context, no matter whether it's Delphi, C++ etc.

So my questions are:
What are the alternatives (cross-platform) to windows messages? Can callbacks replace messages?
Yes you can replace messages with callbacks.
Do other languages support callback of object?
You shouldn't use object methods as callbacks. Common practice in portable code is use of handles (notify calling convention):
DLL source:
type
THandle = LongWord;
{$IF SizeOf(THandle) < SizeOf(Pointer))}
{$MESSAGE Error 'Invallid handle type'}
{$ENDIF}
TCallback = procedure(const aHandle: THandle); cdecl;
var
gCallback: record
Routine: TCallback;
Obj: TObject;
Info: string
end;
function Object2Handle(const aObj: TObject): THandle;
begin
Result:= THandle(Pointer(aObj))
end;
function Handle2Object(const aHandle: THandle; out aObj: TObject): Boolean;
begin
if gCallback.Obj <> nil then
if aHandle = Object2Handle(gCallback.Obj) then
begin
aObj:= gCallback.Obj;
Result:= true;
Exit // WARRNING: program flow disorder
end;
aObj:= nil;
Result:= false
end;
procedure DoCallback();
begin
if Assigned(gCallback.Routine) then
gCallback.Routine(Object2Handle(gCallback.Obj))
end;
procedure SetupCallback(const aCallback: TCallback); cdecl;
begin
gCallback.Routine:= aCallback;
end;
procedure DoSomething(const aHandle: THandle; out aInfo: string); cdecl;
var
O: TObject;
begin
if Handle2Object(aHandle, O) then
aInfo:= Format('%s class object %s', [O.ClassName(), gCallback.Info])
end;
procedure Test();
begin
gCallback.Obj:= TStream.Create();
try
gCallback.Info:= 'created';
DoCallback();
finally
FreeAndNil(gCallback.Obj)
end;
gCallback.Obj:= TMemoryStream.Create();
try
gCallback.Info:= 'will be freed';
DoCallback();
finally
FreeAndNil(gCallback.Obj)
end
end;
exports
SetupCallback,
DoSomething,
Test;
Executable source:
procedure Cb(const aHandle: THandle); cdecl;
const
STUPID: THandle = 1;
EQUALLY_STUPID = $DEAD;
var
S: string;
begin
DoSomething(STUPID, S);
DoSomething(aHandle, S);
DoSomething(EQUALLY_STUPID, S)
end;
begin
SetupCallback(#Cb);
Test()
end.
Edited: You can't shoot yourself in you leg now.
I guess other languages have different technologies as alternative to messages?
OS have a few message alternatives. However not many truly portable.
You can also use:
sockets,
(IMO too big in this case?) ready messaging system (my favorite 0MQ)

Related

Delphi - Making asynchronous calls to WinRT API using TTask.Future

Related to this question, I am trying to implement a procedure that uses the WinRT API to set the desktop wallpaper. To mimic the await functionality in C#, I am using TTask.Future (link) as outlined here and here.
My implementation looks like this:
class procedure TUtilityWin.SetWallpaper(AFileName: String);
var
lStorageFile: IStorageFile;
liao_storagefile: IAsyncOperation_1__IStorageFile;
lFutureTask: IFuture<IAsyncOperation_1__IStorageFile>;
begin
//WinRT Implementation
if TUserProfile_UserProfilePersonalizationSettings.IsSupported then
begin
lFutureTask:=TTask.Future<IAsyncOperation_1__IStorageFile>(
function: IAsyncOperation_1__IStorageFile
begin
Result:=TStorageFile.GetFileFromPathAsync(HSTRING(AFileName));
end);
liao_storagefile:=lFutureTask.Value;
lStorageFile:=liao_storagefile.GetResults;
TUserProfile_UserProfilePersonalizationSettings.Current.TrySetWallpaperImageAsync(lStorageFile);
end;
end;
Per my understanding, when I try to get lFutureTask.Value, the application suspends the current thread until lFutureTask is completed (if it is not already) and then provides the value. However, when I run the application, I get the error message: EOleException with message 'A method was called at an unexpected time'. The break is on this line: lStorageFile:=liao_storagefile.GetResults;
I am new to TTask as well as the WinRT API - so I am certain I am missing something very basic here. Would appreciate any pointers on what would be causing this or what I could be doing differently to fix this. Thanks in advance.
I took a look at the Delphi docs linked in your question, and AFAICT both ITask and IFuture only represent methods that execute on a separate thread (what I call "Delegate Tasks"). There doesn't appear to be any support for asynchronous tasks (what I call "Promise Tasks"). IAsyncOperation<T> is a WinRT type representing an asynchronous task, hence the problems you're having. In particular, there doesn't appear to be any Delphi support for registering continuations onto their "task"s.
So, unless there's some Delphi support for a non-threaded Future/Promise, you're going to have to block a thread.
Currently, your code is spinning up a threaded task that only starts the asynchronous operation (GetFileFromPathAsync). The threaded task is not waiting for the asynchronous operation to complete (IAsyncOperation<T>.Completed), so that task completes immediately after starting the operation, and then the outer code calls GetResult when the operation doesn't have a result yet, causing the exception.
So, to fix this, you'll need a way to have the threaded task block until the asynchronous operation completes. Since WinRT types are purely asynchronous (with no support for synchrony), and since the Delphi types are purely synchronous (with no support for asynchrony), you'll have to bridge it yourself. The best solution is probably the Delphi equivalent of a ManualResetEvent.
Something like this should work:
class procedure TUtilityWin.SetWallpaper(AFileName: String);
var
lStorageFile: IStorageFile;
lFutureTask: IFuture<IStorageFile>;
begin
if TUserProfile_UserProfilePersonalizationSettings.IsSupported then
begin
lFutureTask:=TTask.Future<IStorageFile>(
function: IStorageFile
var
liao_storagefile: IAsyncOperation_1__IStorageFile;
mre: ManualResetEvent;
result: IStorageFile;
begin
mre:= // Create ManualResetEvent
liao_storagefile:=TStorageFile.GetFileFromPathAsync(HSTRING(AFileName));
liao_storagefile.Completed... // Add handler that will set `result` and then set the ManualResetEvent
mre.Wait(); // Wait for the ManualResetEvent to be set
Result:=result;
end);
liao_storagefile:=lFutureTask.Value;
lStorageFile:=liao_storagefile.GetResults;
TUserProfile_UserProfilePersonalizationSettings.Current.TrySetWallpaperImageAsync(lStorageFile);
end;
end;
Sorry, I don't know Delphi, but hopefully this will give you a general direction.
The following (handling an asynchronous call to WebAuthenticationCoreManager.FindAccountProviderAsync) works for me, even though it is as ugly as it gets compared to async/await in .Net:
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Winapi.Winrt,
System.Win.WinRT,
Winapi.Security.Credentials,
Winapi.Security,
Winapi.Foundation.Types
...
type
TCompleteOperationCompleted_IWebAccountProvider = reference to procedure (Value : IWebAccountProvider);
TCompleteOperationError_IWebAccountProvider = reference to procedure ();
TAsyncOperationCompletedHandler_1__IWebAccountProvider = class (TInterfacedObject,
AsyncOperationCompletedHandler_1__IWebAccountProvider_Delegate_Base,
AsyncOperationCompletedHandler_1__IWebAccountProvider)
private
fOnCompleted : TCompleteOperationCompleted_IWebAccountProvider;
fOnError : TCompleteOperationError_IWebAccountProvider;
protected
procedure Invoke(asyncInfo: IAsyncOperation_1__IWebAccountProvider; asyncStatus: AsyncStatus); safecall;
public
constructor Create(OnCompleted : TCompleteOperationCompleted_IWebAccountProvider;
OnError : TCompleteOperationError_IWebAccountProvider = nil);
end;
constructor TAsyncOperationCompletedHandler_1__IWebAccountProvider.Create(
OnCompleted: TCompleteOperationCompleted_IWebAccountProvider;
OnError: TCompleteOperationError_IWebAccountProvider);
begin
fOnCompleted := OnCompleted;
fOnError := OnError;
end;
procedure TAsyncOperationCompletedHandler_1__IWebAccountProvider.Invoke(
asyncInfo: IAsyncOperation_1__IWebAccountProvider; asyncStatus: AsyncStatus);
begin
case asyncStatus of
Winapi.Foundation.Types.AsyncStatus.Completed : if Assigned(fOnCompleted) then fOnCompleted(asyncInfo.GetResults());
Winapi.Foundation.Types.AsyncStatus.Error : if Assigned(fOnError) then fOnError();
else ;//todo
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
const
DefaultProviderId = 'https://login.windows.local';
MicrosoftProviderId = 'https://login.microsoft.com';
var
account: IAsyncOperation_1__IWebAccountProvider;
webAccount: IWebAccountProvider;
begin
account := TAuthentication_Web_Core_WebAuthenticationCoreManager.FindAccountProviderAsync(TWindowsString.Create(MicrosoftProviderId{DefaultProviderId}));
account.Completed := TAsyncOperationCompletedHandler_1__IWebAccountProvider.Create(
procedure(Value : IWebAccountProvider)
begin
ShowMessage('Async operation completed');
//todo
end,
nil
);
end;

Instantiated COM Component gets invalid after leaving method (but not its scope)

I am currently testing two external COM components. I have big issue with one of them, but I cannot really find reason behind such behavior. Let me provide some example.
const
CLASS_SomeClas: TGUID = '{SomeGUID}';
type
ISomeInterface = interface(IDispatch)
['{SomeGUID}']
function SomeMethod(const AInput: WideString): WideString; safecall;
end;
TWrappingClass = class(TObject)
strict private
FInstance: ISomeInterface;
procedure CreateInstance;
public
procedure DoYourActualJob;
end;
procedure TWrappingClass.CreateInstance;
begin
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
dbg(FInstance._AddRef); // Debugs 3
dbg(FInstance._AddRef); // Debugs 4
dbg(FInstance.Release); // Debugs 3
dbg(FInstance._AddRef); // Debugs 4
FInstance.SomeMethod(''); //Runs as expected
end;
procedure TWrappingClass.DoYourActualJob;
begin
CreateInstance;
dbg(FInstance._AddRef); //Debugs -1!
FInstance.SomeMethod(''); //AV
end;
As provided with example instance gets invalid after it leaves CreateInstance method. Component is designed to work with many sequential calls of SomeMethod and it does work when called inside single method.
Could someone give me clue what is actually happening there, why my instance gets invalid? Is it problem with my code, with Delphi or with component's code? When I change the implementation of TWrappingClass to another vendor (that is I change both ISomeInterface and CLASS_SomeClass) then everything works fine.
EDIT:
Behaviour does not change when I don't even call SomeMethod. That is after I leave CreateInstance, call to _AddRef returns -1. Component I am testing is here CadEditorX Probably I am not allowed to attach the OCX without violating its license.
You state clearly in the question that the erroneous behaviour only occurs with one specific COM object. Given this fact, and that Delphi's COM reference counting is known to work correctly, the only reasonable conclusion is that the fault lies in this specific COM object.
Your only recourse of action is to contact the vendor of this COM object and file a bug report with them.
One thing to look at, with a view to a possible work around, is how you are creating the object. You use CreateComObject. This receives a class ID and returns IUnknown. It calls CoCreateInstance passing the class ID, and requesting the IUnknown interface. You then need to query for your interface, ISomeInterface. So your code looks like this:
var
iunk: IUnknown;
intf: ISomeInteface;
....
CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER,
IUnknown, iunk);
iunk.QueryInterface(ISomeInterface, intf);
The fact that you have two interface variables, one IUnknown and one ISomeInterface explains why you see the reference count that you do. Now, you might think that you only have one interface variable, but that's not the case. There are two, only one of them is an implicit local. You can see this by looking at the compiled code and stepping through under the debugger.
This code:
procedure TWrappingClass.CreateInstance;
begin
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
end;
is compiled as if it were this (ignoring error checking):
procedure TWrappingClass.CreateInstance;
var
iunk: IUnknown;
begin
iunk := CreateComObject(CLASS_SomeClass);
try
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
finally
iunk := nil;
end;
end;
Perhaps the COM component cannot handle the call to Release made on its IUnknown interface.
So, you could try to work around this by using CoCreateInstance instead of CreateComObject. Pass ISomeInterface as the riid parameter.
OleCheck(CoCreateInstance(CLASS_SomeClass, nil, CLSCTX_INPROC_SERVER
or CLSCTX_LOCAL_SERVER, ISomeInterface, FInstance));

Invoke method on event handler in Delphi

As following an example of a code written in C# that use an SDK (in-proc COM object)
I've created a class: TSwitcherMonitor, and now I've to assign the callback function to my object property: OnSwitcherDisconnected property. I've this call example in C#, the note talk about double use of lambda expressions. I've googled on this and it seem that Delphi does not have lambda expression.
Here is the call in C#:
Q1: There is a way to do the same way in Delphi using the invoke method or we have to do this in a different way ?
//Create callbacks object
m_switcherMonitor := TSwitcherMonitor.Create(Application.Handle);
// note: this invoke pattern ensures our callback is called in the main thread. We are making double
// use of lambda expressions here to achieve this.
// Essentially, the events will arrive at the callback class (implemented by our monitor classes)
// on a separate thread. We must marshell these to the main thread, and we're doing this by calling
// invoke on the Windows Forms object. The lambda expression is just a simplification./
m_switcherMonitor.OnSwitcherDisconnected += new SwitcherEventHandler((s, a) => this.Invoke((Action)(() => SwitcherDisconnected())));
Q2 : The only thinks I want, is to have a working callback on using the SDK. Does my declaration of TSwitcherEventHandler is correct ?
In reference, here is my TSwitcherEventHandler declaration and my TSwitcherMonitor class:
Type
{TSwitcherEventHandler}
TSwitcherEventHandler = procedure(const sender: TObject; const args: TObject) of object;
{TSwitcherMonitor}
TSwitcherMonitor = Class(TComObject, IBMDSwitcherCallback)
private
FHwnd: HWND;
FSwitcherDisconnected: TSwitcherEventHandler;
published
constructor Create(hWnd: HWND);
public
function Notify(eventType: _BMDSwitcherEventType): HResult; stdcall;
property OnSwitcherDisconnected: TSwitcherEventHandler read FSwitcherDisconnected write FSwitcherDisconnected;
end;
implementation
{ TSwitcherMonitor }
constructor TSwitcherMonitor.Create(hWnd: HWND);
begin
FHwnd:= hWnd;
end;
function TSwitcherMonitor.Notify(eventType: _BMDSwitcherEventType): HRESULT; stdcall;
begin
if eventType = bmdSwitcherEventTypeDisconnected then
if assigned(FSwitcherDisconnected) then
FSwitcherDisconnected(self, nil);
result := S_OK;
end;
I'm not a "Delphi experts" but for shure, as many says, Delphi Rocks !
The C# line:
m_switcherMonitor.OnSwitcherDisconnected += new SwitcherEventHandler((s, a) => this.Invoke((Action)(() => SwitcherDisconnected())));
can be translated in Delphi as simple as:
m_switcherMonitor.OnSwitcherDisconnected := switcherMonitor_OnSwitcherDisconnected;
Now my sample application work. Delphi do some automatic job with pointer and when tranlating C# or C++ code in Delphi just writing it in is more simple expression and Delphi do the job. At the start, it's weird and hard to work with this kind of black magic, when we try to understand how the things works, but Delphi is certainly a true RAD as they advertise.

Method pointer and regular procedure incompatible

I have an app, which has multiple forms. All these forms have a PopupMenu. I build the menu items programatically, all under a common root menu item. I want ALL the menu items to call the same procedure, and the menu item itself is basically acting as an argument....
I had this working when I just had one form doing this functionality. I now have multiple forms needing to do this. I am moving all my code to a common unit.
Example.
Form A has PopupMenu 1. When clicked, call code in Unit CommonUnit.
Form B has PopupMenu 2. When clicked, call code in unit CommonUnit.
When I need to call my popup from each form, I call my top level procedure (which is in unit CommonUnit), passing the name of the top menu item from each form to the top level procedure in the common unit.
I am adding items to my PopupMenu with with code.
M1 := TMenuItem.Create(TopMenuItem);
M1.Caption := FieldByName('NAME').AsString;
M1.Tag := FieldByName('ID').AsInteger;
M1.OnClick := BrowseCategories1Click;
TopMenuItem.Add(M1);
I am getting an error message when I compile. Specifically, the OnClick line is complaining about
Incompatible types: 'method pointer and regular procedure'.
I have defined BrowseCategories1Click exactly like it was before when I was doing this on a single form. The only difference is that it is now defined in a common unit, rather than as part of a form.
It is defined as
procedure BrowseCategories1Click(Sender: TObject);
begin
//
end;
What is the easiest way to get around this?
Thanks
GS
A little background...
Delphi has 3 procedural types:
Standalone or unit-scoped function/procedure pointers declared like so:
var Func: function(arg1:string):string;
var Proc: procedure(arg1:string);
Method pointers declared like so:
var Func: function(arg1:string):string of object;
var Proc: procedure(arg1:string) of object;
And, since Delphi 2009, anonymous(see below) function/method pointers declared like so:
var Func: reference to function(arg1:string):string;
var Proc: reference to procedure(arg1:string);
Standalone pointers and method pointers are not interchangeable. The reason for this is the implicit Self parameter that is accessible in methods. Delphi's event model relies on method pointers, which is why you can't assign a standalone function to an object's event property.
So your event handlers will have to be defined as part of some class definition, any class definition to appease the compiler.
As TOndrej suggested you can hack around the compiler but if these event handlers are in the same unit then they should already be related anyway so you may as well go ahead and wrap them into a class.
One additional suggestion I have not seen yet is to backtrack a little. Let each form implement its own event handler but have that handler delegate responsibility to a function declared in your new unit.
TForm1.BrowseCategoriesClick(Sender:TObject)
begin
BrowseCategories;
end;
TForm2.BrowseCategoriesClick(Sender:TObject)
begin
BrowseCategories;
end;
unit CommonUnit
interface
procedure BrowseCategories;
begin
//
end;
This has the added benefit of separating the response to the user's action from the control that triggered the action. You could easily have the event handlers for a toolbar button and a popup menu item delegate to the same function.
Which direction you choose is ultimately up to you but I'd caution you to focus on which option will make maintainability easier in the future rather than which is the most expedient in the present.
Anonymous methods
Anonymous methods are a different beast all together. An anonymous method pointer can point to a standalone function, a method or a unnamed function declared inline. This last function type is where they get the name anonymous from. Anonymous functions/methods have the unique ability to capture variables declared outside of their scope
function DoFunc(Func:TFunc<string>):string
begin
Result := Func('Foo');
end;
// elsewhere
procedure CallDoFunc;
var
MyString: string;
begin
MyString := 'Bar';
DoFunc(function(Arg1:string):string
begin
Result := Arg1 + MyString;
end);
end;
This makes them the most flexible of the procedural pointer types but they also have potentially more overhead. Variable capture consumes additional resources as does inline declarations. The compiler uses a hidden reference counted interface for inline declarations which adds some minor overhead.
You can wrap your procedures into a class. This class might look like this in a separate unit:
unit CommonUnit;
interface
uses
Dialogs;
type
TMenuActions = class
public
class procedure BrowseCategoriesClick(Sender: TObject);
end;
implementation
{ TMenuActions }
class procedure TMenuActions.BrowseCategoriesClick(Sender: TObject);
begin
ShowMessage('BrowseCategoriesClick');
end;
end.
And to assign the action to a menu item in a different unit is enough to use this:
uses
CommonUnit;
procedure TForm1.FormCreate(Sender: TObject);
begin
PopupMenuItem1.OnClick := TMenuActions.BrowseCategoriesClick;
end;
Update:
Updated to use class procedures (instead of object methods) by David's suggestion. For those who want to use the object methods with the need of object instance, follow this version of the post.
This is the difference between a "procedure" and a "procedure of object"
The OnClick is defined as a TNotifyEvent:
type TNotifyEvent = procedure(Sender: TObject) of object;
You cannot assign a procedure to the OnClick as it is the wrong type. It needs to be a procedure of object.
You could choose one of these:
Derive your forms from a common ancestor and declare the method in it so it's available to descendants
Use a global instance of a class (e.g. data module) shared by all forms
Use a procedure as a fake method like this:
procedure MyClick(Self, Sender: TObject);
begin
//...
end;
var
M: TMethod;
begin
M.Data := nil;
M.Code := #MyClick;
MyMenuItem.OnClick := TNotifyEvent(M);
end;
One solution is to place the OnClick method into a TDatamodule.

Accessing Sub functions /procedures from DPR or other function / procedure in Delphi

As much I know - Subroutines are with Private access mode to its parent unction / procedure, right?
Is there any way to access them from "outer-world" - dpr or other function / procedure in unit?
Also - which way takes more calcualtion and space to compiled file?
for example:
function blablabla(parameter : tparameter) : abcde;
procedure xyz(par_ : tpar_);
begin
// ...
end;
begin
// ...
end;
procedure albalbalb(param : tparam) : www;
begin
xyz(par_ : tpar_); // is there any way to make this function public / published to access it therefore enabling to call it this way?
end;
// all text is random.
// also, is there way to call it from DPR in this manner?
// in C++ this can be done by specifing access mode and/or using "Friend" class .. but in DELPHI?
Nested procedures/functions - those declared inside another procedure or function, are a special type, because they can access the stack (and thereby parameters/local variables) of the procedure they are nested in. Because of this, and Delphi scope rules, there is no way to access them outside the "parent" procedure. You use them only if you need to take advantage of their special features. AFAIK Delphi/Pascal is one of the few languages to have this feature. From a compiler point of view the call has some extra code to allow accessing the parent stack frame, IIRC.
AFAIK "friend" class/functions in C++ are different - they are class access methods, while in your example you are using plain procedures/functions.
In Delphi all procedure/classes declared in the same unit are automatically "friend", unless strict private declarations are used in latest Delphi releases. For example this code snippets will work, as long everything is in the same unit:
type
TExample = class
private
procedure HelloWorld;
public
...
end;
implementation
function DoSomething(AExample: TExample);
begin
// Calling a private method here works
AExample.HelloWordl;
end;
Note: Embedded Routines <> Private/Protected Methods.
Embedded routines i.e. routines inside routines can not be accessed by external routines.
You have posted an example of an Embedded routine, I also heard them called Internal Routines.
Here is another example:
procedure DoThis;
function DoThat : Boolean;
begin
// This Routine is embedded or internal routine.
end;
begin
// DoThat() can only be accessed from here no other place.
end;
Regardless of visibility, methods on classes, can be called using Delphi 2010 via RTTI. I have detailed how to do this in this article.
If you are in the same Unit methods on a class can be accessed by any other code regardless of visibility, unless they are marked with Strict Private. This Question has more details and good example code in the accepted answer.
If you are in two different units you can use the Protected Method Hack to access the protected methods. Which is detailed in detailed in this article.
Yes, you can access a subroutine, which is nested in other (parent) subroutine, from the outer world. Though it's somewhat tricky. I've found this howto in the web.
How to pass nested routine as a procedural parameter (32 bit)
Delphi normally does not support passing nested routines as procedural parameters:
// This code does not compile:
procedure testpass(p: tprocedure);
begin
p;
end;
procedure calltestpass;
procedure inner;
begin
showmessage('hello');
end;
begin
testpass(inner);
end;
The obvious workaround is to pass procedure address and typecast it within testpass:
// This code compiles and runs OK
procedure testpass(p: pointer);
begin
tProcedure(p);
end;
procedure calltestpass;
procedure inner;
begin
showmessage('hello');
end;
begin
testpass(#inner);
end;
There is, however, a pitfall in the above example - if the "inner" routine references any variable that was pushed onto the stack before the "inner" procedure was called from testpass (calltestpass parameters - if there were any, or local variables in calltestpass - if there were any), your system most probably crashes:
// This code compiles OK but generates runtime exception (could even be
// EMachineHangs :-) )
procedure testpass(p: pointer);
begin
tProcedure(p);
end;
procedure calltestpass;
var msg: string;
procedure inner;
begin
msg := 'hello';
showmessage(msg);
end;
begin
testpass(#inner);
end;
The reason is, in simple words, that the stack frame arrangement
was "broken" by the call to testpass routine and "inner" procedure
incorrectly calculates parameters and local variables location
(do not blame Delphi, please).
The workaround is to set up the correct stack context before
"inner" is called from within "testpass".
// This code compiles and runs OK
{$O-}
procedure testpass(p: pointer);
var callersBP: longint;
begin
asm // get caller's base pointer value at the very beginning
push dword ptr [ebp]
pop callersBP
end;
// here we can have some other OP code
asm // pushes caller's base pointer value onto stack and calls tProcedure(p)
push CallersBP
Call p
Pop CallersBP
end;
// here we can have some other OP code
end;
{$O+}
procedure calltestpass;
var msg: string;
procedure inner;
begin
msg := 'hello';
showmessage(msg);
end;
begin
testpass(#inner);
end;
Please note the optimization is switched OFF for testpass routine - optimization generally does not handle mixed OP/assembler code very well.
No, there is no way to do what you're asking. The xyz function is callable only by the enclosing blablabla function. Outside that function, xyz is not in scope and there is no way to name it. If C++ allowed nested function, there wouldn't be any way to refer to it, either, just like there's no way to refer to functions with static linkage from outside the current translation unit.
If you need to call xyz from outside the blablabla function, then move xyz outside. If you need to call it from outside the current unit, then you need to declare that function in the unit's interface section. Then, add that unit to the external code's uses clause and you can call xyz from wherever you want, even the DPR file.
If xyz refers to variables or parameters of the blablabla function, then you'll need to pass them in as parameters since xyz will no longer have access to them otherwise.
The concept of access specifiers isn't really relevant here since we're not talking about classes. Units have interface and implementation sections, which aren't really the same as public and private sections of a class.

Resources