0

I'm working on a large API project that needs to release 2 DLLs with many interfaces written in C++/CLI (one DLL contains interfaces for public use and the other extends some of the public interfaces for internal company use only).

All implementation classes for all interfaces are in a separate project and are all contained in a separate DLL as well.

Most internal interfaces usually just extend the public version by one by 1 or 2 methods, I opted to reuse the same implementation classes for code reuse.

Here's an example:

//Company.PublicInterfaces.dll
namespace Company
{
    namespace PublicInterfaces
    {
        //contains public properties, methods, etc that any developer can use
        public interface class ICompanyProduct
        {
            void GetProductInfo();
            //...etc

        }

        public interface class IOtherStuff
        {
            void GetOtherStuff();
        }

        //...plus many more interface definitions
    }
}

//Company.InternalInterfaces.dll
namespace Company
{
    namespace InternalInterfaces
    {
        //extends the public interface to include secret methods that only company developers can use
        public interface class ICompanyProductInternal : ICompanyProduct
        {
            void GetSecretInfo();
            //...etc
        }

        //...plus many more interface definitions
    }
}

//Company.InterfaceImplementations.dll
#include "Company.InternalInterfaces.h"
namespace Company
{
    namespace InterfaceImplementations
    {
        public ref class CompanyProductImplementer : ICompanyProduct, ICompanyProductInternal
        {
            //implements both interfaces
        }

        public ref class OtherStuffImplementer : IOtherStuff
        {
            //implement other stuff
        }

        //...plus many more interface implementations
    }

}


//C# Test App
using Company.InternalInterfaces
using Company.PublicInterfaces
using Company.InterfaceImplementations

namespace TestApp
{
    class TestAppProgram
    {
        static void Main()
        {
            //write code that uses both interfaces for reusability or for whatever reason....

            ICompanyProductInternal internalProduct = new CompanyProductImplementer();
            IOtherStuff otherStuff = new OtherStuffImplementer();

        }
    }
}

The Company.InternalInterfaces project references the Company.PublicInterfaces project.

Since we can't ship Company.InternalInterfaces.dll, the Company.InterfacesImplementation project can only reference Company.PublicInterfaces or we risk exposing the internal interfaces.

But since class CompanyProductImplementer also implements ICompanyProductInternal, I had to include "Company.InternaInterfaces.h".

When building the C# test app, Visual Studio 2013 complains of

Error: The type 'Company.InternalInterfacs.ICompanyProduct' exists in both 'Company.InterfacesImplementation.dll' and 'Company.InternalInterfaces.dll'

The only way it works is if the implementation project also references the internal interface project, but that's not possible because I'll have to ship the internal interface library as well.

How can I solve this? Obviously this is a typical scenario with regards to many companies wanting to reuse their code for public and internal use in large projects.

hirow
  • 1
  • 3
  • You cannot make CompanyProductImplementer public. Because if you do then you'll always expose the secret. Or to put it another way, your C# code should not ever know anything about the implementation. That's very easy to do, you need a *factory class*. Say CompanyProductFactory. It must have a `public ICompanyProduct Create()` method. Now it is a real secret. https://en.wikipedia.org/wiki/Factory_method_pattern – Hans Passant Nov 03 '16 at 21:59
  • Thank you! Now, given the architecture above, am I going to have to create a 2 different factory classes, one used by users of the public interface and another for the internal interface? I wouldn't want internal users to have to instantiate 2 factories, unless this is considered good practice. My situation is a bit different though given that we're talking about internal interfaces in one assembly extending public interfaces in another, yet they're all implemented by the same classes in another assembly. – hirow Nov 06 '16 at 04:05
  • Hmm, no, don't do that. An internal interface that shouldn't be visible to anybody but internal code does of course force that internal code to be in the same assembly. You can use [InternalsVisibleToAttribute] if you really want to. Just don't. And do ask yourself if you really need that internal interface. You don't, the implementer class is already internal. Keep it simple. – Hans Passant Nov 06 '16 at 05:53
  • The notion of the internal interface is for it to be used as an API by company developers working on secret company applications. The public interface is a watered down version of that API for 3rd party clients. If I get rid of the internal interface, then how will the company develop apps using CompanyProductImplementer after a factory pattern has been implemented? – hirow Nov 06 '16 at 06:48

1 Answers1

0

As I see Company.InternalInterfaces.dll and Company.PublicInterfaces.dll are only interface dlls for the same Company.InterfaceImplementations.dll where all logic implemented. Such configuration typically used when you don't need to hide functionality, but because of usability reasons. In this configuration anyone could access Company.InterfaceImplementations.dll code logic via decompilation and execute any code via reflection.

To hide internal logic you could extract Company.Base.dll with common services used by everyone and split other code from Company.InterfaceImplementations.dll into internal Company.Internal.dll and public Company.Public.dll. In such case you need to distribute Company.Base.dll and Company.Internal.dll or Company.Base.dll and Company.Public.dll.

The other option is to create two build configurations PublicAPI and InternalAPI for Company.InterfaceImplementations.dll. In different configurations, different #define directive should be used. In this case code logic could be included or excluded based on this #define. It will give you ability to exclude internal logic from public dlls.

Error: The type 'Company.InternalInterfacs.ICompanyProduct' exists in both 'Company.InterfacesImplementation.dll' and 'Company.InternalInterfaces.dll'

Any way you should split your code properly to prevent duplicate definitions of the same interfaces in several dlls. Place it in only one dll, e.g. Company.Base.dll.

To properly consumes C++-CLI types defined in other assembles you should use using. E.g:

using "Company.InternalInterfaces.dll" 

instead of

#include "Company.InternalInterfaces.h"

in your Company.InterfaceImplementations.dll assembly. Useful C++-CLI article "How to: Define and Consume Classes and Structs".

Nikita
  • 6,270
  • 2
  • 24
  • 37
  • The idea of adding an internal interface came later after the public interfaces and implementation were completed. Since the public library is massive, I wanted to only extend the public interfaces and derive from the public implementation classes to create the new internal libraries. Why does including the header file of the internal interface within the public implementation assembly cause this error? – hirow Nov 06 '16 at 02:43
  • Thanks, but would it even be possible to hide the implementation of Company.Base.dll? Probably not, if Company.Internal.dll and Company.Public.dll will derive from it. I wouldn't want users to use the base library, only the derived libraries. How would that work? – hirow Nov 06 '16 at 08:26
  • @hirow The problem with header happens because when you include it in your cpp, compiler consumes it and defines the class type declared in header file. To properly consumes C++-CLI types defined in other assembles you should use `using "Company.InternalInterfaces.dll"` instead of `#include "Company.InternalInterfaces.h"`in your *Company.InterfaceImplementations.dll*. Read more about it in article [How to: Define and Consume Classes and Structs](https://msdn.microsoft.com/en-us/library/ke3a209d.aspx). – Nikita Nov 06 '16 at 22:17
  • @hirow Yes, API users will be able to access `Company.Base.dll` directly if you have `Company.Base.dll`, `Company.Internal.dll`and `Company.Public.dll`. To prevent this you could use [IL merge](http://stackoverflow.com/questions/8077570/how-to-merge-multiple-assemblies-into-one) to get only two assemblies `Company.Internal.dll`and `Company.Public.dll` with everything inside them. Some IL merges able to change visibility from `public` to `internal` during merge. – Nikita Nov 06 '16 at 22:25