开发者

C# ShowDialog() throws error because of importing C++ DLL

开发者 https://www.devze.com 2023-04-06 13:04 出处:网络
This C# program works fine without using showdialog() but it creates \"system access violation\" exception when i tried to use showdialog(). Weird!!

This C# program works fine without using showdialog() but it creates "system access violation" exception when i tried to use showdialog(). Weird!!

C# Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace w_CSharp_GUI_1
{
    public partial class Form1 : Form
    {
        private String W_Addr,C_Addr,Pic_Addr="lol";

        [DllImport("face_proj_trial_dll.dll")]
        public static extern string f_detect(string path);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
        OpenFileDialog openFileDialog2 = new OpenFileDialog();

        openFileD开发者_高级运维ialog2.ShowDialog(this);

            Pic_Addr = (f_detect("C:\\pic.jpg"));

            textBox1.Text = Convert.ToString(Pic_Addr);
        }

    }
}

C++ code:

#include "face_detect_DLL.h"

extern "C" __declspec(dllexport) char* _stdcall f_detect(char* path)
{
    return path;
}


That's not at all surprising. You are returning a C string that was actually created by the C# marshaller. The marshaller then tries to free that memory twice. Once as the return value and once for the parameter passed to the DLL. The first free will fail because the memory was not allocated with the allocator that the C# marshaller assumes.

Anyway, you simply don't want to return a char* from your DLL. I'm not sure what you really want to do but the normal patterns with string P/invokes are:

  1. For marshalling strings from C# to C++ declare them as string in your C# and char* in C++.
  2. When going the other way use StringBuilder. Allocate a buffer before you call and use MarshalAs. There are a gazillion examples on the web of this pattern.


Functions that return a string are a memory management problem. The memory for the string has to be released. The pinvoke marshaller is going to call CoTaskMemFree() on the returned string. That's going to crash on Vista and up, silently leak memory on XP since the string wasn't allocated with CoTaskMemAlloc.

You'll need to declare the return type as IntPtr to prevent the marshaller from doing this. And marshal it yourself with Marshal.PtrToStringAnsi(). That solves the crash but not the memory leak. You'll need to declare the function as void f_detect(const char* path, char* somevalue, size_t somevaluebuffersize) so that the caller can pass his own buffer. StringBuilder on the managed side.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号