Das InternalsVisibleTo-Attribut funktioniert nicht

73

Ich versuche, das InternalsVisibleToAssembly-Attribut zu verwenden, um meine internen Klassen in einer .NET-Klassenbibliothek für mein Unit-Test-Projekt sichtbar zu machen. Aus irgendeinem Grund erhalte ich immer wieder die Fehlermeldung:

Auf 'MyClassName' kann aufgrund seiner Schutzstufe nicht zugegriffen werden

Beide Assemblys sind signiert und ich habe den richtigen Schlüssel in der Attributdeklaration aufgeführt. Irgendwelche Ideen?

skb
quelle
1
Können Sie das, was Sie für das InternalsVisibleTo-Attribut haben, für die Klasse veröffentlichen, die Sie verfügbar machen möchten? Schwer zu sagen, was falsch ist, ohne zu sehen, was Sie sehen.
Eric Schoonover

Antworten:

102

Sind Sie absolut sicher, dass Sie den richtigen öffentlichen Schlüssel im Attribut angegeben haben? Beachten Sie, dass Sie den vollständigen öffentlichen Schlüssel angeben müssen, nicht nur das Token für den öffentlichen Schlüssel. Es sieht ungefähr so ​​aus:

[assembly: InternalsVisibleTo("MyFriendAssembly,
PublicKey=0024000004800000940000000602000000240000525341310004000001000100F73
F4DDC11F0CA6209BC63EFCBBAC3DACB04B612E04FA07F01D919FB5A1579D20283DC12901C8B66
A08FB8A9CB6A5E81989007B3AA43CD7442BED6D21F4D33FB590A46420FB75265C889D536A9519
674440C3C2FB06C5924360243CACD4B641BE574C31A434CE845323395842FAAF106B234C2C140
6E2F553073FF557D2DB6C5")]

Es sind ungefähr 320 hexadezimale Ziffern. Sie sind sich nicht sicher, warum Sie den vollständigen öffentlichen Schlüssel angeben müssen. Möglicherweise ist es für jemanden mit nur dem Token für den öffentlichen Schlüssel, das in anderen Assemblyreferenzen verwendet wird, einfacher, die Identität der befreundeten Assembly zu fälschen.

Joe
quelle
50
Um den öffentlichen Schlüssel der Freundesversammlung "sn -Tp MyFriendAssembly" zu erhalten
Ian G
@ Tyler: Deshalb solltest du @ Leute. Ich füge unten eine Antwort hinzu, die ein VS-Makro enthält, mit dem das IVT für ein Projekt in Ihrer Lösung generiert werden kann.
12
Um den Kommentar von @Ian G zu verdeutlichen, öffnen Sie die Eingabeaufforderung von Visual Studio und ändern Sie das Verzeichnis (CD) in das entsprechende Verzeichnis, das die Assembly-DLL-Datei 'friend' enthält. Geben Sie an der Eingabeaufforderung sn.exe -Tp NameOfAssemblyoder sn - Tp NameOfAssemblywie Ian sagte ein. Hierbei wird das Strong Name Tool von Microsoft verwendet, um den vollständigen öffentlichen Schlüssel aus der Assembly zu finden und anzuzeigen. Wenn Sie nur eine .pub-Datei haben, die Ihren Schlüssel enthält, können Sie diese mit extrahieren sn.exe -p NameOfKeyFile.snk NameOfKeyPairFile.pub. Dadurch wird die Schlüsseldatei NameOfKeyFile.snk erstellt, mit der Sie die vorherigen Anweisungen befolgen können.
Sheridan
4
Zumindest bei VS2012 ist es sehr wichtig, dass das [assembly:InternalsVisibleTo("...")]Attribut trotz der offensichtlichen Formatierung des Beispiels hier und in der MSDN-Dokumentation in einer einzelnen Zeile steht . Sie können die Ausgabe des öffentlichen Schlüssels nicht einfach kopieren und einfügen sn.exe -Tp MyFriendAssemblyund dann ein @"multi line string"(beachten Sie das @ -Zeichen) ...does not contain a definition for 'X' and no extension method 'X' ... could be foundverwenden. Andernfalls beschwert sich der Compiler, wenn Sie versuchen, über MyFriendAssembly auf Ihre Interna zuzugreifen.
AlwaysLearning
Stellen Sie außerdem sicher, dass Sie im Unit Test-Projekt keine neue Schlüsseldatei erstellen. Sie müssen die Schlüsseldatei aus dem Hauptprojekt laden. Ich war ein bisschen langsam und tat das (zum ersten Mal) und machte ein bisschen Fluchen, um herauszufinden, warum es immer noch nicht funktionierte. Eine sehr einfache Möglichkeit, den öffentlichen Schlüssel abzurufen , finden Sie im MSDN BLOG . Dann ersetzen Sie das Argument -T $(TargetPath)durch-Tp $(TargetPath)
berühmte Kaneis
40

Ein weiteres mögliches "gotcha": Der Name der Freund-Assembly, den Sie in angeben, InternalsVisibleToAttributemuss genau mit dem Namen Ihrer Freund-Assembly übereinstimmen, wie in den Projekteigenschaften des Freundes (auf der Registerkarte Anwendung) angegeben.

In meinem Fall hatte ich ein Projekt Thingamajigund ein Begleitprojekt ThingamajigAutoTests(Namen wurden geändert, um die Schuldigen zu schützen), die beide nicht signierte Baugruppen hervorbrachten. Ich habe das Attribut ordnungsgemäß [assembly: InternalsVisibleTo( "ThingamajigAutoTests" )]zur Datei Thingamajig \ AssemblyInfo.cs hinzugefügt und die Attribute AssemblyKeyFileund AssemblyKeyNamewie oben angegeben auskommentiert . Das ThingamajigProjekt lief einwandfrei, aber seine internen Mitglieder weigerten sich hartnäckig, im Autotest-Projekt aufzutauchen.

Nach ThingamajigAutoTestslangem Kopfkratzen überprüfte ich die Projekteigenschaften erneut und stellte fest, dass der Assemblyname als "ThingamajigAutoTests.dll" angegeben wurde. Bingo - Ich habe dem Assemblernamen im InternalsVisibleToAttribut die Erweiterung ".dll" hinzugefügt , und die Teile wurden zusammengefügt.

Manchmal sind es die kleinsten Dinge ...

John Beyer
quelle
2
+1 als Ihr Kommentar half mir: In meinem Fall hatte ich die Assembly irgendwann umbenannt, aber VS hatte den Assembly-Namen und den Standard-Namespace in den Projekteigenschaften als die alten Werte
belassen
2
Hmm. Genau das Gegenteil für mich. Ich musste die ".dll" entfernen, damit die Lösung erstellt werden konnte.
kmote
Ich kann bestätigen, dass @kmote richtig ist: Ich musste die ".dll" entfernen, damit die Lösung erstellt werden konnte (und Intellisense funktioniert). (.NET 4.5, VS2013, nicht signierte Assemblys) Sehr seltsames Verhalten. Diese Frage und all ihre Antworten sind großartig, um Probleme mit diesem scheinbar problematischen Attribut zu lösen.
Davidbak
Mit meiner Tippgenauigkeit bin ich ziemlich zum Scheitern verurteilt, wenn es darum geht, dieses Attribut richtig zu machen. Hilfreicher Tipp!
Timmyl
+1 Dies ist auch bei Leerzeichen der Fall. Warum mein Assemblername ein Leerzeichen enthält, weiß ich nicht, aber das hat es behoben. : D
user3797758
37

Wenn Ihre Assemblys nicht signiert sind, aber immer noch der gleiche Fehler angezeigt wird, überprüfen Sie Ihre AssemblyInfo.cs-Datei auf eine der folgenden Zeilen:

[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

Auf der Registerkarte "Eigenschaften" wird Ihre Baugruppe weiterhin als vorzeichenlos angezeigt, wenn eine (oder beide) dieser Zeilen vorhanden sind. Das Attribut "InternalsVisibleTo" behandelt eine Baugruppe mit diesen Zeilen jedoch als stark signiert. Löschen Sie einfach diese Zeilen (oder kommentieren Sie sie aus), und es sollte für Sie gut funktionieren.

Skimedic
quelle
1
Danke, das war mein Problem, und nirgendwo sonst konnte ich diese Antwort finden.
Andre
1
Dies rettete mich, nachdem ich beim Aufruf von sn.exe -Tp MyAssembly.dll
Dunc
12

Es ist erwähnenswert, dass Sie Folgendes verwenden müssen, wenn die Assembly "friend" (Tests) in C ++ / CLI und nicht in C # / VB.Net geschrieben ist:

#using "AssemblyUnderTest.dll" as_friend

anstelle einer Projektreferenz oder der üblichen #usingAussage. Aus irgendeinem Grund gibt es in der Projektreferenz-Benutzeroberfläche keine Möglichkeit, dies zu tun.

Colin Desmond
quelle
9

Sie können das AssemblyHelper-Tool verwenden , das die InternalsVisibleTo-Syntax für Sie generiert. Hier ist der Link zur neuesten Version . Beachten Sie nur, dass dies nur für stark benannte Assemblys funktioniert.

Vadim
quelle
Seltsamerweise funktioniert es auch in meiner .NET Core 2.2-Testanwendung immer noch nicht
Prime By Design
5

Hier ist ein Makro, mit dem ich dieses Attribut schnell generiere. Es ist ein bisschen hacky, aber es funktioniert. Auf meiner Maschine. Wenn die zuletzt signierte Binärdatei aktiviert ist /bin/debug. Usw. Zweideutigkeit usw. Wie auch immer, Sie können sehen, wie es den Schlüssel bekommt, so dass Sie einen Hinweis geben. Korrigieren / verbessern Sie, wie es Ihre Zeit erlaubt.

Sub GetInternalsVisibleToForCurrentProject()
    Dim temp = "[assembly:  global::System.Runtime.CompilerServices." + _
               "InternalsVisibleTo(""{0}, publickey={1}"")]"
    Dim projs As System.Array
    Dim proj As Project
    projs = DTE.ActiveSolutionProjects()
    If projs.Length < 1 Then
        Return
    End If

    proj = CType(projs.GetValue(0), EnvDTE.Project)
    Dim path, dir, filename As String
    path = proj.FullName
    dir = System.IO.Path.GetDirectoryName(path)
    filename = System.IO.Path.GetFileNameWithoutExtension(path)
    filename = System.IO.Path.ChangeExtension(filename, "dll")
    dir += "\bin\debug\"
    filename = System.IO.Path.Combine(dir, filename)
    If Not System.IO.File.Exists(filename) Then
        MsgBox("Cannot load file " + filename)
        Return
    End If
    Dim assy As System.Reflection.Assembly
    assy = System.Reflection.Assembly.Load(filename)
    Dim pk As Byte() = assy.GetName().GetPublicKey()
    Dim hex As String = BitConverter.ToString(pk).Replace("-", "")
    System.Windows.Forms.Clipboard.SetText(String.Format(temp, assy.GetName().Name, hex))
    MsgBox("InternalsVisibleTo attribute copied to the clipboard.")
End Sub

quelle
4

Sie müssen den Kompilierungsschalter / out: verwenden, wenn Sie die Friend-Assembly kompilieren (die Assembly, die das InternalsVisibleTo-Attribut nicht enthält).

Der Compiler muss den Namen der zu kompilierenden Assembly kennen, um zu bestimmen, ob die resultierende Assembly als Friend-Assembly betrachtet werden soll.

Asche
quelle
Standardmäßig fügen die Microsoft C # -Ziele die /outOption hinzu, wenn die .msbuild-Datei die folgenden Zeilen enthält : <PropertyGroup> <AssemblyName>YourFriendAssemblyName</AssemblyName> </PropertyGroup>. Dies kann auch in den Projekteigenschaften auf der Registerkarte "Anwendung" im Feld "Baugruppenname" festgelegt werden.
Suzanne Dupéron
4

Zusätzlich zu all dem kann das Problem gelöst werden , wenn alles korrekt zu sein scheint, die Friend-Assembly sich jedoch hartnäckig weigert, Interna zu sehen. Ein erneutes Laden der Lösung oder ein Neustart von Visual Studio können das Problem lösen.

Alex J.
quelle
Das Neuladen der beiden Projekte hat es für mich in Visual Studio 2017
behoben
3

Sie müssen einen neuen vollständigen öffentlichen Schlüssel für die Assembly generieren und dann das Attribut für die Assembly angeben.

[assembly: InternalsVisibleTo("assemblyname,
PublicKey="Full Public Key")]

Führen Sie die folgenden MSDN- Schritte aus, um aus Visual Studio einen neuen vollständigen öffentlichen Schlüssel für die Assembly zu generieren.

So fügen Sie dem Menü "Extras" ein Element "Öffentlichen Schlüssel abrufen" hinzu

Klicken Sie in Visual Studio im Menü Extras auf Externe Tools .

Klicken Sie im Dialogfeld Externe Tools auf Hinzufügen und geben Sie im Titelfeld Get Assembly Public Key ein.

Füllen Sie das Befehlsfeld aus, indem Sie zu sn.exe navigieren. Es wird normalerweise am folgenden Speicherort installiert: C: \ Programme (x86) \ Microsoft SDKs \ Windows \ v7.0a \ Bin \ x64 \ sn.exe .

Geben Sie im Feld Argumente Folgendes ein (Groß- und Kleinschreibung beachten ): -Tp $ (TargetPath) . Aktivieren Sie das Kontrollkästchen Ausgabefenster verwenden.

Klicken Sie auf OK . Der neue Befehl wird dem Menü Extras hinzugefügt.

Wenn Sie das Token für den öffentlichen Schlüssel der Assembly benötigen, die Sie entwickeln, klicken Sie im Menü Extras auf den Befehl Öffentlichen Schlüssel für Assembly abrufen. Das Token für den öffentlichen Schlüssel wird im Ausgabefenster angezeigt.

Murugan Sivananantha Perumal
quelle
Visual Studio 2019 Pfad istC:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\sn.exe
Vishal
und Sie können die sdk hier herunterladen developer.microsoft.com/en-us/windows/downloads/windows-10-sdk
Walter Vehoeven
3

In meinem Fall mit VS.Net 2015, musste ich unterzeichnen BEIDE Baugruppen (wenn mindestens 1 Montage unterzeichnet werden soll oder Sie wollen Bezug auf den öffentlichen Schlüssel der Assembly).

In meinem Projekt wurde überhaupt nicht signiert. Also begann ich, meiner Testbibliothek einen Vorzeichenschlüssel hinzuzufügen und das InternalsVisibleTo-Attribut in der Basisbibliothek meines Projekts zu verwenden. Aber VS.Net erklärte immer, dass es nicht auf die Friend-Methoden zugreifen konnte.

Als ich anfing, die Basisbibliothek zu signieren (es kann der gleiche oder ein anderer Signaturschlüssel sein - solange Sie die Basisbibliothek signieren), konnte VS.Net sofort wie erwartet arbeiten.

Jochen
quelle
2

Frühere Antworten mit PublicKey haben funktioniert: (Visual Studio 2015: MUSS in einer Zeile stehen, andernfalls wird beanstandet, dass die Assemblyreferenz ungültig ist oder nicht referenziert werden kann. PublicKeyToken hat nicht funktioniert.)

[assembly: InternalsVisibleTo("NameSpace.MyFriendAssembly, PublicKey=0024000004800000940000000602000000240000525341310004000001000100F73F4DDC11F0CA6209BC63EFCBBAC3DACB04B612E04FA07F01D919FB5A1579D20283DC12901C8B66A08FB8A9CB6A5E81989007B3AA43CD7442BED6D21F4D33FB590A46420FB75265C889D536A9519674440C3C2FB06C5924360243CACD4B641BE574C31A434CE845323395842FAAF106B234C2C1406E2F553073FF557D2DB6C5")]

Danke an @Joe

So erhalten Sie den öffentlichen Schlüssel der Freundesversammlung:

sn -Tp path\to\assembly\MyFriendAssembly.dll

Innerhalb einer Developper-Eingabeaufforderung (Start> Programme> Visual Studio 2015> Visual Studio-Tools> Entwickler-Eingabeaufforderung für VS2015). Vielen Dank an @Ian G.

Der letzte Schliff, der es nach dem oben Gesagten für mich zum Funktionieren brachte, war, mein Bibliotheksprojekt für Freunde genauso zu signieren, wie das Projekt der zu teilenden Bibliothek signiert ist. Da es sich um eine neue Testbibliothek handelte, war sie noch nicht signiert.

Micaël
quelle
2

Eine weitere Möglichkeit, die je nach Schreibweise Ihres Codes schwierig zu finden sein kann.

  1. Sie rufen eine in X definierte interne Methode aus einer anderen Assembly Y auf
  2. Die Methodensignatur verwendet interne Typen, die in Z definiert sind
  3. Sie müssen dann [InternalsVisibleTo] in X UND in Z hinzufügen

Zum Beispiel:

// In X
internal static class XType
{
    internal static ZType GetZ() { ... }
}

// In Y:
object someUntypedValue = XType.GetZ();

// In Z:
internal class ZType { ... }

Wenn Sie es wie oben geschrieben haben und sich nicht direkt in Y auf ZType beziehen, nachdem Sie Y als Freund von X hinzugefügt haben, sind Sie möglicherweise verwirrt, warum Ihr Code immer noch nicht kompiliert wird.

Der Kompilierungsfehler könnte in diesem Fall definitiv hilfreicher sein.

Tatiana Racheva
quelle
2

Ich schreibe das aus Frustration. Stellen Sie sicher, dass die Assembly, auf die Sie Zugriff gewähren, wie erwartet benannt ist.

Ich habe mein Projekt umbenannt, aber dadurch wird der Baugruppenname nicht automatisch aktualisiert. Klicken Sie mit der rechten Maustaste auf Ihr Projekt und klicken Sie auf Eigenschaften . Stellen Sie unter Anwendung sicher, dass der Assemblyname und der Standardnamespace Ihren Erwartungen entsprechen.

Kameron Kincade
quelle
1

Gilt nur, wenn Sie nicht signierte Assemblys als nicht signierte Assembly behalten möchten (und sie aus mehreren Gründen nicht signieren möchten):

Es gibt noch einen weiteren Punkt: Wenn Sie Ihre Basisbibliothek von VS.Net in ein lokales Verzeichnis kompilieren, funktioniert dies möglicherweise wie erwartet.

ABER: Sobald Sie Ihre Basisbibliothek auf einem Netzwerklaufwerk kompilieren, gelten Sicherheitsrichtlinien und die Assembly kann nicht erfolgreich geladen werden. Dies führt erneut dazu, dass VS.NET oder der Compiler fehlschlägt, wenn nach der PublicKey-Übereinstimmung gesucht wird.

ENDLICH ist es möglich, nicht signierte Assemblys zu verwenden: https://msdn.microsoft.com/en-us/library/bb384966.aspx Sie müssen sicherstellen, dass BEIDE Assemblys NICHT SIGNIERT sind und das Assembly-Attribut keine PublicKey-Informationen enthalten muss:

<Assembly: InternalsVisibleTo("friend_unsigned_B")>

Jochen
quelle
1

Ich hatte das gleiche Problem. Keine der Lösungen funktionierte.

Schließlich wurde festgestellt, dass das Problem darauf zurückzuführen ist, dass Klasse X die interne Schnittstelle Y explizit implementiert.

Die Methode X.InterfaceMethod war nicht verfügbar, obwohl ich keine Ahnung habe, warum.

Die Lösung bestand darin, (X als YourInterface) .InterfaceMethod in die Testbibliothek zu übertragen, und dann funktionierten die Dinge.

Wächter
quelle
0

Nebenbei bemerkt, wenn Sie den öffentlichen Schlüssel einfach erhalten möchten, ohne sn verwenden zu müssen und seine Optionen herauszufinden, können Sie das praktische Programm hier herunterladen . Es bestimmt nicht nur den öffentlichen Schlüssel, sondern erstellt auch die Zeile "Assembly: InternalsVisibleTo ...", die zum Kopieren in die Zwischenablage und zum Einfügen in Ihren Code bereit ist.

Bill W.
quelle
0

Ich habe gerade ein ähnliches Problem mit dem InternalsVisibleToAttribut gelöst . Alles schien richtig zu sein und ich konnte nicht herausfinden, warum die interne Klasse, die ich anstrebte, immer noch nicht zugänglich war.

Durch Ändern der Groß- und Kleinschreibung des Schlüssels wurde das Problem behoben.

Schlüsselschärfer
quelle
0

Wenn Sie mehr als eine Assembly haben, auf die verwiesen wird, überprüfen Sie, ob alle erforderlichen Assemblys das Attribut InternalsVisibleTo haben. Manchmal ist es nicht offensichtlich und keine Meldung, dass Sie dieses Attribut einer anderen Assembly hinzufügen müssen.

VladExL
quelle
0

1- Signieren Sie das Testprojekt: Gehen Sie in Visual Studio zum Eigenschaftenfenster des Testprojekts und signieren Sie die Assembly, indem Sie das Kontrollkästchen mit demselben Satz auf der Registerkarte Signieren aktivieren.

2- Erstellen Sie einen PublicKey für das Testprojekt: Öffnen Sie die Visual Studio-Eingabeaufforderung (z. B. die Entwickler-Eingabeaufforderung für VS 2017). Wechseln Sie in den Ordner, in dem sich die DLL-Datei des Testprojekts befindet. Erstellen Sie einen öffentlichen Schlüssel über sn.exe:

sn -Tp TestProject.dll

Beachten Sie, dass das Argument -Tp, aber nicht -tp ist.

3- Einführung in das PublicKey des Projekt getestet werden: Gehen Sie auf die Datei AssemblyInfo.cs in dem Projekt getestet werden und diese Zeile mit dem im vorherigen Schritt erstellt PublicKey hinzu:

[assembly: InternalsVisibleTo (“ TestProjectAssemblyName , PublicKey = 2066212d128683a85f31645c60719617ba512c0bfdba6791612ed56350368f6cc40a17b4942ff16cda9e760684658fa3f357c137a1005b04cb002400000480000094000000060200000024000052534131000400000100010065fe67a14eb30ffcdd99880e9d725f04e5c720dffc561b23e2953c34db8b7c5d4643f476408ad1b1e28d6bde7d64279b0f51bf0e60be2d383a6c497bf27307447506b746bd2075" )]

Vergessen Sie nicht, den oben genannten PublicKey durch Ihren zu ersetzen.

4- Machen Sie die private Methode intern: Ändern Sie im zu testenden Projekt den Zugriffsmodifikator der Methode in intern.

interne statische Leere DoSomething () {...}

user3029066
quelle