Question

[EDIT] J'ai changé le source comme suggéré par Stephen Martin (en gras). Et ajouté le code source C ++ également.

J'aimerais appeler une fonction non gérée dans une dll auto-écrite en C ++. Cette bibliothèque lit la mémoire partagée de la machine pour obtenir des informations sur l'état d'un logiciel tiers. Puisqu'il y a deux valeurs, j'aimerais retourner les valeurs dans une structure. Cependant, dans la structure, il y a char [] (tableaux de caractères de taille fixe). J'essaie maintenant de recevoir cette structure de l'appel de la DLL comme ceci:

[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_OUTPUT
{
    UInt16 ReadyForConnect;        

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    String VersionStr;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
    String NameOfFile;    
    // actually more of those
}

public partial class Form1 : Form
{
    public SYSTEM_OUTPUT output;

    [DllImport("testeshm.dll", EntryPoint="getStatus")]
    public extern static int getStatus(out SYSTEM_OUTPUT output);

    public Form1()
    {
        InitializeComponent();           

    }

    private void ReadSharedMem_Click(object sender, EventArgs e)
    {
        try
        {
            label1.Text = getStatus(out output).ToString();
        }
        catch (AccessViolationException ave)
        {
            label1.Text = ave.Message;
        }
    }
}

Je posterai aussi le code de la dll c ++, je suis sûr qu'il y a encore beaucoup à faire. La structure originale STATUS_DATA contient un tableau de quatre instances de la structure SYSTEM_CHARACTERISTICS et contient des char [] , qui ne sont pas rempli (encore), résultant en un mauvais pointeur. C'est pourquoi j'essaie d'extraire un sous-ensemble du premier élément SYSTEM_CHARACTERISTICS dans STATUS_DATA .

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#if defined(_MSC_VER)
#include <windows.h>
#define DLL extern "C" __declspec(dllexport)
#else
#define DLL
#endif

using namespace std;

enum { SYSID_LEN = 1024, VERS_LEN = 128, SCENE_LEN = 1024 };
enum { MAX_ENGINES = 4 };

struct SYSTEM_CHARACTERISTICS
{
    unsigned short  ReadyForConnect;
    char            VizVersionStr[VERS_LEN];
    char            NameOfFile[SCENE_LEN];

    char            Unimplemented[SCENE_LEN]; // not implemented yet, resulting to bad pointer, which I want to exclude (reason to have SYSTEM_OUTPUT)
};

struct SYSTEM_OUTPUT
{
    unsigned short  ReadyForConnect;        
    char            VizVersionStr[VERS_LEN];
    char            NameOfFile[SCENE_LEN];
};

struct STATUS_DATA
{
    SYSTEM_CHARACTERISTICS engine[MAX_ENGINES];
};


TCHAR szName[]=TEXT("E_STATUS");


DLL int getStatus(SYSTEM_OUTPUT* output)
{
    HANDLE hMapFile;
    STATUS_DATA* pBuf;

    hMapFile = OpenFileMapping(
        FILE_MAP_READ,          // read access
        FALSE,                  // do not inherit the name
        szName);                // name of mapping object 

    if (hMapFile == NULL) 
    { 
        _tprintf(TEXT("Could not open file mapping object (%d).\n"), 
            GetLastError());
        return -2;

    } 

    pBuf = (STATUS_DATA*) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);                                          

    if (pBuf == NULL) 
    { 
        _tprintf(TEXT("Could not map view of file (%d).\n"), 
            GetLastError()); 

        CloseHandle(hMapFile);  
        return -1;

    }

    output->ReadyForConnect = pBuf->engine[0].ReadyForConnect;              
    memcpy(output->VizVersionStr, pBuf->engine[0].VizVersionStr, sizeof(pBuf->engine[0].VizVersionStr));
    memcpy(output->NameOfFile, pBuf->engine[0].NameOfFile, sizeof(pBuf->engine[0].NameOfFile));

    CloseHandle(hMapFile);
    UnmapViewOfFile(pBuf);  

    return 0;
}

Maintenant, je reçois une structure output vide et la valeur de retour est différente de 0 comme prévu. C'est plutôt un nombre changeant avec sept chiffres, ce qui me laisse perplexe ... Ai-je foiré dans la dll? Si je fais le code non managé et le débogue, je peux voir que la sortie est remplie avec les valeurs appropriées.

Était-ce utile?

La solution

Lors du retour d'informations dans une structure, la méthode standard consiste à passer un pointeur sur une structure en tant que paramètre de la méthode. La méthode remplit les membres de la structure, puis renvoie un code de statut (ou booléen). Donc, vous voulez probablement changer votre méthode C ++ pour prendre un SYSTEM_OUTPUT * et renvoyer 0 en cas de succès ou un code d'erreur:

public partial class Form1 : Form
{
    public SYSTEM_OUTPUT output;

    [DllImport("testeshm.dll", EntryPoint="getStatus")]
    public extern static int getStatus(out SYSTEM_OUTPUT output);

    public Form1()
    {
        InitializeComponent();           
    }

