У меня была такая же проблема с этой ошибкой. Я не смог обнаружить project1 из project2. Я добавил ссылку из проекта 2 в проект 1, но она все еще не работала. Затем я выгрузил project1, удалил его из решения. Затем я добавил его снова, сделал ссылку из проекта 2 и та да ... это сработало .....: -)
При возврате информации в структуре стандартный метод заключается в передаче указателя на структуру в качестве параметра метода. Метод заполняет элементы структуры, а затем возвращает какой-либо код состояния (или логическое значение). Так что вы, вероятно, захотите изменить свой метод C ++, чтобы он принимал SYSTEM_OUTPUT * и возвращал 0 в случае успеха или кода ошибки:
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;
}
}
}
кто выделил память для структуры? Вы не можете удалить собственную память из управляемой кучи. Вообще говоря, собственная DLL должна выделяться в куче COM, если она ожидает, что вызывающий объект освободит память или вернет интерфейс обратного вызова, такой как IMalloc, для освобождения возвращаемой памяти. Это означает, что вам нужно получить адрес памяти результата как IntPtr и использовать System.Runtime.InteropServices.Marshal для копирования данных из собственной в управляемую кучу (может быть в структуру) перед освобождением памяти.
Отредактируйте обновленную сигнатуру функции. : использовать общедоступный статический extern int getStatus (ref SYSTEM_OUTPUT output); Вы не выделяете память в куче COM в собственной функции, поэтому в этом нет необходимости.
[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; // ... }
[MarshalAs(UnmanagedType.LPStr)] public String VersionStr;
Фактически вы не маршалируете какие-либо данные управляемой стороне. Когда вы объявляете output
на управляемой стороне, его значение по умолчанию - null
. Затем на неуправляемой стороне вы никогда не выделяете память для вывода
. Вы должны выделить некоторую неуправляемую память, передать указатель на эту память вашей функции dll, а затем упорядочить указатель для этой памяти в вашей структуре:
[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
}
}
}
Изменить:
Ваша проблема может заключаться в разнице между упаковка стратегий. Я обновил определение структуры.
То, что вы пытаетесь сделать, возможно, но я думаю, что вы решаете неправильную проблему.
Почему бы не прочитать файл с отображением памяти прямо из C #? Взгляните на Winterdom.IO.FileMap
Я использовал его, и он отлично работает.
MemoryMappedFile file = MemoryMappedFile.Open(FileMapRead, name);
using (Stream stream = memoryMappedFile.MapView(MapAccess.FileMapAllAccess, 0, length))
{
// here read the information that you need
}
На этом вы еще не закончили - вам все равно нужно преобразовать байтовый буфер в структуру, но вы все на управляемой стороне, и будет легче.
Думали ли вы о добавлении сборки C ++ / CLI в свой проект? Это чрезвычайно простой и эффективный способ преодолеть разрыв между управляемым и неуправляемым кодом. Я сам довольно часто им пользуюсь.
РЕДАКТИРОВАТЬ: Я переписываю весь этот ответ.
Я взял весь ваш код C ++ и C #, бросил его в решение и запустил - и все у меня работает. У меня не было вашего конкретного материала для отображения памяти, поэтому я смоделировал его, заполнив pBuf некоторыми поддельными данными, и все вернулось в норму; и возвращаемое значение, и структура вывода верны.
Что-то не так с настройками вашего проекта? Звучит глупо, но вы упомянули запуск и отладку неизмененного кода; вы ведь создаете DLL?