Вы хотите сериализация .
В качестве примечания, ваше заявление:
Что я действительно хотел бы сделать, это обработать методы alloc и free в конструкторе и деструкторе ExceptionWrapper, но структуры могут это сделать 'у него нет конструкторов или деструкторов.
не соответствует действительности.struct
s в C # может иметь и делать иметь конструктор (ы), просто не позволяя пользователю явно объявить конструктор без параметров .То есть, например, вы можете объявить конструктор, который принимает Exception
.Для деструкторов, которые не используются широко в управляемом коде, вы должны реализовать IDisposable
, если ваш класс будет содержать некоторые неуправляемые ресурсы.
Exception
не является blittable, вы не можете упорядочить его так, как вы описали, но он может быть сериализован, так как байтовый массив делает возможным взаимодействие.Я прочитал ваш другой вопрос:
Последствия создания исключения в делегате неуправляемого обратного вызова
и некоторые из них используются в вашем коде.Давайте иметь два проекта, один для управляемого и другой для неуправляемого кода.Вы можете создать их со всеми настройками по умолчанию, но обратите внимание, что битность исполняемых образов должна быть одинаковой.Для каждого проекта требуется изменить только один файл:
Управляемое консольное приложение - Program.cs:
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.InteropServices;
using System.IO;
using System;
namespace ConsoleApp1 {
class Program {
[DllImport(@"C:\Projects\ConsoleApp1\Debug\MyDll.dll", EntryPoint = "?return_callback_val@@YGHP6AHXZ@Z")]
static extern int return_callback_val(IntPtr callback);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int CallbackDelegate();
static int Callback() {
try {
throw new Exception("something went wrong");
}
catch(Exception e) {
UnmanagedHelper.SetLastException(e);
}
return 0;
}
static void Main() {
CallbackDelegate @delegate = new CallbackDelegate(Callback);
IntPtr callback = Marshal.GetFunctionPointerForDelegate(@delegate);
int returnedVal = return_callback_val(callback);
var e = UnmanagedHelper.GetLastException();
Console.WriteLine("exception: {0}", e);
}
}
}
namespace ConsoleApp1 {
public static class ExceptionSerializer {
public static byte[] Serialize(Exception x) {
using(var ms = new MemoryStream { }) {
m_formatter.Serialize(ms, x);
return ms.ToArray();
}
}
public static Exception Deserialize(byte[] bytes) {
using(var ms = new MemoryStream(bytes)) {
return (Exception)m_formatter.Deserialize(ms);
}
}
static readonly BinaryFormatter m_formatter = new BinaryFormatter { };
}
}
namespace ConsoleApp1 {
public static class UnmanagedHelper {
[DllImport(@"C:\Projects\ConsoleApp1\Debug\MyDll.dll", EntryPoint = "?StoreException@@YGHHQAE@Z")]
static extern int StoreException(int length, byte[] bytes);
[DllImport(@"C:\Projects\ConsoleApp1\Debug\MyDll.dll", EntryPoint = "?RetrieveException@@YGHHQAE@Z")]
static extern int RetrieveException(int length, byte[] bytes);
public static void SetLastException(Exception x) {
var bytes = ExceptionSerializer.Serialize(x);
var ret = StoreException(bytes.Length, bytes);
if(0!=ret) {
Console.WriteLine("bytes too long; max available size is {0}", ret);
}
}
public static Exception GetLastException() {
var bytes = new byte[1024];
var ret = RetrieveException(bytes.Length, bytes);
if(0==ret) {
return ExceptionSerializer.Deserialize(bytes);
}
else if(~0!=ret) {
Console.WriteLine("buffer too small; total {0} bytes are needed", ret);
}
return null;
}
}
}
Библиотека неповрежденных классов - MyDll.cpp:
// MyDll.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#define DLLEXPORT __declspec(dllexport)
#define MAX_BUFFER_LENGTH 4096
BYTE buffer[MAX_BUFFER_LENGTH];
int buffer_length;
DLLEXPORT
int WINAPI return_callback_val(int(*callback)(void)) {
return callback();
}
DLLEXPORT
int WINAPI StoreException(int length, BYTE bytes[]) {
if (length<MAX_BUFFER_LENGTH) {
buffer_length=length;
memcpy(buffer, bytes, buffer_length);
return 0;
}
return MAX_BUFFER_LENGTH;
}
DLLEXPORT
int WINAPI RetrieveException(int length, BYTE bytes[]) {
if (buffer_length<1) {
return ~0;
}
if (buffer_length<length) {
memcpy(bytes, buffer, buffer_length);
return 0;
}
return buffer_length;
}
С помощью этого кода вы можете сначала сериализовать исключение, а затем десериализовать его в любое более позднее время дляизвлекает объект, который представляет исходное исключение - просто ссылка не будет такой же, как оригинал, как и объекты, на которые она ссылается.
Я бы добавил несколько примечаний к коду:
Имя dll и точку входа метода DllImport
должны быть изменены как ваша реальная сборка, я не сделалманипулировать искаженными именами.
Unmanaged Helper
- это просто демонстрация взаимодействия с примерами неуправляемых методов StoreException
и RetrieveException
;вам не нужно писать код, например, как я с ними справляюсь, возможно, вы захотите создать свой собственный дизайн.
Если вы хотите десериализовать исключение в неуправляемом коде, вы, возможно, захотите создать свой собственный модуль форматирования, а не BinaryFormatter
.Я не знаю существующую реализацию спецификации Microsoft в не-C ++ C ++.
Если вы хотите построить код на C вместо C ++, вам придется изменить параметр Compile As
(Visual Studio) и переименовать MyDll.cpp вMyDll.c;также обратите внимание, что искаженные имена будут похожи на _StoreException@8
;используйте DLL Export Viewer или шестнадцатеричные редакторы, чтобы найти точное имя.