By far, the simplest solution is to give both the main app and the dll some common knowledge. The way you would do this is to define the common things, such as interfaces, delegates, and the like, in a dll which is referenced both by the main application as well as by the dll. I was thinking that you may not be allowed to do something like that, but you have made it clear that the main app is allowed to reference dlls that you have created, so this solution should work.

As it stands, the problem is that the main application knows about types in the dll because the main application has a reference to the dll. The dll doesn't know about types defined in the main application because the dll does not (and can not) have a reference to the main application. That means that some of the simpler solutions, such as passing an instance of the main form to the dll, won't work because the dll wouldn't recognize the main form as a type. By using a common dll, you could define a delegate in that dll. The working dll could have a method that accepts a delegate of that type and calls it when it is ready to show the main form. Therefore, the main application could have a method that launches the main form, and could pass that method to the dll as a delegate.

Another alternative, which may be simpler, would be to pass an instance of the mainform to the dll as type Form (in other words, the mainform would be cast to its base, which is a type that the dll will know about). The dll would then be able to call Show or ShowDialog on that Form object, without knowing what specific type of form is being shown. The dll wouldn't be able to interact with that form in pretty nearly any other way, though, as it would only be able to call methods of the base Form class, and would know nothing about it being a mainform, or even what a mainform was.