With some code massaging to take platform differences into account, yes, Delphi components written for Win32 can work in .NET.
Most components live “closer to the metal” than application code, so you should expect VCL components to require more work to port to .NET than Delphi application code. The amount of work required to port a GUI component written for VCL for Win32 to run in VCL for .NET should be considerably less than what is required to port the same VCL component to CLX. As with CLX, porting non-visual components to .NET requires less work than porting GUI controls.
Components often take responsibility for managing memory allocations so that the component user (application writer) doesn’t have to. Memory allocations may need to be rethought for .NET, since GetMem() doesn’t exist there and raw pointer arithmetic is very restricted. Destructors are not guaranteed to be executed in .NET, but components that only use the destructor to deallocate memory should work fine because the system will reclaim that memory after the object is no longer “alive”. Doing “heavy” things like closing files or sending network messages in a destructor will need to be rethought for .NET – this should be done outside of the destructor.
Why? Because the .NET framework does not provide deterministic finalization. That means that Delphi destructors are only called as a courtesy, if the code using your objects is kind enough to do so. In VCL for .NET, destructors will usually be called because of the way VCL manages object lifetimes / ownership, but in the general case (Delphi classes being used by non-Delphi .NET code) you must assume your destructors will not be called.
.NET does provide a “last chance” cleanup option that an individual class may choose to implement, called finalizers. However, finalizers execute long after the object has been thrown away, and finalizers execute in a very bizarre, restricted context (different thread, can’t make external calls, can’t access data, etc etc) so their usefulness is very limited. Finalizers also add performance overhead to the memory system, so they should only be used in extraordinary situations. Finalizers may be suitable for a specific object’s needs, but finalizers are not a good match for destructors in general. That’s why we didn’t implement Delphi destructors to use finalizers.
There is also an IDisposeable pattern in .NET, which Brian Long’s article on BDN goes into pretty deeply. The main downside to the IDisposeable pattern is that it is only a pattern – there’s no way to guarantee that the user of your code will honor the pattern and call Close() when they’re supposed to. On the other hand, anybody who opens a file-like thing and doesn’t call Close() is asking for trouble.
What we’ve found in porting VCL itself to .NET is that 90% of the destructors in VCL are disposing of things that .NET will take care of anyway, so the destructor isn’t needed at all. The other 5% are releasing resource handles and so forth – things that the .NET GC system does not keep track of. Those are dealt with using finalizers.
The same goes for unit finalization – another kind of destructor. In this case, 99% of VCL’s unit finalizations are either destroying globally allocated instances or unregistering classes from a global catalog. Both of those chores go away in .NET. (Class registration is replaced by .NET metadata reflection)
Scan your VCL component source code looking for things like GetMem(), PChar, massive destructors, or heavy pointer typecasting. If you have Delphi 7, turn on the “unsafe code” warnings and compile the VCL component source to see what warning the compiler gives you. That should give you an indication of how easily that VCL component will be to port to VCL for .NET.