I have a simple C# GUI which has a button on it. When clicking on the button, a C++ DLL is called and some computations are done inside C++ DLL.
The C# GUI (executable) imports a C++ DLL like this:
class Call_Link_DLL
{
[DllImport(@"Link_DLL.dll", EntryPoint = "OPS_Link_LoadFDD")]
public static extern System.IntPtr OPS_Link_LoadFDD([In,Out,MarshalAs(UnmanagedType.LPStr)] string char_Ptr_octave_bin_dir);
}
SystemIntPtr_ReturnedByDLL = Call_Link_DLL.OPS_Link_LoadFDD(char_Ptr_octave_bin_dir);
Inside C++ DLL I'm allocating a pointer to a class object like this:
FDD*FDD_Ptr_Object=new FDD();
When C++ DLL is done with its calculations, the pointer is de-allocated like this:
delete FDD_Ptr_Object;//de-allocating makes C# executable (GUI) not able to re-execute without having to close GUI!
But the problem is that: when I click on the GUI button for the 1st time, C++ DLL runs and generates results. But for the 2nd time when I click on the GUI button, the program crashes. I have to close GUI and click on GUI button again to re-execute. The confusing point is that when I comment out the de-allocation line (delete FDD_Ptr_Object;
), the GUI works perfect when I push the GUI button for the 2nd time, 3rd time and so on. I was wondering if anybody ran into such a problem. Thanks.
EDIT: I post all the details here:
The following is a simple GUI developed with C#. It has one button, when clicking on the button a C++ DLL (named Link_DLL.dll) would be called.
The following is the code in C# for importing a C++ DLL named Link_DLL.dll:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace GUI_Call_Link_DLL
{
class Call_Link_DLL
{
[DllImport(@"Link_DLL.dll", EntryPoint = "OPS_Link_LoadFDD")]
public static extern System.IntPtr OPS_Link_LoadFDD([In,Out,MarshalAs(UnmanagedType.LPStr)] string char_Ptr_octave_bin_dir);
[DllImport(@"Link_DLL.dll", EntryPoint = "OPS_Link_RunFDD_getFreqDomainResults")]
public static extern System.IntPtr OPS_Link_RunFDD_getFreqDomainResults([In, Out, MarshalAs(UnmanagedType.LPStr)] string char_Address, [In, Out, MarshalAs(UnmanagedType.I4)]int int_NumChann, [In, Out, MarshalAs(UnmanagedType.I4)]int int_SamplingFreq);
[DllImport(@"Link_DLL.dll", EntryPoint = "OPS_Link_feedFDD_setNaturalFreqSelectedByUser")]
public static extern System.IntPtr OPS_Link_feedFDD_setNaturalFreqSelectedByUser([In, Out, MarshalAs(UnmanagedType.LPStr)]string char_Address_File_naturalFreqSelectedByUser);
[DllImport(@"Link_DLL.dll", EntryPoint = "OPS_Link_RunFDD_getModeShapesResults")]
public static extern System.IntPtr OPS_Link_RunFDD_getModeShapesResults();
[DllImport(@"Link_DLL.dll", EntryPoint = "OPS_Link_UnloadFDD")]
public static extern System.IntPtr OPS_Link_UnloadFDD();
}
}
The code inside C# to call Link_DLL.dll when button is clicked:
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 GUI_Call_Link_DLL
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//you can copy "Octave-3.6.1" folder and subfolders anywhere you want and give binary path here:
string char_Ptr_octave_bin_dir = @"C:\Software\Octave-3.6.1\bin\";
string char_Address = openFileDialog1.FileName;
int int_NumChann = int.Parse(textBox1.Text);
int int_SamplingFreq = int.Parse(textBox2.Text);
string char_Address_File_naturalFreqSelectedByUser = "FDD_TEMP_naturalFreqSelectedByUser.txt";
System.IntPtr SystemIntPtr_ReturnedByDLL;//char* returned by DLL
string string_ReturnedByDLL = "Good";//convert to string the char* returned by DLL
try
{
SystemIntPtr_ReturnedByDLL = Call_Link_DLL.OPS_Link_LoadFDD(char_Ptr_octave_bin_dir);
string_ReturnedByDLL = Marshal.PtrToStringAnsi(SystemIntPtr_ReturnedByDLL);
if (string_ReturnedByDLL != "Good") { throw new Exception(); }
SystemIntPtr_ReturnedByDLL = Call_Link_DLL.OPS_Link_RunFDD_getFreqDomainResults(char_Address, int_NumChann, int_SamplingFreq);
string_ReturnedByDLL = Marshal.PtrToStringAnsi(SystemIntPtr_ReturnedByDLL);
if (string_ReturnedByDLL != "Good") { throw new Exception(); }
SystemIntPtr_ReturnedByDLL = Call_Link_DLL.OPS_Link_feedFDD_setNaturalFreqSelectedByUser(char_Address_File_naturalFreqSelectedByUser);
string_ReturnedByDLL = Marshal.PtrToStringAnsi(SystemIntPtr_ReturnedByDLL);
if (string_ReturnedByDLL != "Good") { throw new Exception(); }
SystemIntPtr_ReturnedByDLL = Call_Link_DLL.OPS_Link_RunFDD_getModeShapesResults();
string_ReturnedByDLL = Marshal.PtrToStringAnsi(SystemIntPtr_ReturnedByDLL);
if (string_ReturnedByDLL != "Good") { throw new Exception(); }
SystemIntPtr_ReturnedByDLL = Call_Link_DLL.OPS_Link_UnloadFDD();
string_ReturnedByDLL = Marshal.PtrToStringAnsi(SystemIntPtr_ReturnedByDLL);
if (string_ReturnedByDLL != "Good") { throw new Exception(); }
}
catch (Exception) { MessageBox.Show(string_ReturnedByDLL); }
}
private void button2_Click(object sender, EventArgs e)
{
//show dialog and get result
DialogResult DialogResult_Object = openFileDialog1.ShowDialog();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
}
}
}
The C++ code in Link_DLL.dll is this:
#include "Link.h"
//Declare handle to FDD_DLL
//This handle is declared within namespace to be accessible from within any function
namespace namespace_top_of_Link{
HINSTANCE hDLL=NULL;
}
//Exported function to be called by executable (C# or C++)
//To load FDD_DLL
//To add dependencies directories to search path
//Dependencies path is determined by executable
extern "C" __declspec(dllexport)
char* OPS_Link_LoadFDD(char* char_Ptr_octave_bin_dir){
try{
std::string string_octave_bin_dir = std::string(char_Ptr_octave_bin_dir);//convert char* to std::string
std::wstring wstring_octave_bin_dir = std::wstring(string_octave_bin_dir.begin(), string_octave_bin_dir.end());// convert std::string to std::wstring
SetDllDirectory(wstring_octave_bin_dir.c_str());
//SetDllDirectory((LPCWSTR)L"C:\\Software\\Octave-3.6.1\\bin\\");
namespace_top_of_Link::hDLL=LoadLibrary((LPCWSTR)L"FDD_DLL.dll");
if(namespace_top_of_Link::hDLL==NULL){
throw "FDD DLL or dependencies could not be loaded";
}
}
catch(char*char_Ptr_Exception){
return char_Ptr_Exception;
}
return "Good";
}
//Exported function to be called by executable (C# or C++)
//To run FDD_DLL
//FDD_DLL returns results inside text files
//Results are frequencies and corresponding singular values
extern "C" __declspec(dllexport)
char* OPS_Link_RunFDD_getFreqDomainResults(char* char_Address,int int_NumChann,int int_SamplingFreq){
try{
typedef void (*Ptr_OPS_FDD_getFreqDomainResults)(char*, int, int);
Ptr_OPS_FDD_getFreqDomainResults Ptr_OPS_FDD_getFreqDomainResults_Object=
(Ptr_OPS_FDD_getFreqDomainResults)GetProcAddress(namespace_top_of_Link::hDLL,"OPS_FDD_getFreqDomainResults");
Ptr_OPS_FDD_getFreqDomainResults_Object(char_Address, int_NumChann, int_SamplingFreq);
}
catch(char*char_Ptr_Exception){
return char_Ptr_Exception;
}
return "Good";
}
//Exported function to be called by executable (C# or C++)
//To pass user-selected natural frequencies to FDD_DLL
//User-selected natural frequencies should be inside a text file
//Address of text file is passed to FDD_DLL
extern "C" __declspec(dllexport)
char* OPS_Link_feedFDD_setNaturalFreqSelectedByUser(const char* char_Address_File_naturalFreqSelectedByUser){
try{
typedef void (*Ptr_OPS_FDD_setNaturalFreqSelectedByUser)(const char*);
Ptr_OPS_FDD_setNaturalFreqSelectedByUser Ptr_OPS_FDD_setNaturalFreqSelectedByUser_Object;
Ptr_OPS_FDD_setNaturalFreqSelectedByUser_Object=
(Ptr_OPS_FDD_setNaturalFreqSelectedByUser)GetProcAddress(namespace_top_of_Link::hDLL,"OPS_FDD_setNaturalFreqSelectedByUser");
Ptr_OPS_FDD_setNaturalFreqSelectedByUser_Object(char_Address_File_naturalFreqSelectedByUser);
}
catch(char*char_Ptr_Exception){
return char_Ptr_Exception;
}
return "Good";
}
//Exported function to be called by executable (C# or C++)
//To run FDD_DLL
//FDD_DLL returns results inside text files
//Results are mode shapes at user-selected natural frequencies
extern "C" __declspec(dllexport)
char* OPS_Link_RunFDD_getModeShapesResults(){
try{
typedef void (*Ptr_OPS_FDD_getModeShapesResults)();
Ptr_OPS_FDD_getModeShapesResults Ptr_OPS_FDD_getModeShapesResults_Object=
(Ptr_OPS_FDD_getModeShapesResults)GetProcAddress(namespace_top_of_Link::hDLL,"OPS_FDD_getModeShapesResults");
Ptr_OPS_FDD_getModeShapesResults_Object();
}
catch(char* char_Ptr_Exception){
return char_Ptr_Exception;
}
return "Good";
}
//Exported function to be called by executable (C# or C++)
//To unload FDD_DLL
extern "C" __declspec(dllexport)
char* OPS_Link_UnloadFDD(){
try{
if(FreeLibrary(namespace_top_of_Link::hDLL)==0){//If function can not free, return value is zero
throw "FDD DLL could not be unloaded";
}
}
catch(char* char_Ptr_Exception){
return char_Ptr_Exception;
}
return "Good";
}
Actually Link_DLL.dll is loading another C++ DLL named FDD_DLL.dll, the C++ code in FDD_DLL.dll is this:
#include "FDD.h"
//Declare object of FDD class
//FDD object is declared within namespace to be accessible from within any function
//FDD object pointer will be de-allocated (delete) when done with it
namespace namespace_top_of_FDD{
FDD*FDD_Ptr_Object=new FDD();
}
//Exported function to be called by Link_DLL
//To initialize FDD object
//To run FDD object methods
//FDD object methods will return results inside text file
//Results are frequencies and corresponding singular values
extern "C" __declspec(dllexport)
void OPS_FDD_getFreqDomainResults(char* char_Address,int int_NumChann,int int_SamplingFreq){
namespace_top_of_FDD::FDD_Ptr_Object->m_char_Address=char_Address;
namespace_top_of_FDD::FDD_Ptr_Object->m_int_NumChann=int_NumChann;
namespace_top_of_FDD::FDD_Ptr_Object->m_int_SamplingFreq=int_SamplingFreq;
namespace_top_of_FDD::FDD_Ptr_Object->getMatrixOfChannels();
namespace_top_of_FDD::FDD_Ptr_Object->getCPSD();
namespace_top_of_FDD::FDD_Ptr_Object->getSVD();
namespace_top_of_FDD::FDD_Ptr_Object->getFreqDomainResultsPrinted();
namespace_top_of_FDD::FDD_Ptr_Object->getFreqPlot();
}
//Exported function to be called by Link_DLL
//To pass user-selected natural frequencies from Link_DLL to FDD_DLL
//User-selected natural frequencies should be inside a text file
//Address of text file is passed
//FDD_DLL would read text file
extern "C" __declspec(dllexport)
void OPS_FDD_setNaturalFreqSelectedByUser(const char* char_Address_File_naturalFreqSelectedByUser){
namespace_top_of_FDD::FDD_Ptr_Object->getNaturalFreqValue(char_Address_File_naturalFreqSelectedByUser);
}
//Exported function to be called by Link_DLL
//To run FDD object methods
//FDD object methods will return results inside text files
//Results are mode shapes at user-selected natural frequencies
//Finally FDD object pointer will be de-allocated (delete)
extern "C" __declspec(dllexport)
void OPS_FDD_getModeShapesResults(){
namespace_top_of_FDD::FDD_Ptr_Object->get_NaturalFreqIndex_from_NaturalFreqValue();
namespace_top_of_FDD::FDD_Ptr_Object->getRealModesFromComplexModesAtNaturalFreq();
namespace_top_of_FDD::FDD_Ptr_Object->getModeShapesNormalized();
namespace_top_of_FDD::FDD_Ptr_Object->getModeShapesPrinted();
//namespace_top_of_FDD::FDD_Ptr_Object->getModesPlot();
namespace_top_of_FDD::FDD_Ptr_Object->getNormalizedModesPlot();
//namespace_top_of_FDD::FDD_Ptr_Object->getNormalizedModesPlotAtSinglePlot();
namespace_top_of_FDD::FDD_Ptr_Object->~FDD();
//delete namespace_top_of_FDD::FDD_Ptr_Object;//de-allocating makes C# executable (GUI) not able to re-execute without having to close GUI!
//namespace_top_of_FDD::FDD_Ptr_Object=NULL;
}
FDD::FDD()
:m_char_Address(""),m_int_NumChann(0),m_int_SamplingFreq(0)
{
}
FDD::~FDD(){
}
// The rest of the C++ code inside FDD_DLL.dll is just implementation of methods of FDD class...
As can be seen, there is this line "delete namespace_top_of_FDD::FDD_Ptr_Object;
" in FDD_DLL.dll, when I comment out that line, the button of C# GUI can be clicked several times, but when I un-comment that line, the button of C# GUI works when clicked for the 1st time, but for the 2nd time the program crashes.. Please let me know if you have any idea...