80 votes

Qu'est-ce qui pourrait rendre les arguments P / Invoke hors d'usage lorsqu'ils sont passés?

C'est un problème qui se produit spécifiquement sur le BRAS, pas sur x86 ou x64. J'ai eu ce problème signalé par un utilisateur, et a été en mesure de le reproduire à l'aide de UWP sur Raspberry Pi 2 via Windows Ido. J'ai vu ce genre de problème avant incompatibles avec les conventions d'appel, mais je suis en précisant Cdecl dans le P/Invoke déclaration et j'ai essayé en ajoutant explicitement __cdecl sur le natif de côté avec les mêmes résultats. Voici quelques infos:

P/Invoke déclaration (référence):

[DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern FLSliceResult FLEncoder_Finish(FLEncoder* encoder, FLError* outError);

Le C# structs (référence):

internal unsafe partial struct FLSliceResult
{
    public void* buf;
    private UIntPtr _size;

    public ulong size
    {
        get {
            return _size.ToUInt64();
        }
        set {
            _size = (UIntPtr)value;
        }
    }
}

internal enum FLError
{
    NoError = 0,
    MemoryError,
    OutOfRange,
    InvalidData,
    EncodeError,
    JSONError,
    UnknownValue,
    InternalError,
    NotFound,
    SharedKeysStateError,
}

internal unsafe struct FLEncoder
{
}

La fonction dans l'en-tête C (référence)

FLSliceResult FLEncoder_Finish(FLEncoder, FLError*);

FLSliceResult peut être à l'origine de certains des problèmes parce qu'il est renvoyé par la valeur et de a en C++ choses sur elle sur le natif de côté?

Les structs sur le natif de côté réel de l'information, mais pour l'API C, FLEncoder est défini comme un pointeur opaque. Lors de l'appel de la méthode ci-dessus sur x86 et x64 les choses fonctionnent bien, mais sur le BRAS, j'observe ce qui suit. L'adresse du premier argument est l'adresse de la DEUXIÈME argument, et le deuxième argument est null (par exemple, lorsque je me connecte les adresses sur le C#, je reçois, par exemple, 0x054f59b8 et 0x0583f3bc, mais alors sur le natif de côté les arguments sont 0x0583f3bc et 0x00000000). Ce qui pourrait causer ce genre de problème de commande? Quelqu'un a une idée, parce que je suis perplexe...

Voici le code que j'ai exécuter à reproduire:

unsafe {
    var enc = Native.FLEncoder_New();
    Native.FLEncoder_BeginDict(enc, 1);
    Native.FLEncoder_WriteKey(enc, "answer");
    Native.FLEncoder_WriteInt(enc, 42);
    Native.FLEncoder_EndDict(enc);
    FLError err;
    NativeRaw.FLEncoder_Finish(enc, &err);
    Native.FLEncoder_Free(enc);
}

L'exécution d'un C++ application avec le suivant fonctionne très bien:

auto enc = FLEncoder_New();
FLEncoder_BeginDict(enc, 1);
FLEncoder_WriteKey(enc, FLSTR("answer"));
FLEncoder_WriteInt(enc, 42);
FLEncoder_EndDict(enc);
FLError err;
auto result = FLEncoder_Finish(enc, &err);
FLEncoder_Free(enc);

Cette logique peut déclencher l'accident avec la dernière développeur pour construire , mais malheureusement, je n'ai pas encore compris comment fiable pour être en mesure de fournir natif symboles de débogage via Nuget telle qu'elle peut être renforcée par le biais de (seulement de tout compiler à partir des sources semble le faire...) pour la mise au point est un peu difficile parce que les deux natifs et les composants gérés besoin d'être construit. Je suis ouvert aux suggestions sur la façon de rendre cela plus facile, si quelqu'un veut l'essayer. Mais si quelqu'un a vécu cela avant ou a des idées sur les raisons de ce qui se passe, s'il vous plaît ajouter une réponse, merci! Bien sûr, si quelqu'un veut une reproduction de cas (soit facile d'en construire un qui ne fournit pas de source de gué ou un disque dur d'en construire un qui n'), puis de laisser un commentaire mais je ne veux pas passer par le processus de fabrication d'un si personne ne va l'utiliser (je ne suis pas sûr de la façon populaire de l'exécution de Windows trucs sur les BRAS est)

EDIT Intéressante mise à jour: Si je "faux" à la signature, en C# et supprimer le 2ème paramètre, alors le premier est par OK.

EDIT 2 Deuxième intéressante mise à jour: Si je change le C# FLSliceResult définition de la taille d' UIntPtr de ulong alors que les arguments viennent correctement...ce qui n'a pas de sens puisqu' size_t sur le BRAS doit être unsigned int.

EDIT 3 Ajout d' [StructLayout(LayoutKind.Sequential, Size = 12)] à la définition en C# aussi fait ce travail, mais POURQUOI? sizeof(FLSliceResult) en C / C++ pour cette architecture retourne 8 comme il se doit. Réglage de la même taille en C# sont les causes d'un accident, mais un réglage à 12 fait le travail.

EDIT 4 - je être minimisée les cas de test pour que je puisse écrire en C++ de cas de test ainsi. En C# UWP il échoue, mais en C++ UWP il réussit.

EDIT 5 Ici sont les démonté instructions pour les deux C++ et C# pour la comparaison (même si C# je ne suis pas sûr combien de prendre si j'ai commis une erreur sur le côté de la prise de trop)

EDIT 6 une analyse Plus poussée montre que, lors de la "bonne" exécuter lorsque je mentir et de dire que la structure est de 12 octets sur C#, la valeur de retour est adoptée pour le registre r0, avec les deux autres arguments à venir via r1, r2. Toutefois, dans le bad run, c'est décalé afin que les deux arguments sont à venir dans la via r0, r1 et la valeur de retour est quelque part d'autre (pointeur de pile?)

EDIT 7 j'ai consulté l' Appel de Procédure Standard pour l'Architecture ARM. J'ai trouvé cette citation: "Un Type Composite de plus de 4 octets, ou dont la taille ne peut pas être déterminée de manière statique par deux de l'appelant et le destinataire de l'appel, est stockée dans la mémoire à une adresse du passé comme un argument supplémentaire lorsque la fonction a été appelée (§5.5, la règle A. 4). La mémoire à utiliser pour le résultat peut être modifié à tout moment durant l'appel de la fonction." Cela implique que le passage dans r0 est le comportement correct comme argument supplémentaire implique la première (depuis la convention d'appel C ne dispose pas d'un moyen de spécifier le nombre d'arguments). Je me demande si le CLR est source de confusion avec une autre règle fondamentale 64-bit types de données: "Un double-mot de taille moyenne Type de Données Fondamental (p. ex., long, double et 64 bits des conteneurs vecteurs) est retourné dans r0 et r1."

EDIT 8 Ok il y a beaucoup de preuves pointant vers le CLR faire quelque chose de mal ici, j'ai donc déposé un rapport de bug. J'espère que quelqu'un le remarque entre tous les robots automatisés de poster des questions sur ce repo :-S.

1voto

borrrden Points 20950

La question que j'ai déposée sur GH est là depuis assez longtemps. Je crois que ce comportement est simplement un bug et qu’il n’ya plus de temps à examiner.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X