Unmanaged dll interop problem.

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top