【发布时间】:2017-10-03 09:43:29
【问题描述】:
我有一个本地的、非托管的 C++ DLL (symulator.dll),我必须加载它并从托管的 C# 应用程序调用。
DLL 利用 C++ 类和动态内存分配(通过 new 运算符)。
它导出一个名为Init的函数,其定义如下:
extern "C" __declspec( dllexport ) int Init( void )
{
sym = new CSymulator();
sym->Init();
return 0;
}
DLL 中包含的CSymulator 类有一个相当简单的构造函数:
CSymulator::CSymulator( void )
{
memset( mem, 0, sizeof( mem ) );
memset( &rmr, 0, sizeof( rmr ) );
md = 0;
ma = 0;
tacts = 0;
}
DLL导出的Init()函数调用的CSymulator::Init()方法定义如下:
int CSymulator::Init( void )
{
int *a = new int;
*a = 1;
FILE *f = fopen( "tmp.log", "wb" );
fprintf( f, "%i", *a );
fclose( f );
delete a;
return 0;
}
我正在使用以下代码将本机 C++ DLL 加载到托管 C# 应用程序中:
public partial class Form1 : Form
{
public IntPtr SimHandle;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
delegate int SimInit();
SimInit DLL_Init;
public void InicjujDLL()
{
IntPtr adres;
adres = GetProcAddress(SimHandle, "Init");
DLL_Init = (SimInit)Marshal.GetDelegateForFunctionPointer(adres, typeof(SimInit));
int rc = DLL_Init();
}
private void WczytajDLL()
{
String fileName = "D:\\prg\\kompilator\\Debug DLL\\symulator.dll";
SimHandle = LoadLibrary(fileName);
if (SimHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception(string.Format("Blad przy wczytywaniu biblioteki ({0})", errorCode));
}
else
{
InicjujDLL();
}
}
private void Form1_Load(object sender, EventArgs e)
{
WczytajDLL();
}
}
此代码应生成一个名为 tmp.log 的文件,其中包含 1 的内容。但由于某种原因,tmp.log 包含垃圾数据(一个随机的 32 位整数值,而不是 1;例如,2550276)。
它不是产生垃圾输出的唯一函数。任何试图动态分配内存的 DLL 函数在这样做后都无法使用它。
就好像原生 C++ DLL 以某种方式让 C# 垃圾收集器清除了它的内存。
如何防止这种行为?
【问题讨论】:
-
没有必要与
LoadLibrary混为一谈;GetProcAddress。只需阅读 p-invoke -
我也尝试使用 P-Invoke(应该在原始问题中提到它)。它导致完全相同的结果:由 DLL 分配和初始化的类以某种方式“忘记”它们的值成员。
-
不相关的(可能):
mem是如何声明的,在memset( mem, 0, sizeof( mem ) );之前有没有做过什么? -
@PhilBrubaker
mem被声明为CSymulator类的公共成员,如下所示:unsigned short mem[ 65535 ];。到目前为止,它只在构造函数中被触及;它没有在任何其他地方被触及。 -
有趣的是,每当我运行试图访问类的 DLL 代码时,我也会遇到这些访问冲突错误。
First-chance exception at 0x571020a7 (msvcr100d.dll) in gui.exe: 0xC0000005: Access violation reading location 0xcccccccc. A first chance exception of type 'System.AccessViolationException' occurred in gui.exe
标签: c# c++ dll native unmanaged