Friend Assemblies (C++)

You can make all types at namespace or global scope in an assembly component accessible to one or more client assemblies or .netmodules.

Remarks

To make types at namespace or global scope in an assembly component accessible to a client assembly or .netmodule, do the following:

  • In the component, specify an assembly attribute: InternalsVisibleToAttribute, and pass the name of the client assembly or .netmodule that will access all types at namespace or global scope in the component. You can specify multiple client assemblies or .netmodules by specifying additional attributes.

  • In the client assembly or .netmodule, when referencing the component assembly (with #using), pass the as_friend attribute. If you specify the as_friend attribute for an assembly that does not specify InternalsVisibleToAttribute, a runtime exception will be thrown if you try to access a type at namespace or global scope in the component.

A build error will result if the assembly containing the InternalsVisibleToAttribute attribute does not have a strong name, but the client assembly (uses the as_friend attribute) does.

Although types at namespace and global scope can be visible to a client assembly or .netmodule, member accessibility is still in effect. For example, you will not be able to access a private member.

Access to all types in an assembly must be explicitly granted. For example, assembly C will not have access to all types in assembly A if assembly C references assembly B and where assembly B has access to all types in assembly A.

For information on how to specify the accessibility of types outside an assembly, see Type Visibility.

For information on signing (giving a strong name) to an assembly built using the Visual C++ compiler, see Strong Name Assemblies (Assembly Signing).

An alternative to using the friend assemblies feature, you can use StrongNameIdentityPermission to restrict access to individual types.

Example

Description

The following sample defines a component that specifies a client assembly that will have access to all types in the component.

Code

// friend_assemblies.cpp
// compile with: /clr /LD
using namespace System::Runtime::CompilerServices;
using namespace System;
// an assembly attribute, not bound to a type
[assembly:InternalsVisibleTo("friend_assemblies_2")];

ref class Class1 {
public:
   void Test_Public() {
      Console::WriteLine("Class1::Test_Public");
   }
};

Example

Description

The following sample access a private type in the component.

Code

// friend_assemblies_2.cpp
// compile with: /clr
#using "friend_assemblies.dll" as_friend

int main() {
   Class1 ^ a = gcnew Class1;
   a->Test_Public();
}

Output

Class1::Test_Public

Example

Description

The following sample defines a component but does not specify a client assembly that will have access to all types in the component.

Note that the component is linked using /opt:noref. This ensures that private types are emitted in the component's metadata, which is not required when the InternalsVisibleTo attribute is present. For more information, see /OPT (Optimizations).

Code

// friend_assemblies_3.cpp
// compile with: /clr /LD /link /opt:noref
using namespace System;

ref class Class1 {
public:
   void Test_Public() {
      Console::WriteLine("Class1::Test_Public");
   }
};

Example

Description

The following sample defines a client that tries to access a private type in a component that does not give access to its private types. Due to the behavior of the runtime, if you want to catch the exception, you must attempt to access a private type in helper function.

Code

// friend_assemblies_4.cpp
// compile with: /clr
#using "friend_assemblies_3.dll" as_friend
using namespace System;

void Test() {
   Class1 ^ a = gcnew Class1;
}

int main() {
   // to catch this kind of exception, use a helper function
   try {
      Test();   
   }
   catch(MethodAccessException ^ e) {
      Console::WriteLine("caught an exception");
   }
}

Output

caught an exception

Example

Description

The following sample shows how to create a strong-name component that specifies a client assembly that will have access to all types in the component.

Note that the component must specify its public key. The following is a suggested sequence of steps to create a key pair and get the public key:

sn -d friend_assemblies.snk

sn -k friend_assemblies.snk

sn -i friend_assemblies.snk friend_assemblies.snk

sn -pc friend_assemblies.snk key.publickey

sn -tp key.publickey

Code

// friend_assemblies_5.cpp
// compile with: /clr /LD /link /keyfile:friend_assemblies.snk
using namespace System::Runtime::CompilerServices;
using namespace System;
// an assembly attribute, not bound to a type

[assembly:InternalsVisibleTo("friend_assemblies_6, PublicKey=00240000048000009400000006020000002400005253413100040000010001000bf45d77fd991f3bff0ef51af48a12d35699e04616f27ba561195a69ebd3449c345389dc9603d65be8cd1987bc7ea48bdda35ac7d57d3d82c666b7fc1a5b79836d139ef0ac8c4e715434211660f481612771a9f7059b9b742c3d8af00e01716ed4b872e6f1be0e94863eb5745224f0deaba5b137624d7049b6f2d87fba639fc5")];

private ref class Class1 {
public:
   void Test_Public() {
      Console::WriteLine("Class1::Test_Public");
   }
};

Example

Description

The following sample access a private type in the strong name component.

Code

// friend_assemblies_6.cpp
// compile with: /clr /link /keyfile:friend_assemblies.snk
#using "friend_assemblies_5.dll" as_friend

int main() {
   Class1 ^ a = gcnew Class1;
   a->Test_Public();
}

Output

Class1::Test_Public

Requirements

Compiler option: /clr

See Also

Concepts

Language Features for Targeting the CLR