    private void ReadSharedMem_Click(object sender, EventArgs e)
    {
        try
        {
            if(getStatus(out output) != 0)
            {
                //Do something about error.
            }
        }
        catch (AccessViolationException ave)
        {
            label1.Text = ave.Message;
        }
    }
}

Autres conseils

  1. Assurez-vous que votre champ ReadyForConnect n'est pas rempli jusqu'à 4 octets. Dans mon projet, tous les champs int courts (2 octets) étaient remplis d'octets factices à 4 octets dans une DLL non gérée. Si tel est le problème, vous devriez organiser la structure de cette façon:
    [StructLayout(LayoutKind.Sequential)] 
    public struct SYSTEM_OUTPUT 
    {     
       [MarshalAs(UnmanagedType.I2)] 
       UInt16 ReadyForConnect;
       [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.I1, SizeConst=2)]
       byte[] aligment;          // 2 byte aligment up to 4 bytes margin
       [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]    
       String VersionStr;    
       [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]    
       String NameOfFile;        // ...
    }
  1. Si les chaînes sont des chaînes à terminaison ANSI, vous pouvez les annoter comme suit:
  [MarshalAs(UnmanagedType.LPStr)]                   public String VersionStr;

Vous ne marshalez aucune donnée sur le côté géré. Lorsque vous déclarez output du côté géré, sa valeur par défaut est null . Ensuite, côté non géré, vous n'allouez jamais de mémoire pour sortie . Vous devez allouer de la mémoire non gérée, passer le pointeur sur cette mémoire à votre fonction dll, puis marshaler le pointeur de cette mémoire sur votre struct:

[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct SYSTEM_OUTPUT
{
    UInt16 ReadyForConnect;        

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    String VersionStr;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
    String NameOfFile;    
    // actually more of those
}

public partial class Form1 : Form
{
    public SYSTEM_OUTPUT output;

    [DllImport("testeshm.dll", EntryPoint="getStatus")]
    public extern static int getStatus(IntPtr output);

    public Form1()
    {
        InitializeComponent();           

    }

    private void ReadSharedMem_Click(object sender, EventArgs e)
    {
        IntPtr ptr;
        try
        {
            ptr = Marshall.AllocHGlobal(Marshall.SizeOf(typeof(SYSTEM_OUTPUT)));
            int ret = getStatus(ptr);

            if(ret == 0)
            {
                output = (SYSTEM_OUTPUT)Marshal.PtrToStructure(ptr, typeof(SYSTEM_OUTPUT));
            }

        //do something with output

            label1.Text = ret;
        }
        catch (AccessViolationException ave)
        {
            label1.Text = ave.Message;
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);  //make sure to free the memory
        }
    }
}

Modifier:

Votre problème pourrait être un problème avec la différence entre emballage stratégies. J'ai mis à jour la définition de la structure.

EDIT: Je suis en train de réécrire toute cette réponse.

J'ai pris tout votre code C ++ et C #, je l'ai inséré dans une solution et je l'ai exécuté - et tout fonctionne pour moi. Je n'avais pas vos données de mappage de mémoire spécifiques, alors je l'ai simulé en remplissant pBuf avec de fausses données, et tout le reste bien. la valeur de retour et la structure de sortie sont correctes.

Quelque chose ne va pas avec les paramètres de votre projet? Cela semble idiot, mais vous avez mentionné l'exécution et le débogage du code non endommagé; vous construisez une dll bien?

Qu'est-ce que vous essayez de faire est possible, mais je pense que vous résolvez le mauvais problème.

Pourquoi ne pas lire le fichier mappé en mémoire directement à partir de C #? Consultez Winterdom.IO.FileMap

.

Je l'ai utilisé et cela fonctionne bien.

MemoryMappedFile file = MemoryMappedFile.Open(FileMapRead, name);
using (Stream stream = memoryMappedFile.MapView(MapAccess.FileMapAllAccess, 0, length))
{
    // here read the information that you need   
}

Avec cela, vous n’êtes pas fini - vous devez toujours convertir un tampon d’octets en structure, mais vous êtes tous du côté géré et ce sera plus facile.

qui a alloué la mémoire pour la structure? Vous ne pouvez pas supprimer la mémoire native du segment de mémoire géré. En règle générale, la DLL native doit allouer sur le tas COM si elle s'attend à ce que l'appelant libère de la mémoire, ou renvoyer une interface de rappel telle qu'IMalloc pour libérer la mémoire renvoyée. Cela signifie que vous devez recevoir l'adresse de la mémoire de résultat sous la forme IntPtr et utiliser System.Runtime.InteropServices.Marshal pour copier les données d'un segment de mémoire natif vers un segment de mémoire géré (éventuellement dans une structure) avant de libérer de la mémoire.

Modifier pour la signature de fonction mise à jour: utilise public statique extern int getStatus (réf. sortie SYSTEM_OUTPUT); Vous n'allouez pas sur le tas COM dans la fonction native, la sortie est donc inutile.

Avez-vous envisagé d’ajouter un assemblage C ++ / CLI à votre projet? C'est un moyen extrêmement simple et puissant de combler le fossé entre le code géré et le code non géré. Je l'utilise beaucoup moi-même.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top