Reputation: 4939
The Question In a Nutshell:
How can I get the size of a user defined structure passed as an object to a class library?
Overview:
The point of this test project is to build a class library that wraps up memory sharing using memory mapping within .Net 4 (Or less). In the end I want to be able to define a structure in my main application, pass it to the class library wrapper and let the class library determine the size of the structure.
Definitions:
Initial Idea:
I want the class library wrapper to have no knowledge of the TestStruct generated by MyAppA other than it is an object that the class library wrapper needs to keep up with and use for memory sharing...
I was thinking I would create the TestStruct in MyAppA and add as many variables to it as I needed (in this case just 1, a string). Then pass it into the MemoryMapTool constructor and let the MemoryMapTool class determine the size of the struct. This is currently the issue. Working with memory I tend to be cautious and research before I just try something that will possibly fail killing my IDE or OS... ;)
I was originally going to just pass the TestStruct straight to the MemoryMapTool constructor but ran into this issue...
long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(oData));
Error: The type or namespace name 'oData' could not be found (are you missing a using directive or an assembly reference?)
I then was thinking of trying to just use...
long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(oData);
... and it seems to work (at least the IDE likes it). But for some reason I don't feel like that's the correct way to do it.
UPDATE: After trying that I get a new error...
Error: Type 'MyAppA.Form1+TestStruct' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
MemoryMapTool.cs Contents
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.MemoryMappedFiles;
using System.Threading;
namespace SharedMemoryWorker
{
public class MemoryMapTool : IDisposable
{
#region Private class variables
private string m_sLastError = "";
private MemoryMappedFile mmf = null;
private string m_sMapName = "";
private object m_oData = null;
#endregion
#region Public properties
public string MapName
{
get
{
return m_sMapName;
}
set
{
m_sMapName = value;
}
}
public object Data
{
get
{
return m_oData;
}
set
{
m_oData = value;
}
}
#endregion
#region Constructor
private MemoryMapTool(string sMapName, object oData)
{
long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(oData);
try
{
//Save the map name
m_sMapName = sMapName;
//Create new map or use an existing one
//mmf = MemoryMappedFile.CreateOrOpen(m_sMapName, lMapSize);
}
catch (Exception ex)
{
m_sLastError = ex.Message;
throw new NullReferenceException("Error creating new object!");
}
}
public void Dispose()
{
//Deconstructor
}
#endregion
#region Public class methods
public string GetLastError()
{
return m_sLastError;
}
#endregion
}
}
MyAppA, Form1.cs Contents
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MyAppA
{
public partial class Form1 : Form
{
#region Public structures
public class TestStruct
{
#region Private class variables
private string m_sTest = null;
#endregion
#region Public properties
public string Test
{
get
{
return m_sTest;
}
set
{
m_sTest = value;
}
}
#endregion
}
#endregion
public Form1()
{
InitializeComponent();
}
}
}
MyAppB, Form1.cs Contents
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MyAppB
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}
Upvotes: 2
Views: 475
Reputation: 13976
I think the error is pretty clear. You need to tell the CLR how you want the structure or class members to be laid out in memory. See the StructLayoutAttribute
- it needs to be applied explicitly to classes (whereas for structs Sequential
is the default).
From your description it appears that you want to do IPC between two or more managed processes. You may also want to establish a unified marshaling strategy for strings (see the MarshalAsAttribute
). You can choose one and stick with it throughout your classes.
Finally, I would like to say that this isn't really appropriate for what you're trying to do (too much overhead and room for error). Instead you could:
My choice would be #2. Performance can be very good, especially with WCF and on the same computer with named pipes or net.tcp bindings and it just works.
Upvotes: 1