D
Don.Leri
Hi,
I have a logger.dll (unmanaged c++ dll compiled in vs2005).
I have a C# interop to use that dll in managed code implemented in
Interfaces.dll (used by other C# dlls).
I also have a number of other C# dlls referencing Interfaces.dll and
using logger.dll interop for logging.
The problem is that calls to logger.dll functions may occasionally
fail with error "Attempted to read or write protected memory".
I understand that it may be the reason of bad pointers. What I don't
understand is how it may happen.
After some testing it looks like it happens when interop is used from
multiple threads. It happens on some client servers and on 1 of 3
computers used for testing (2k3 server + sp1 + .net 2.0).
Interop is like this (brief):
namespace LoggerInterop {
#region Includes
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LogId = System.UInt32;
using LogResult = System.UInt32;
#endregion
public class Log
{
#region Message
public enum MessageType { ... }
public class Message : IDisposable
{
#region Interop
internal enum AttachmentType { ... }
[ StructLayout(LayoutKind.Sequential, Pack = 4)
]
internal struct MsgAttachments
{
[MarshalAs(UnmanagedType.I4 )] public Int32 Count;
[MarshalAs(UnmanagedType.SysInt)] public IntPtr Array;
}
[ StructLayout(LayoutKind.Sequential, Pack = 1)
]
internal struct MsgStruct
{
[MarshalAs(UnmanagedType.I8 )] public Int64 Time;
[MarshalAs(UnmanagedType.I8 )] public UInt64 Id;
[MarshalAs(UnmanagedType.I4 )] public MessageType Type;
[MarshalAs(UnmanagedType.Struct)] public MsgAttachments
Attachments;
}
[ StructLayout(LayoutKind.Sequential, Pack = 4)
]
internal struct MsgAttachment
{
public MsgAttachment(AttachmentType T, IntPtr P)
{
Type = T;
Pointer = P;
}
[MarshalAs(UnmanagedType.I4 )] public AttachmentType
Type;
[MarshalAs(UnmanagedType.SysInt)] public IntPtr
Pointer;
}
[ StructLayout(LayoutKind.Sequential, Pack = 4)
]
internal struct AtcString
{
public AtcString(string T)
{
Str = T;
}
[MarshalAs(UnmanagedType.LPWStr)] public string Str;
}
[ StructLayout(LayoutKind.Sequential, Pack = 4)
]
internal struct AtcByteArray
{
public AtcByteArray(Byte[] A)
{
Count = A.Length;
Array = Marshal.AllocHGlobal(Count);
Marshal.Copy(A, 0, Array, Count);
}
[MarshalAs(UnmanagedType.I4 )] public Int32 Count;
[MarshalAs(UnmanagedType.SysInt)] public IntPtr Array;
}
#endregion
#region Constructor / Destructor
public Message(UInt64 Id, MessageType Type)
{
_Msg.Time = DateTime.Now.ToFileTime();
_Msg.Id = Id;
_Msg.Type = Type;
_Msg.Attachments.Count = 0;
_Msg.Attachments.Array =
Marshal.AllocHGlobal(MaxAttachments * Marshal.SizeOf(typeof(Int32)));
}
~Message() { Dispose(); }
#endregion
#region Constants
private const int MaxAttachments = 10;
#endregion
#region Containers
private bool _Disposed = false;
internal MsgStruct _Msg;
#endregion
#region IDisposable
public void Dispose()
{
if (_Disposed) return;
for (Int32 i = 0; i < _Msg.Attachments.Count; ++i)
{
IntPtr p = get_ptr(i);
MsgAttachment atc = (MsgAttachment)ptr_to_struct(p,
typeof(MsgAttachment));
switch (atc.Type)
{
case AttachmentType.Comment:
{
Marshal.DestroyStructure(atc.Pointer,
typeof(AtcString));
break;
}
case AttachmentType.ByteArray:
{
AtcByteArray bytes =
(AtcByteArray)ptr_to_struct(atc.Pointer, typeof(AtcByteArray));
Marshal.FreeHGlobal(bytes.Array);
break;
}
}
Marshal.FreeHGlobal(atc.Pointer);
Marshal.FreeHGlobal(p);
}
Marshal.FreeHGlobal(_Msg.Attachments.Array);
_Disposed = true;
}
#endregion
#region Public Methods
public bool AddComment(string Comment)
{
if (_Disposed || _Msg.Attachments.Count == MaxAttachments)
return false;
add_ptr(struct_to_ptr(new
MsgAttachment(AttachmentType.Comment, struct_to_ptr(new
AtcString(Comment)))));
return true;
}
public bool AddByteArray(Byte[] Array)
{
if (_Disposed || _Msg.Attachments.Count == MaxAttachments)
return false;
add_ptr(struct_to_ptr(new
MsgAttachment(AttachmentType.ByteArray, struct_to_ptr(new
AtcByteArray(Array)))));
return true;
}
public override string ToString() { ... }
#endregion
#region Private Methods
private IntPtr struct_to_ptr(object S)
{
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(S));
Marshal.StructureToPtr(S, p, false);
return p;
}
private object ptr_to_struct(IntPtr P, Type T)
{
return Marshal.PtrToStructure(P, T);
}
private void add_ptr(IntPtr P)
{
Marshal.WriteInt32(
_Msg.Attachments.Array,
_Msg.Attachments.Count * Marshal.SizeOf(typeof(Int32)),
P.ToInt32()
);
_Msg.Attachments.Count++;
}
private IntPtr get_ptr(Int32 I)
{
return new
IntPtr(Marshal.ReadInt32(_Msg.Attachments.Array, I *
Marshal.SizeOf(typeof(Int32))));
}
#endregion
}
#endregion
#region Dll
[DllImport("Logger.dll")] private static extern LogResult
LogOpen([MarshalAs(UnmanagedType.LPWStr)] string Name, ref LogId Id);
[DllImport("Logger.dll")] private static extern LogResult
LogWrite(LogId Id, IntPtr Message);
[DllImport("Logger.dll")] private static extern LogResult
LogClose(LogId Id);
#endregion
#region Constructor / Destructor
public Log(string Name)
{
if (LOG_OK != LogOpen(Name, ref _Id)) throw new
Exception("Failed to open log file.");
}
~Log() { if (_Id != 0) LogClose(_Id); }
#endregion
#region Constants
private const LogResult LOG_OK = 0;
#endregion
#region Containers
private LogId _Id = 0;
#endregion
#region Public Methods
public void Write(Message Msg)
{
try
{
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(Msg._Msg));
Marshal.StructureToPtr(Msg._Msg, p, false);
LogWrite(_Id, p);
Marshal.FreeHGlobal(p);
}
catch (Exception ex)
{
// ... write to event log ...
}
}
#endregion
}
}
In test app it fails on calling LogWrite or in constructor on LogOpen.
The only place were logger.dll is doing something to memory is in
LogOpen returing LogId.
Here is a logger.h (brief):
#include <wtypes.h>
// Enums
enum MessageType { ... };
enum AttachmentType { ... };
// Types
// data
typedef DWORD LOGID, *PLOGID;
typedef DWORD LOGERROR;
typedef ULARGE_INTEGER LOGTIME; // in filetime
format
typedef ULARGE_INTEGER LOGMSGID;
// attachments
typedef struct
{
PCTSTR Text;
}
ATCSTRING;
typedef struct
{
DWORD Count;
BYTE* Array;
}
ATCBYTEARRAY;
// message
typedef struct
{
AttachmentType Type;
void* Pointer;
}
MSGATTACHMENT;
typedef struct
{
DWORD Count;
MSGATTACHMENT** Array;
}
MSGATTACHMENTS;
typedef struct
{
LOGTIME Time;
LOGMSGID Id;
MessageType Type;
MSGATTACHMENTS Attachments;
}
LOGMSG, *PLOGMSG;
typedef const LOGMSG* PCLOGMSG;
// functions
typedef LOGERROR (*PLOGOPEN) (PCTSTR, PLOGID);
typedef LOGERROR (*PLOGWRITE) (LOGID, PCLOGMSG);
typedef LOGERROR (*PLOGCLOSE) (LOGID);
How can the mentioned error happen? And what can i do to avoid it? Any
ideas on possible probblems in interop or logger.dll, maybe i should
set something specific in dll configuration (it was generated as multi-
threaded dll)?
I've read a lot of related topics but still got no clue.
Any help and suggestions would be greatly appreciated.
Thanks.
I have a logger.dll (unmanaged c++ dll compiled in vs2005).
I have a C# interop to use that dll in managed code implemented in
Interfaces.dll (used by other C# dlls).
I also have a number of other C# dlls referencing Interfaces.dll and
using logger.dll interop for logging.
The problem is that calls to logger.dll functions may occasionally
fail with error "Attempted to read or write protected memory".
I understand that it may be the reason of bad pointers. What I don't
understand is how it may happen.
After some testing it looks like it happens when interop is used from
multiple threads. It happens on some client servers and on 1 of 3
computers used for testing (2k3 server + sp1 + .net 2.0).
Interop is like this (brief):
namespace LoggerInterop {
#region Includes
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LogId = System.UInt32;
using LogResult = System.UInt32;
#endregion
public class Log
{
#region Message
public enum MessageType { ... }
public class Message : IDisposable
{
#region Interop
internal enum AttachmentType { ... }
[ StructLayout(LayoutKind.Sequential, Pack = 4)
]
internal struct MsgAttachments
{
[MarshalAs(UnmanagedType.I4 )] public Int32 Count;
[MarshalAs(UnmanagedType.SysInt)] public IntPtr Array;
}
[ StructLayout(LayoutKind.Sequential, Pack = 1)
]
internal struct MsgStruct
{
[MarshalAs(UnmanagedType.I8 )] public Int64 Time;
[MarshalAs(UnmanagedType.I8 )] public UInt64 Id;
[MarshalAs(UnmanagedType.I4 )] public MessageType Type;
[MarshalAs(UnmanagedType.Struct)] public MsgAttachments
Attachments;
}
[ StructLayout(LayoutKind.Sequential, Pack = 4)
]
internal struct MsgAttachment
{
public MsgAttachment(AttachmentType T, IntPtr P)
{
Type = T;
Pointer = P;
}
[MarshalAs(UnmanagedType.I4 )] public AttachmentType
Type;
[MarshalAs(UnmanagedType.SysInt)] public IntPtr
Pointer;
}
[ StructLayout(LayoutKind.Sequential, Pack = 4)
]
internal struct AtcString
{
public AtcString(string T)
{
Str = T;
}
[MarshalAs(UnmanagedType.LPWStr)] public string Str;
}
[ StructLayout(LayoutKind.Sequential, Pack = 4)
]
internal struct AtcByteArray
{
public AtcByteArray(Byte[] A)
{
Count = A.Length;
Array = Marshal.AllocHGlobal(Count);
Marshal.Copy(A, 0, Array, Count);
}
[MarshalAs(UnmanagedType.I4 )] public Int32 Count;
[MarshalAs(UnmanagedType.SysInt)] public IntPtr Array;
}
#endregion
#region Constructor / Destructor
public Message(UInt64 Id, MessageType Type)
{
_Msg.Time = DateTime.Now.ToFileTime();
_Msg.Id = Id;
_Msg.Type = Type;
_Msg.Attachments.Count = 0;
_Msg.Attachments.Array =
Marshal.AllocHGlobal(MaxAttachments * Marshal.SizeOf(typeof(Int32)));
}
~Message() { Dispose(); }
#endregion
#region Constants
private const int MaxAttachments = 10;
#endregion
#region Containers
private bool _Disposed = false;
internal MsgStruct _Msg;
#endregion
#region IDisposable
public void Dispose()
{
if (_Disposed) return;
for (Int32 i = 0; i < _Msg.Attachments.Count; ++i)
{
IntPtr p = get_ptr(i);
MsgAttachment atc = (MsgAttachment)ptr_to_struct(p,
typeof(MsgAttachment));
switch (atc.Type)
{
case AttachmentType.Comment:
{
Marshal.DestroyStructure(atc.Pointer,
typeof(AtcString));
break;
}
case AttachmentType.ByteArray:
{
AtcByteArray bytes =
(AtcByteArray)ptr_to_struct(atc.Pointer, typeof(AtcByteArray));
Marshal.FreeHGlobal(bytes.Array);
break;
}
}
Marshal.FreeHGlobal(atc.Pointer);
Marshal.FreeHGlobal(p);
}
Marshal.FreeHGlobal(_Msg.Attachments.Array);
_Disposed = true;
}
#endregion
#region Public Methods
public bool AddComment(string Comment)
{
if (_Disposed || _Msg.Attachments.Count == MaxAttachments)
return false;
add_ptr(struct_to_ptr(new
MsgAttachment(AttachmentType.Comment, struct_to_ptr(new
AtcString(Comment)))));
return true;
}
public bool AddByteArray(Byte[] Array)
{
if (_Disposed || _Msg.Attachments.Count == MaxAttachments)
return false;
add_ptr(struct_to_ptr(new
MsgAttachment(AttachmentType.ByteArray, struct_to_ptr(new
AtcByteArray(Array)))));
return true;
}
public override string ToString() { ... }
#endregion
#region Private Methods
private IntPtr struct_to_ptr(object S)
{
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(S));
Marshal.StructureToPtr(S, p, false);
return p;
}
private object ptr_to_struct(IntPtr P, Type T)
{
return Marshal.PtrToStructure(P, T);
}
private void add_ptr(IntPtr P)
{
Marshal.WriteInt32(
_Msg.Attachments.Array,
_Msg.Attachments.Count * Marshal.SizeOf(typeof(Int32)),
P.ToInt32()
);
_Msg.Attachments.Count++;
}
private IntPtr get_ptr(Int32 I)
{
return new
IntPtr(Marshal.ReadInt32(_Msg.Attachments.Array, I *
Marshal.SizeOf(typeof(Int32))));
}
#endregion
}
#endregion
#region Dll
[DllImport("Logger.dll")] private static extern LogResult
LogOpen([MarshalAs(UnmanagedType.LPWStr)] string Name, ref LogId Id);
[DllImport("Logger.dll")] private static extern LogResult
LogWrite(LogId Id, IntPtr Message);
[DllImport("Logger.dll")] private static extern LogResult
LogClose(LogId Id);
#endregion
#region Constructor / Destructor
public Log(string Name)
{
if (LOG_OK != LogOpen(Name, ref _Id)) throw new
Exception("Failed to open log file.");
}
~Log() { if (_Id != 0) LogClose(_Id); }
#endregion
#region Constants
private const LogResult LOG_OK = 0;
#endregion
#region Containers
private LogId _Id = 0;
#endregion
#region Public Methods
public void Write(Message Msg)
{
try
{
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(Msg._Msg));
Marshal.StructureToPtr(Msg._Msg, p, false);
LogWrite(_Id, p);
Marshal.FreeHGlobal(p);
}
catch (Exception ex)
{
// ... write to event log ...
}
}
#endregion
}
}
In test app it fails on calling LogWrite or in constructor on LogOpen.
The only place were logger.dll is doing something to memory is in
LogOpen returing LogId.
Here is a logger.h (brief):
#include <wtypes.h>
// Enums
enum MessageType { ... };
enum AttachmentType { ... };
// Types
// data
typedef DWORD LOGID, *PLOGID;
typedef DWORD LOGERROR;
typedef ULARGE_INTEGER LOGTIME; // in filetime
format
typedef ULARGE_INTEGER LOGMSGID;
// attachments
typedef struct
{
PCTSTR Text;
}
ATCSTRING;
typedef struct
{
DWORD Count;
BYTE* Array;
}
ATCBYTEARRAY;
// message
typedef struct
{
AttachmentType Type;
void* Pointer;
}
MSGATTACHMENT;
typedef struct
{
DWORD Count;
MSGATTACHMENT** Array;
}
MSGATTACHMENTS;
typedef struct
{
LOGTIME Time;
LOGMSGID Id;
MessageType Type;
MSGATTACHMENTS Attachments;
}
LOGMSG, *PLOGMSG;
typedef const LOGMSG* PCLOGMSG;
// functions
typedef LOGERROR (*PLOGOPEN) (PCTSTR, PLOGID);
typedef LOGERROR (*PLOGWRITE) (LOGID, PCLOGMSG);
typedef LOGERROR (*PLOGCLOSE) (LOGID);
How can the mentioned error happen? And what can i do to avoid it? Any
ideas on possible probblems in interop or logger.dll, maybe i should
set something specific in dll configuration (it was generated as multi-
threaded dll)?
I've read a lot of related topics but still got no clue.
Any help and suggestions would be greatly appreciated.
Thanks.