目前我正在开发一个小项目,将软件包从VB6更新为VB.NET和一个小DLL,在VC6中写入更新到最新的Visual Studio版本。
现在我遇到一个小问题: 在VB6中是一个Form应用程序,一个模具VC-Code定义了两个DLL文件。 对于这些应用程序之间的通信,有一个共享内存和许多其他功能进行通信。
这是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;
};
现在让我们来看看VB.NET代码: 结构:
Public Structure MEMORY
dim mem1 as double
dim mem2 as int32
dim mem3 as short
end structure
功能:
Public Class Dll1
<DllImport(Dll_Datei_1)> _
Public Shared Sub Fkt_set_memAddr(<MarshalAs(UnmanagedType.Struct)> ByRef sharedmem As MEMORY)
End Sub
End Class
现在,如果我像这样调用函数:
public sharedMEM as MEMORY
Fkt_set_memAddr(sharedMEM)
DLL应该从VB.NET结构中获取地址, 所以到现在为止,我能够从同一个内存中写入和读取。
但它不起作用。 总是当我从DLL中的内存设置第一个值时,在调试DLL时,它表示值正确写入。如果我在VB.NET中检查它,值就像
2.5481197073372403e-307
出了什么问题?
感谢您的帮助! 岸堤
答案 0 :(得分:0)
由于托管对象是可移动的,因此您无法将托管对象的持久地址传递给非托管DLL。
见What are pinned objects?。 (fixed
在那篇文章中是针对C#的,VB.NET不支持。)
你需要:
Marshal.AllocHGlobal
分配内存块,将指针传递给DLL,然后按住它。Marshal.PtrToStructure
从内存块中读取一个MEMORY。Marshal.StructureToPtr
将内存块写入内存块。我建议您创建一个包装类来执行这些操作,例如:
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