Delphi allows for interface delegation using the implements keyword.
For example
IIndep1 = interface
function foo2: integer;
end;
IIndep2 = interface
function goo2: integer;
end;
TIndep1And2 = class(TInterfacedObject, IIndep1, IIndep2)
private
FNested : IIndep1; //e.g. passed via constructor or internally created (not shown here)
public
Constructor Create(AIndep1: IIndep1);
function goo2: integer;
property AsIndep1 : IIndep1 read FNested implements IIndep1;
end;
That works well, but not for inherited interfaces. (Error message "Missing implementation of interface method ILev1.foo")
ILev1 = interface
function foo: Integer;
end;
ILev2 = interface(ILev1)
function goo: Integer;
end;
TLev2Fails = class(TInterfacedObject, ILev1, ILev2) //Also fails with ILev2 alone (Error: "ILev1 not mentioned in interface list")
private
FNested : ILev1; //passed via constructor or internally created
public
Constructor Create(AILev1: ILev1);
function goo: Integer;
property AsLev1 : ILev1 read FNested implements ILev1;
end;
The workaround is to add an extra ancestor class
TLev1Wrapper = class(TInterfacedObject, ILev1)
private
FNested : ILev1; //passed via constructor or internally created
public
Constructor Create(AILev1: ILev1);
property AsLev1 : ILev1 read FNested implements ILev1;
end;
TLev2Works = class(TLev1Wrapper, ILev2)
public
function goo: Integer;
end;
Is there a way to avoid the wrapper class ancestor?
[EDIT]
Just a note on interface delegation, the purpose of using implements is to avoid satisfying the interface directly, but passing that requirement to an aggregated or composed member. Providing the full interface and manually delegating to a composed member defeats the benefits gained from using implements to direct the interface. In fact, in that case the implements keyword and property may be removed.
This looks like the compiler is attempting to enforce the expectations (read: requirements) of IUnknown.QueryInterface:
For any one object, a specific query for the IUnknown interface on any
of the object's interfaces must always return the same pointer value.
If you were able to delegate the implementation of a base interface whilst implementing a derived interface yourself then:
obj := TLev2Fails.Create(otherLev1); // Assuming your class could compile
lev1 := obj as ILev1; // yields reference to otherLev1 implementor
lev2 := obj as ILev2; // yields reference to TLev2Fails instance
unk1 := lev1 as IUnknown; // returns IUnknown of otherLev1 implementor
unk2 := lev2 as IUnknown; // returns IUnknown of obj TLev2fails instance
This would not be the case if your nested object were correctly implemented as a TAggregatedObject derived class, but the compiler has no way of knowing whether this is the case let alone enforcing it so instead it appears it simply requires that if you implement a derived interface then you must also directly implement any interfaces that interface itself inherits.
The compiler error in this situation isn't being very helpful, though it could be read as telling you what you need to do, just not why you need to do it in this case, which is not that unusual for compiler errors.
If you wish to delegate in this case then you must "delegate manually".
Whilst this loses the benefit of the implements facility, it does at least retain the benefits of re-use, just not quite as conveniently.
NOTE: Even if your delegated implementation is based on a TAggregatedObject, the compiler still cannot determine that these implementation details satisfies the requirements of QueryInterface so you will still get this error (even if using a class reference for the delegated interface).
You must still delegate manually.
Having said all that, I cannot currently see how this is any different for the case when interfaces are involved with no inheritance relationship, but it is quite possible that this is valid and I just haven't worked through all the necessary 'thought experiments' to prove it to myself. :)
It may be that the compiler is just being extra cautious in situations when it thinks it can/should be and the documentation simply fails to mention this resulting limitation of implements.
Or it may be a bug in the compiler, though I think there are sufficiently apparent reasons for the behaviour and the behaviour is itself so well established and consistent (having reproduced the exact same behaviour in Delphi 7) that an omission in relevant documentation is the more likely explanation.
Is there a way to avoid the wrapper class ancestor?
No, the inherited interface has to be bound to a an ancestor class. Delphi does not implicitly bind inherited interfaces, because of reasons explained here. Also, it cannot bind to the current class, unless it manually delegates all calls to the composed item.
We are left with only one option. An ancestor wrapper class, to which we can bind ILev1
Related
I need to check if method was implemented in class.
My classes implements an interface.
Code.
IMyInterface = interface
['{538C19EB-22E3-478D-B163-741D6BB29991}']
procedure Show();
end;
TMyClassFormCustomer = class(TInterfacedObject, IMyInterface)
public
procedure Show();
end;
TMyClassFormVendors = class(TInterfacedObject, IMyInterface)
public
procedure Show();
end;
....
procedure TMyClassFormCustomer.Show;
var
Form: TFormCustomer;
begin
Form:= TFormCustomer.Create(nil);
try
Form.ShowModal();
finally
FreeAndNil(Form)
end;
end;
{ TMyClassFormVendors }
procedure TMyClassFormVendors.Show;
begin
end;
It is possible to check if method TMyClassFormVendors.Show have implementation?
When a method not have implementation I have disable item in menu
Thanks.
I see a couple of possibilities:
If your TMyClassFormVendors does not want to do anything in .Show then simply don't declare and implement IMyInterface. In your code you can then query for that interface with if Supports(MyClass, IMyInterface) and react accordingly (Disable Menu entry)
If your IMyInterface interface actually has more than one method declared, some of which are supported (I don't want to use the word implemented, as all methods have to be implemented) and others are not, then you should better split the interface into several different ones and proceed as described in 1
You could also declare and implement another interface "ICapabilities" that could have methods like CanShow etc. In your code you could then query your class like if (Myclass as ICapabilities).CanShow then ...
I personally would favour 2. as it is the cleanest approach IMO, but it depends on what you want to do specifically
You could use similar approach as it is used for handling the event methods. So what you want to do is instead of implementing the method directly into your class you add a property which can store a method pointer to the actual method you wanna use. And then at runtime you only check to see if the pointer to such method was already assigned or not.
The code for this would look something like this:
type
//Method type object
TMyMethod = procedure (SomeParameter: Integer) of object;
TMyClass = class(TObject)
private
//Field storing pointer to the method object
FMyMethod: TMyMethod;
protected
...
public
//proerty used for assigning and accesing the method
property MyMethod: TMyMethod read FMyMethod write FMyMethod;
end;
When you are designing the method objects you can define how many and which type of parameters you wanna use in that method.
Don't forget you need to assign method to your method property in order to use it. You can do this at any time. You can even unassign it by setting it to nil if needed.
You do this in same way as you do with event methods
MyClass.MyMethod := MyProcedure;
where MyProcedure needs to have the same parameter structure as the method object type you defined earlier.
And finally you can check to see if methods has been assigned at runtime using:
if Assigned(MyClass.MyMethod) then
begin
//Do something
end
else //Skip forward
EDIT: Another advantage of this approach is that your actual method that you use can be defined and written in completely different unit.
When using interfaces, are the implementing class methods forced to return interfaces, or is there a way to return objects?
An example will clarify:
in unit EmployeeIntf:
IEmployee = interface(IInterface)
function CoWorker: IEmployee; // We dont know anything about TEmployee here
end;
in unit Employee:
uses EmployeeIntf;
...
TEmployee = class(TObject, IEmployee)
public
function CoWorker: IEmployee; // Returns an error if set to TEmployee
end;
Current code stops on a compiler error if the method returns a TEmployee: E2211 Declaration of 'CoWorker' differs of declaration in interface IEmployee (using Delphi 2010)
Is the TEmployee.CoWorker method forced to return an interface, or is there a way to return a TEmployee instead, as far as the TEmployee is a IEmployee?
If only interfaces are allowed in this case, what are the OO design reasons for that?
[Edit]
As asked by many contributors, I dont need ref counting on TEmployee and would like to isolate the above question from any ref counting consideration.
The background of this question is a need to use a very limited set of public functions of TEmployee in an external component (in a separate package). I can't simply import the 'Employee' unit in the package because of too many uneeded dependencies in uses sections, so I'm looking for a loosely coupling solution.
Thanks,
You can make the compiler happy by introducing a method resolution clause:
type
TEmployee = class(TInterfacedObject, IEmployee)
function IEmployee.CoWorker = IEmployeeCoWorker;
public
function IEmployeeCoWorker: IEmployee;
function CoWorker: TEmployee;
end;
function TEmployee.IEmployeeCoWorker: IEmployee;
begin
result := CoWorker;
end;
Of course, this will complicate the code, but if it actually is what you need...
are the implementing class methods forced to return interfaces?
No, but implementations are forced to match the signatures of the interface definitions.
is there a way to return objects?
Yes, if you declare the interface accordingly.
type
IEmployee = interface
function CoWorker: TEmployee;
end;
I'm assuming that you wish for the CoWorker function to return IEmployee, for the benefit of clients of this interface.
However, the implementation code sometime has a valid reason to access the implementing object. Of course doing so does create a coupling that may be undesirable.
As of Delphi 2010 you are able to use the as operator to gain access to the implementing object.
var
EmpIntf: IEmployee;
EmpImp: TEmployee;
...
EmpImp := EmpIntf as TEmployee;
Beware that you are now subverting the lifetime management of the object. If you access EmpImp after the interface reference has gone to zero, then you are in trouble.
Finally, I would comment that using such an approach has the whiff of bad design. It is usually better to find an approach that does not us as in this way.
Delphi does not support covariant return types. So this has nothing to do with interfaces but is simply a restriction of the language itself.
Normally this is not a problem though, since clients normally should use IEmployee and should not care about the specific type of the coworkers. You have to provide more details (probably in a new question) why you need the specific type.
If you use interface reference-counting, you have even more reasons to only use interface type references, since mixing interface and object references can mess up the reference counting.
If you are defining an interface function that returns an interface, why would you want to return an object in the implementation?
I believe that you only don't know how to implement "function CoWorker: IEmployee". If that's the problem, here is a simple solution:
TEmployee = class(TInterfacedObject, IEmployee)
public
function CoWorker: IEmployee;
end;
function TEmployee.CoWorker: IEmployee;
begin
Result := TEmployee.Create;
end;
BTW, you should derive interface-implementing objects from TInterfacedObject, not from TObject.
[EDIT]
As for your clarification - can't you define a small public interface for TEmployee and then derive this class from both interfaces? Like this:
type
IEmployeePublic = ...
IEmployee = ...
TEmployee = class(TInterfacedObject, IEmployee, IEmployeePublic)
function CoWorker: IEmployeePublic;
end;
I have this Delphi class
type
TAnotherClass = class
end;
TMyClass = class
function Foo: TAnotherClass;
end;
function TMyClass.Foo: TAnotherClass;
begin
Result := TAnotherClass.Create;
end;
Now I'd like to expose this class via "dwsRTTIExposer.pas":
myUnit.ExposeRTTI(TypeInfo(TMyClass));
myUnit.ExposeRTTI(TypeInfo(TAnotherClass));
My Script looks like that:
var a: TMyClass = TMyClass.Create;
var b: TAnotherClass;
b := a.Foo;
Unfortunatelly Delphi Web Script doesn't recognize the return value from TMyClass.Foo as a valid Script Class. Is there a possibility to do that without falling back to manually expose each method with an OnEval-Eventhandler?
ExposeRTTI currently doesn't support parameters of class type.
This is because returning a direct Delphi class in the script can be problematic, as the life-cycle of Delphi objects is arbitrary and undetermined (the Delphi-side object can be destroyed at any time without notice f.i.).
You don't have to expose each method manually, you can use the RTTI exposer for every methods that involves basic types, and only have to deal manually with methods involving class types.
That'll then leave you with having to decide how you want the script-side objects to be exposed, and what their relationship to the Delphi-side object is, which is something the RTTI provides no clues about.
For instance with your original code, the OnEval code would just create a new script object that wraps the method Result for each call.
But the RTTI signature of Foo would still be exactly the same if its implementation was changed to something like
TMyClass = class
private
FFoo: TAnotherClass;
public
function Foo: TAnotherClass;
end;
function TMyClass.Foo: TAnotherClass;
begin
if FFoo=nil then
FFoo := TAnotherClass.Create;
Result := FFoo;
end;
However in that case, the OnEval would have to be completely different, as you would have to return the same script-side object on subsequent calls, and you would also need to hook the script-side object's destructor to properly handle the consequences on the private FFoo field.
Once Delphi has truly garbage-collected objects, the constraint could be relaxed, but currently the only thing that gets close is TInterfacedObject, which is unsafe, and you still have to deal with manual event handlers to handles things like circular references or classes that disable the reference counting (like the VCL components).
I'm pretty sure it's possible to call a class and its member function dynamically in Delphi, but I can't quite seem to make it work. What am I missing?
// Here's a list of classes (some code removed for clarity)
moClassList : TList;
moClassList.Add( TClassA );
moClassList.Add( TClassB );
// Here is where I want to call an object's member function if the
// object's class is in the list:
for i := 0 to moClassList.Count - 1 do
if oObject is TClass(moClassList[i]) then
with oObject as TClass(moClassList[i]) do
Foo();
I get an undeclared identifier for Foo() at compile.
Clarification/Additional Information:
What I'm trying to accomplish is to create a Change Notification system between business classes. Class A registers to be notified of changes in Class B, and the system stores a mapping of Class A -> Class B. Then, when a Class B object changes, the system will call a A.Foo() to process the change. I'd like the notification system to not require any hard-coded classes if possible. There will always be a Foo() for any class that registers for notification.
Maybe this can't be done or there's a completely different and better approach to my problem.
By the way, this is not exactly an "Observer" design pattern because it's not dealing with objects in memory. Managing changes between related persistent data seems like a standard problem to be solved, but I've not found very much discussion about it.
Again, any assistance would be greatly appreciated.
Jeff
First of all you're doing something very unusual with TList: TList is a list of UNTYPED POINTERS. You can add any pointer you want to that list, and when you're doing moClassList.Add( TClassA ) you're actually adding a reference to the class TClassA to the list. Technically that's not wrong, it's just very unusual: I'd expect to see TClassList if you actually want to add a class! Or TList<TClass> if you're using a Delphi version that support it.
Then you're looping over the content of the list, and you're checking if oObject is of the type in the list. So you do want classes in that list after all. The test will work properly and test rather the object is of that type, but then when you do with oObject as TClass(moClassList[i]) do you're actually casting the object to... TObject. Not what you wanted, I'm sure!
And here you have an other problem: Using Foo() in that context will probably not work. TObject doesn't contain a Foo() method, but an other Foo() method might be available in context: That's the problem with the with keyword!
And to finally answer the question in the title bar: Delphi is not an Dynamic language. The compiler can't call a method it doesn't know about at compile time. You'll need to find a OOP way of expressing what you want (using simple inheritance or interfaces), or you may call the function using RTTI.
Edited after question clarification.
All your business classes need to implement some kind of notification request management, so your design benefits allot from a base class. Declare a base class that implements all you need, then derive all your business classes from it:
TBusinessBase = class
public
procedure RegisterNotification(...);
procedure UnregisterNotification(...);
procedure Foo;virtual;abstract;
end;
In your initial example you'd no longer need the list of supported classes. You'll simply do:
oObject.Foo;
No need for type testing since Delphi is strongly typed. No need for casting since you can declare oObject": TBusinessBase.
Alternatively, if you for some reason you can't change the inheritance for all your objects, you can use interfaces.
TClass is defined:
TClass = class of TObject;
You then write oObject as TClass which is effectively a null operation since oObject already was a TObject.
What you need is something like this:
type
TFoo = class
procedure Foo();
end;
TFooClass = class of TFoo;
TBar = class(TFoo)
procedure Bar();
end;
....
if oObject is TFooClass(moClassList[i]) then
with oObject as TFooClass(moClassList[i]) do
Foo();
This explains why your attempts to call Foo() does not compile, but I simply have no idea what you are trying to achieve. Even after your clarification I'm struggling to understand the problem.
Here's a really contrived example (using an array instead of a TList) that I think is what you're trying to do (error handling and try..finally intentionally omitted for clarity).
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TBaseClass=class(TObject)
procedure Foo; virtual;
end;
TClassA=class(TBaseClass)
procedure Foo; override;
end;
TClassB=class(TBaseClass)
procedure Foo; override;
end;
TClassArray= array of TBaseClass;
{ TClassB }
procedure TClassB.Foo;
begin
Writeln('TClassB.Foo() called.');
end;
{ TClassA }
procedure TClassA.Foo;
begin
Writeln('TClassA.Foo() called.');
end;
var
Base: TBaseClass;
ClassArr: TClassArray;
{ TBaseClass }
procedure TBaseClass.Foo;
begin
Writeln('TBaseClass.Foo called!!!!!!!!');
end;
begin
ClassArr := TClassArray.Create(TClassA.Create, TClassB.Create);
for Base in ClassArr do
Base.Foo;
for Base in ClassArr do
Base.Free;
ReadLn;
end.
I have a legacy app written in Delphi 7. We are adding new modules to the app. The modules are written in Visual Studio 2010, .NET 4, C#, and exposed to the app through COM.
I have successfully defined a class, registered the assembly, exported the type library, imported the type library into Delphi, created the COM client in Delphi and executed the module. Now comes the tricky part: I want to pass another object (that has been defined-registered-exported-blah-blah-blah as above) as a parameter to a method on the first module.
.NET
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("11111111-1111-1111-1111-AAAAAAAAAAAA")]
public interface IUserMaintenance
{
bool AssignUser(IUserInfo);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[Guid("11111111-1111-1111-1111-BBBBBBBBBBBB")]
public class UserMaintenance: IUserMaintenance
{
private IUserInfo _UserInfo;
public bool AssignUser(IUserInfo userInfo)
{
_UserInfo = userInfo;
LoadUser();
}
private void LoadUser()
{
//load user data from database using _UserInfo.UserName
}
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("22222222-2222-2222-2222-AAAAAAAAAAAA")]
public interface IUserInfo
{
void Initialize(string userName);
string UserName { get; }
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[Guid("22222222-2222-2222-2222-BBBBBBBBBBBB")]
public class UserInfo: IUserInfo
{
public string UserName { get; private set };
public void Initialize(string userName)
{
UserName = userName;
}
}
Assuming that I have separate classes implementing each interface, and the assemblies containing those classes compile and register successfully, I import the type libraries into Delphi creating UserMaintenance_TLB.pas and UserInfo_TLB.pas. However, I see something unexptected: while the interfaces that I defined in .NET exist (the ones beginning with "I"), Delphi has generated another set of interfaces (the ones beginning with "_"). Delphi does not use the I-interfaces that I declared in .NET at all.
Delphi
UserMaintenance_TLB.pas
// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
IUserMaintenance = interface;
IUserMaintenanceDisp = dispinterface;
_UserMaintenance = interface;
_UserMaintenanceDisp = dispinterface;
UserInfo_TLB.pas
// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
IUserInfo = interface;
IUserInfoDisp = dispinterface;
_UserInfo = interface;
_UserInfoDisp = dispinterface;
Delphi has also created the corresponding Delphi types:
// *********************************************************************//
// OLE Server Proxy class declaration
// Server Object : TUserMaintenance
// Help String :
// Default Interface: _UserMaintenance
// Def. Intf. DISP? : No
// Event Interface:
// TypeFlags : (2) CanCreate
// *********************************************************************//
TUserMaintenance = class(TOleServer)
private
FIntf: _UserMaintenance;
function GetDefaultInterface: _UserMaintenance;
protected
procedure InitServerData; override;
function Get_ToString: WideString;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Connect; override;
procedure ConnectTo(svrIntf: _UserMaintenance);
procedure Disconnect; override;
function Equals(obj: OleVariant): WordBool;
function GetHashCode: Integer;
function GetType: _Type;
WordBool AssignUser(IUserInfo userInfo);
property DefaultInterface: _UserMaintenance read GetDefaultInterface;
property ToString: WideString read Get_ToString;
published
end;
What I would like to do is create an instance of TUserMaintenance, and then pass an instance of TUserInfo to it. However, two things are immediately obvious: The Delphi class TUserInfo does NOT implement IUserInfo, and the DefaultInterface is the new interface that Delphi generated, _UserInfo. I cannot declare my UserInfo variable as type IUserInfo because TUserInfo does not implement the interface. Nor can I declare it as type _UserInfo because UserMaintenance.LoadUser expects an IUserInfo instance.
Admittedly, this is a much simplified example of my actual issue, but I think that it sufficiently illustrates the problem.
So my question is this: is there any way for me to force the interface type in Delphi to remain consistent with the interface that is declared in .NET? Or is there another way that I can pass an instance of UserInfo to UserMaintenance?
Part of the problem that in Delphi, Objects may implement interfaces, but they are not in and of themselves interfaced objects.
To understand this distinction, you have to look at the raw implementation of an interface
on a Delphi object, and understand the reference counting mechanism used by COM. Another thing
that has to be understood is that .NET doesn't work the same way, so what appears to be an
interface in .NET for an object IS the same as the ComVisible aspect presented.
TYPE
TMyObject = class(TInterfacedObject, IMyObject, IMyNewInterface)
end;
Given the above, the following things can be said of this object.
You created a new COM object using the Delphi COM object wizard.
The Interface IMyObject is defined as the default interface, and TMyObject is the concrete class
that will implement that interface.
IMyNewInterface is a secondary interface defined somewhere else, that you have indicated your
object implements.
You can do the following with this object
var
I1: IMyObject;
I2: IMyNewInterface;
T: TMyObject;
begin
I1:=TMyObject.Create;
I2:=TMyObject.Create;
T:=TMyObject.Create;
end;
You can do these things because TMyObject IMPLEMENTS these interfaces. Also,
since these are reference counted, you don't have to release them from memory, when
the method scope ends, so does the lifetime of the object - WITH THE EXCEPTION OF THE LAST ONE
Because you are using an OBJECT REFERENCE instead of an INTERFACE REFERENCE, you must free
the OBJECT you created.
Given the code you posted, if you look closely, you will actually find the same situation.
In your case, your object is completely implemented in .NET, so what you are trying to
use is a Delphi code Wrapper - which is an OBJECT, not an INTERFACE.
You notice on the wrapper, that there is no second method to pass an instance
of the IUserInfo object to, and that is because this OBJECT is not implementing the INTERFACE,
(your .NET object was created to do that) - what you need to do in Delphi is "select that interface"
You would accomplish that task by doing the following:
If you have already done a TUserMaintenance.Create(nil) call and have an instance of that object,
get the default interface, then "cast" it to the appropriate implemented interface
var
UMDelphiWrapper: TUserMaintenance;
UIDelphiWrapper: TUserInfo;
UI: IUserInfo;
UM: IUserMaintenance;
begin
//this part creates a Delphi OBJECT reference to the Implementation class -
//this is NOT Reference counted, because it doesn't implement an interface.
UIDelphiWrapper:=TUserInfo.Create(nil);
try
//this is the part where you acquire the interface of the object that actually
//implementes this interface - e.g. your .NET class
//not that this is the INTERFACE reference - which WILL be reference counted
UI:=UIDelphiWrapper.DefaultInterface as IUserInfo;
//UI.<Set some properties of your IUserInfo object>
try
//this part creates a Delphi OBJECT reference to the Implementation class -
//this is NOT Reference counted, because it doesn't implement an interface.
UMDelhpiWrapper:=TUserMaintenance.Create(nil);
try
//this is the part where you acquire the interface of the object that actually
//implementes this interface - e.g. your .NET class
//not that this is the INTERFACE reference - which WILL be reference counted
UM:=UMdelphiWrapper.DefaultInterface as IUserMaintenance;
try
//Here, you have an interface type implemented by your .NET class that you are
//sending to the implementation of your management object (Also a .NET class)
UM.SendUser(UI);
//do whatever else you need to do with your interface and user/management .NET object(s)
finally
//this IS a reference counted COM object - no "free" necessary
//this would naturally happen when the reference goes out of scope in the method
//but for clairity sake is set to NIL to explicitly release your reference
UM:=nil;
end;
finally
//This is a delphi object that is NOT reference counted and must be released
FreeAndNil(UMDelphiWrapper);
end;
finally
//this IS a reference counted COM object - no "free" necessary
//this would naturally happen when the reference goes out of scope in the method
//but for clairity sake is set to NIL to explicitly release your reference
UI:=nil;
end;
Finally
//This is a delphi object that is NOT reference counted and must be released
FreeAndNIl(UIDelphiWrapper);
end;
In addition to actually using the Delphi provided wrappers you could instead directly create references to your .NET objects as long as you know the correct information.
var
UI: IUserInfo;
UM: IUserManager;
begin
UI:=CreateOleObject('YourAssembly.YourImplementationClass') as IUserInfo;
UI.SomeProperty:=SomeValue;
UM:=CreateOleObject('YourAssembly.YourImplementationClass') as IUserManger;
UM.SomeMetohd(UI);
end;
This code is much cleaner - however, you must still have the accurate definition of IUserInfo and IUserMaintenance, as well as know the class friendly name of your objects as they have been registered with COM.
You could do this yourself by typing the code out - this is what the type import function should have done for you when you imported the COM exposed DLL from your assembly. I didn't see the actual implementation in the code you provided, but you should still find this information in your header file. -- If you don't, then you didn't import the correct assembly, or the Delphi 7 import didn't function correctly - or it needs to be refreshed (e.g. you added new methods to your .NET implementation, but didn't re-register (with COM) and re-import the new assembly type info).
type
IUserInfo = interface
['22222222-2222-2222-2222-AAAAAAAAAAAA']
//define your methods
end;
Hope i understand your question correctly. If you want to get rid of the confusing (and unnecessary) _UserInfo in the typelibrary, you should not export your CoClass, only the interface.
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("22222222-2222-2222-2222-AAAAAAAAAAAA")]
public interface IUserInfo
{
}
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("22222222-2222-2222-2222-BBBBBBBBBBBB")]
public class UserInfo: IUserInfo
{
}
Note the first attribute of the CoClass is set to ClassInterfaceType.None, this way DotNet will not reveal the CoClass itself to the typelibrary. Nevertheless you can instantiate your object in Delphi as you always do:
var
pUserInfo: IUserInfo;
begin
pUserInfo := CoUserInfo.Create;