简体   繁体   English

将本机非托管 C++ DLL 加载到托管 C# 应用程序会导致 DLL 输出垃圾

[英]Loading a native unmanaged C++ DLL into a managed C# app causes the DLL to output garbage

I have a native, unmanaged C++ DLL (symulator.dll) which I have to load in and call from a managed C# application.我有一个本地的、非托管的 C++ DLL (symulator.dll),我必须加载它并从托管的 C# 应用程序调用。

The DLL utilizes C++ classes and dynamic memory allocation (via the new operator). DLL 利用 C++ 类和动态内存分配(通过new运算符)。

It exports a function called Init and its definition is as follows:它导出了一个名为Init的函数,其定义如下:

extern "C" __declspec( dllexport ) int Init( void )
{
    sym = new CSymulator();
    sym->Init();
    return 0;
}

The CSymulator class contained within the DLL has a rather simple constructor: DLL 中包含的CSymulator类有一个相当简单的构造函数:

CSymulator::CSymulator( void )
{
    memset( mem, 0, sizeof( mem ) );
    memset( &rmr, 0, sizeof( rmr ) );
    md = 0;
    ma = 0;
    tacts = 0;
}

The CSymulator::Init() method, called by the Init() function exported by the DLL, is defined as follows: 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;
}

I am loading the native C++ DLL into the managed C# application using this code:我正在使用以下代码将本机 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();
    }
}

This code should produce a file named tmp.log with the content of 1 in it.此代码应生成一个名为tmp.log的文件,其中包含1的内容。 But for some reason, tmp.log contains garbage data (a random 32-bit integer value instead of 1; for example, 2550276 ).但是由于某种原因, tmp.log包含垃圾数据(一个随机的 32 位整数值而不是 1;例如2550276 )。

It's not the only function that produces garbage output.它不是产生垃圾输出的唯一函数。 Any DLL function that tries to allocate memory dynamically is unable to use it after doing so.任何试图动态分配内存的 DLL 函数在这样做之后都无法使用它。

It's as if the native C++ DLL is somehow getting its memory purged by the C# garbage collector.就好像原生 C++ DLL 以某种方式让 C# 垃圾收集器清除了它的内存。

How to prevent this behavior?如何防止这种行为?

Wait a sec: Look at the reference below:等一下:看下面的参考:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int MultiplyByTen(int numberToMultiply);

I didn't notice that your delegate doesn't have the same attribute.我没有注意到您的代表没有相同的属性。

For reference, I don't see anything unusual in how you are doing the LoadLibrary: Dynamically Loading a Native Library作为参考,我没有看到您执行 LoadLibrary 的方式有什么异常: 动态加载本机库

I have done this myself using the exact reference without a problem.我自己使用准确的参考资料完成了这项工作,没有任何问题。 I would suggest removing ALL code temporarily that executes inside the DLL and just do a simple pass-through value.我建议暂时删除在 DLL 中执行的所有代码,只做一个简单的传递值。 Right now Init() is always returning 0. Try something else since you have zeroed out memory before, getting a zero back may just be a side-effect of the mem-zero op.现在 Init() 总是返回 0。尝试其他方法,因为您之前已将内存清零,返回零可能只是 mem-zero 操作的副作用。 Return 1974 or something.返回 1974 年什么的。

Ensure you have allow unsafe code enabled and use the memory viewer to look at the stack (you are getting a pointer back so you have a starting point).确保您启用了允许不安全代码并使用内存查看器查看堆栈(您正在获取一个指针,因此您有一个起点)。 If you do this beside the IL, you might spot where your memory is getting trashed.如果您在 IL 旁边执行此操作,您可能会发现您的记忆被破坏的地方。

HTH高温高压

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM