Just to let you know the terminology, you're looking for "Inversion of Control" or "IoC."
There are several ways to implement this, including Injection of Dependency and callbacks (like delegates), as in Nico's answer. There are also service locators (although many consider this to be an "anti-pattern") and "Factories".
Personally, I prefer the Injection Dependency approach:
Basically, your DLL (aka ) needs an object that can execute a function, but it needs someone else (the caller) to implement the actual logic.
So, all you have to do is create an interface in your DLL that defines the type of object you want:
Namespace DLL Public Interface IDataRetriever Public Function GetData() As Object End Interface End Namespace
Then, in the MainForm project that references your DLL, simply create a class that implements this interface:
Public Class DataRetriever Implements DLL.IDataRetriever Public Function GetData() As Object Implements DLL.IDataRetriever.GetData //... Return New Object() End Function End Class
(Note that any class can implement an interface, including a class that already exists or even MainForm itself. You do not need to create a new class just for the interface - although make sure that you follow the separation of problems .)
Now, when you call the DLL, you can pass it your DataRetriever, and your DLL will know what it is connected with.
Namespace DLL Public Class Utility Public Shared Function DLLFunction( retriever as IDataRetriever ) retriever.GetData() End Function End Class End Namespace
Class MainForm Sub Example() DLL.Utility.DLLFunction( New DataRetriever() ) End Sub End Class