1

currently I am working on a small project, updating a softwarepackage from VB6 to VB.NET and a small DLL, writen in VC6 updating to the latest Visual Studio Version.

Now I have a small problem: In VB6 is a Form application an die VC-Code defines two DLL-Files. For Communication between these to application, there was a shared memory like and a lot of other functions to communicate.

This is the Shared Memory Struct in C:

typedef struct MEMORY
{
    double Mem0;        
    int Mem1;       
    short Mem2;
}MEMORY;

MEMORY  *sharedStructMEMORY; // Creating a pointer.


 //This is a function, I call out of VB:
void DLL_API __stdcall Fkt_get_memAddr (MEMORY *pnt_sharedmem)
{
     sharedStructMEMORY = pnt_sharedmem;
};

Now lets have a look to the VB.NET Code: The Struct:

Public Structure MEMORY
    dim mem1 as double
    dim mem2 as int32
    dim mem3 as short
end structure

The Function:

Public Class Dll1
    <DllImport(Dll_Datei_1)> _
    Public Shared Sub Fkt_set_memAddr(<MarshalAs(UnmanagedType.Struct)> ByRef sharedmem As MEMORY)
    End Sub
End Class

now, if i call the Function like this:

public sharedMEM as MEMORY

Fkt_set_memAddr(sharedMEM)

The DLL should get the adress from the VB.NET struct, so that until now, I am able to write and read from the same memory.

But it does not work. Always when I set the first value from the memory in the DLL, while debuggig the DLL, it says the value is written correctly. If I check it in VB.NET, the value is like

2.5481197073372403e-307

Whats going wrong?

Thanks for help! Andi

Andi
  • 11
  • 1
  • VB6 and VB.NET are *not* the same thing. Please edit your post and tags to clarify. – tcarvin Jul 31 '14 at 12:26
  • "shared memory" usually means a memory shared by multiple processes. Do you mean that, or just a memory block commonly used by an EXE and multiple DLLs in a process? – Ripple Jul 31 '14 at 14:31
  • what I mean is a Memory, used for exchanging easily and fast values between DLL and an EXE. – Andi Aug 01 '14 at 05:41

1 Answers1

0

Since managed objects are movable, you can't pass a persistent address of a managed object to an unmanaged DLL.
See What are pinned objects?. (fixed in that article is for C#, not supported by VB.NET.)

You need to:

  • Allocate a memory block by Marshal.AllocHGlobal, pass the pointer to it to the DLL, and hold it.
  • Read a MEMORY from the memory block through Marshal.PtrToStructure.
  • Write a MEMORY to the memory block through Marshal.StructureToPtr.

I would suggest you create a wrapper class to do those, for example:

Imports System.Runtime.InteropServices

' Note: No need to be Structure.
<StructLayout(LayoutKind.Sequential, Pack:=4)>
Public Class MEMORY
    Public mem1 As Double
    Public mem2 As Int32
    Public mem3 As Short
End Class


' Wrapper class to access the memory block.
' Note: This is not thread-safe.
Public Class MEMORYWrapper
    Implements IDisposable

    <DllImport(Dll_Datei_1)> _
    Private Shared Sub Fkt_set_memAddr(ptr As IntPtr)
    End Sub

    ' Holds the pinned address.
    Private _ptrToMemory As IntPtr

    ' Constructor allocates the memory block, and notify its address to DLL.
    ' Note: Creating instances twice could lead you to hell ;-)
    '       Better to be singletonized.
    Public Sub New()
        Me._ptrToMemory = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(MEMORY)))
        Fkt_set_memAddr(Me._ptrToMemory)
    End Sub

    ' Reads a MEMORY from the memory block.
    Public Function ReadMemory() As MEMORY
        Dim mem As MEMORY
        mem = DirectCast(
            Marshal.PtrToStructure(Me._ptrToMemory, GetType(MEMORY)), MEMORY)
        Return mem
    End Function

    ' Writes a MEMORY to the memory block.
    Public Sub WriteMemory(mem As MEMORY)
        Marshal.StructureToPtr(mem, Me._ptrToMemory, False)
    End Sub

    ' Implements IDisposable to free the memory block.

    Private disposedValue As Boolean = False        ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' TODO: free other state (managed objects).
            End If

            ' TODO: free your own state (unmanaged objects).
            ' Free the memory block.
            Marshal.FreeHGlobal(Me._ptrToMemory)
            Me._ptrToMemory = IntPtr.Zero

            ' TODO: set large fields to null.
        End If
        Me.disposedValue = True
    End Sub


#Region " IDisposable Support "
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

' A sample of the usage.
Module Module1

    Private memWrapper As New MEMORYWrapper

    Sub Main()
        Dim m As New MEMORY
        m.mem1 = 0.123
        m.mem2 = 555
        m.mem3 = 432

        memWrapper.WriteMemory(m)

        Dim m2 As MEMORY = memWrapper.ReadMemory()

        Console.WriteLine("{0},{1},{2}", m2.mem1, m2.mem2, m2.mem3)

        memWrapper.Dispose()
    End Sub
End Module
Community
  • 1
  • 1
Ripple
  • 1,257
  • 1
  • 9
  • 15
  • Hi, thanks for your help! I changed it. Now i get the error message in debbuging from the C-DLL like this: cxx0030 error expression cannot be evaluated – Andi Jul 31 '14 at 05:37
  • @Andi Sorry, I didn't read your code correctly. You mean the VB.NET program allocates the memory, and the other DLL writes into it, right? If so, I'll suggest the way later. – Ripple Jul 31 '14 at 09:19
  • yes, you have it. The VB.NET program allocates the memory. – Andi Aug 01 '14 at 05:35