Frage

[EDIT] änderte ich die Quelle als von Stephen Martin (fett markiert) vorgeschlagen. Und hinzugefügt, um den Quellcode C ++ als auch.

Ich möchte eine nicht verwaltete Funktion in einem selbst geschriebenen C ++ DLL rufen. Diese Bibliothek liest die Shared-Memory-Maschine für Statusinformationen einer Software von Drittanbietern. Da gibt es ein paar Werte sind, würde Ich mag die Werte in einer Struktur zurückzukehren. Doch innerhalb des struct es wird char [] (Arrays von char mit einer festen Größe). Ich versuche jetzt, dass die Struktur aus dem DLL-Aufruf wie folgt zu erhalten:

[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;
        }
    }
}

werde ich schreiben Code aus der c ++ dll als gut, ich bin sicher, dass es mehr auf die Jagd nach. Die ursprüngliche struct STATUS_DATA hat eine Reihe von vier Instanzen des struct SYSTEM_CHARACTERISTICS und innerhalb dieser Struktur gibt es char[]s, die (noch) nicht aufgefüllt werden, was zu einem fehlerhaften Zeiger. Deshalb versuche ich eine Teilmenge des ersten SYSTEM_CHARACTERISTICS Artikel in STATUS_DATA zu extrahieren.

#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;
}

Jetzt bin immer ich eine leere output Struktur und der Rückgabewert ist nicht 0, wie beabsichtigt. Es ist vielmehr eine wechselnde Zahl mit sieben Ziffern, die lässt mich verwirrt ... Habe ich verwirrt in der dll up? Wenn ich den nicht verwalteten Code ausführbar und debuggen es machen, kann ich sehen, dass output mit den entsprechenden Werten gefüllt wird.

War es hilfreich?

Lösung

Wenn Information in einer Struktur zurückkehr das Standardverfahren ist ein Zeiger auf eine Struktur als Parameter des Verfahrens zu übergeben. Das Verfahren füllt die struct Mitglieder und kehrt dann einen Statuscode (oder boolean) in irgendeiner Form. So möchten Sie wahrscheinlich Ihre C ++ Methode ändern, um eine SYSTEM_OUTPUT * zu nehmen und zurück 0 für Erfolg oder ein Fehlercode:

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;
        }
    }
}

Andere Tipps

  1. Stellen Sie sicher, dass Ihr ReadyForConnect Feld ist nicht auf 4 Bytes gefüllt. In meinem Projekt drehte sie all short int aus (2 Byte) Felder wurden mit Dummy-Bytes auf 4 Byte in nicht verwalteten DLL gefüllt. Wenn das das Problem, sollten Sie marshall die Struktur auf diese Weise:
    [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. Wenn die Saiten ANSI null Strings beendet Sie können sie mit Anmerkungen versehen wie:
  [MarshalAs(UnmanagedType.LPStr)]                   public String VersionStr;

Sie sind Marshalling eigentlich keine Daten über an der verwalteten Seite. Wenn Sie output auf der verwalteten Seite erklären, ist es Standardwert null ist. Dann wird auf der nicht verwalteten Seite, Sie nie einen Speicher für output zuordnen. Sie sollten einige nicht verwalteten Speicher, übergeben Sie den Zeiger auf diesen Speicher auf Ihre DLL-Funktion, dann Marschall den Zeiger für den Speicherplatz auf Ihrer Struktur zuweisen:

[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
        }
    }
}

Edit:

Ihr Problem könnte ein Problem mit dem Unterschied zwischen dem, Verpackung Strategien. Ich habe die Struktur Definition aktualisiert.

EDIT: Ich Umschreiben diese ganze Antwort

.

habe ich alle beide Ihre C ++ und C # -Code, fiel sie in eine Lösung und es lief - und alles funktioniert für mich. Ich hatte nicht Ihr spezielle Speicherzuordnung Zeug, damit ich es simuliert, indem pBuf mit einigen gefälschten Daten füllen, und alles macht es wieder in Ordnung; sowohl der Rückgabewert und die Ausgabe struct korrekt ist.

Könnte etwas mit Ihren Projekteinstellungen falsch sein? Das klingt albern, aber Sie erwähnten Ausführen und Debuggen der unamnaged Code; Sie sind eine dll richtig bauen?

Was sind Sie möglich ist, versuchen zu tun, aber ich denke, Sie das falsche Problem zu lösen.

Warum direkt von C # nicht lesen Sie die Memory-Mapped-Datei? Schauen Sie sich auf Winterdom.IO.FileMap

Ich habe es benutzt und es funktioniert gut.

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

Damit Sie nicht fertig sind -. Sie noch einen Byte-Puffer auf eine Struktur umwandeln müssen, aber sie sind alle auf der verwalteten Seite und es wird einfacher sein

, die den Speicher für die Struktur zugeordnet? Sie können nicht nativen Speicher aus dem verwalteten Heap löschen. Im Allgemeinen die native DLL auf dem COM-Heap reservieren sollte, wenn es um den Anrufer erwarten, dass der Speicher freizugeben, oder eine Callback-Schnittstelle wie IMalloc Rückkehr des zurückkehrenden Speicher freizugeben. Das heißt, Sie müssen das Ergebnis Speicheradresse als IntPtr empfangen und verwenden System.Runtime.InteropServices.Marshal zu verwalteten Heap Daten von nativen zu kopieren (auf eine Struktur sein kann), bevor der Speicher freigegeben wird.

Bearbeiten für die aktualisierte Funktionssignatur: verwenden public static extern int getStatus (ref SYSTEM_OUTPUT Ausgang); Sie sind nicht auf dem COM-Heap in der nativen Funktion Zuweisung, so out nicht notwendig ist.

Haben Sie darüber nachgedacht einen C ++ / CLI Baugruppe zu Ihrem Projekt hinzufügen? Das ist eine extrem einfache und leistungsfähige Art und Weise die Lücke zwischen verwalteten und nicht verwalteten Code zu überbrücken. Ich benutze es ziemlich viel selbst.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top