Laden...

System.AccessViolationException bei C++ dllimport in C#

Erstellt von tintenherz vor 16 Jahren Letzter Beitrag vor 16 Jahren 5.897 Views
T
tintenherz Themenstarter:in
4 Beiträge seit 2007
vor 16 Jahren
System.AccessViolationException bei C++ dllimport in C#

Hallo und Guten Tag,

da dies mein erster Thread hier ist möchte ich mich als erstes bei Denjenigen, die Ihr Wissen hier kostenlos und uneigennützig weitergeben, bedanken.
Ich habe mir schon diverse Male hier einen Rat oder ein gutes Beispiel holen können.

Nun zu meiner Herausforderung:
Ich arbeite mit VS2005.
Ich benutze in einem C# Projekt eine C++ Dll (unmanaged Code).
Dllimport etc. soweit erfolgreich:

[DllImport("ABSServicesud.dll", EntryPoint = "#6",
        ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool OpenDatabase();

Aufruf in C#:

private void button1_Click(object sender, System.EventArgs e)
        {
            try
            {
                bool bRet = OpenDatabase();
                if (bRet)
		       .......
            }
            catch (System.AccessViolationException x )
            {
                MessageBox.Show(x.ToString());
             }
        }

Methode in der C++ Dll:

bool CPrintService::thumbsup:penDatabase()
{
	AfxMessageBox(_T("1"));
	
	CString strProvider			=_T("SQLOLEDB.1");	//Provider für ADO     -> z.B. "SQLOLEDB.1"
	CString strDataSource		=_T("SRV-SQL-TEST");		//Name des DB-Servers  -> z.B. "SRV-SQL-TEST"
	CString strPassword			=_T("Test-User");		//Passwort             -> z.B. "Test-User"
	CString strUserID			=_T("Test-User");			//Benutzerkennung      -> z.B. "Test-User" 
	CString strInitialCatalog	=_T("APPLI");			//Name der DB          -> z.B. "APPLI"		  
	CString strApplicationName	=_T("PrintService");
	AfxMessageBox(_T("2"));
	//ADO Connection String bauen
	CString strConnect=_T("");
	BuildConnectString(strProvider, strPassword, strUserID, strDataSource, strInitialCatalog, strApplicationName, strConnect );
	AfxMessageBox(_T("3"));
	try
	{
		TRY
		{
			 m_Database.Open(strConnect);
		}
		CATCH(CDBException, e)
		{
			//TODO
			return false;
		}
		END_CATCH
	}
	catch (...) //catch any exception
	{
		AfxMessageBox(_T("4"));
		//TODO
		return false;
	}

Das m_Database Objekt ist von einer Frameworkklasse abgeleitet und gibt BOOL zurück.

Der Fehler tritt in der Zeile: "m_Database.Open(strConnect);" auf.
Fehlermeldung: "System.AccessViolationException: Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist."

Ich laufe in keine der Fehlerbehandlungen in OpenDatabase() hinein, sondern die Fehlermeldung kommt aus der C#-Ebene.

Ich hoffe ich habe mich detailiert genug ausgedrückt und dennoch nicht zuviel Text geschrieben.
Mir gehen die Ideen aus an welcher Stellle ich den Fehler suchen soll und wäre daher für jeden Hinweis dankbar.

S
8.746 Beiträge seit 2005
vor 16 Jahren

Der Fehler kommt aus der DLL. Debugge sie mal.

T
tintenherz Themenstarter:in
4 Beiträge seit 2007
vor 16 Jahren

Danke svenson für den Hinweis.

Ich habe versucht die DLL zu debuggen was jedoch leider gescheitert ist.

Habe mich an folgende Anleitung gehalten: http://groups.google.de/group/microsoft.public.dotnet.framework.windowsforms/browse_thread/thread/3183883992690024/303cf0ea31491cbd

Zusammenfassung:

  • dafür gesorgt das die Application (C#) die Dll von dort läd wo das C++-Proj. sie
    erstellt.
  • Das C++-Proj. als Startprojekt festgelegt.
  • C++-Proj.->Eigenschaften->Konfigurationseingenschaften->Debuggen->Befehl: Pfad
    zu der Exe der Applikation (C#).

Die Dll wird zwar aufgerufen, jedoch ist kein Debugging möglich.

Sollte jemand eine andere Möglichkeit kennen würde ich mich über einen Hinweis freuen.

T
tintenherz Themenstarter:in
4 Beiträge seit 2007
vor 16 Jahren

Das Debuggen der Dll konnte ich inzwischen erfolgreich durchführen.

Lösung:
Sourcecode der Dll in das Projektverzeichniss kopieren und die Verweispfade löschen.

T
tintenherz Themenstarter:in
4 Beiträge seit 2007
vor 16 Jahren

Ich habe ein leichteres Projekt erstellt in der Hoffnung das Jemand von Euch doch noch einen Vorschlag machen kann.

Mein Problem ist die Nutzung von nativen C-Dll's aus C#, wobei ich die Methoden einer Dll per dllimport in C# importiert habe, diese C-Dll jedoch weitere C++-Dll's inkludiert.

C#-Anwendung: Program.cs

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




namespace WindowsApplication6
{
    /// 
    /// Summary description for Form1.
    /// 
    public class Form1 : System.Windows.Forms.Form
    {
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.TextBox textBox2;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox textBox3;
        /// 
        /// Required designer variable.
        /// 
        private System.ComponentModel.Container components = null;

        public Form1()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            //
            // TODO: Add any constructor code after InitializeComponent call
            //
        }

        /// 
        /// Clean up any resources being used.
        /// 
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code
        /// 
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// 
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.textBox2 = new System.Windows.Forms.TextBox();
            this.label2 = new System.Windows.Forms.Label();
            this.textBox3 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            //
            // button1
            //
            this.button1.Location = new System.Drawing.Point(64, 192);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(144, 64);
            this.button1.TabIndex = 0;
            this.button1.Text = "call sum";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // textBox1
            //
            this.textBox1.Location = new System.Drawing.Point(40, 120);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(72, 22);
            this.textBox1.TabIndex = 1;
            this.textBox1.Text = "2";
            //
            // label1
            //
            this.label1.Location = new System.Drawing.Point(128, 128);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(16, 16);
            this.label1.TabIndex = 2;
            this.label1.Text = "+";
            //
            // textBox2
            //
            this.textBox2.Location = new System.Drawing.Point(152, 120);
            this.textBox2.Name = "textBox2";
            this.textBox2.Size = new System.Drawing.Size(56, 22);
            this.textBox2.TabIndex = 3;
            this.textBox2.Text = "3";
            //
            // label2
            //
            this.label2.Location = new System.Drawing.Point(224, 120);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(24, 23);
            this.label2.TabIndex = 4;
            this.label2.Text = "=";
            //
            // textBox3
            //
            this.textBox3.Location = new System.Drawing.Point(248, 120);
            this.textBox3.Name = "textBox3";
            this.textBox3.Size = new System.Drawing.Size(112, 22);
            this.textBox3.TabIndex = 5;
            this.textBox3.Text = "5";
            //
            // Form1
            //
            this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
            this.ClientSize = new System.Drawing.Size(576, 322);
            this.Controls.AddRange(new System.Windows.Forms.Control[] { this.textBox3, this.label2, this.textBox2, this.label1, this.textBox1, this.button1 });
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }
        #endregion

        /// 
        /// The main entry point for the application.
        /// 
        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }

        #region My Code
        #region Dll Imports
        #endregion
        
        #region Button Click Events

        [DllImport("TestDll1.dll", EntryPoint = "#7",
        ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
        public static extern bool Test1();

        [DllImport("TestDll1.dll", EntryPoint = "#8",
        ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Test1_1();

        [DllImport("TestDll1.dll", EntryPoint = "#9",
        ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
        public static extern int TestInt1();

        private void button1_Click(object sender, System.EventArgs e)
        {
            //int nTestInt = TestInt1();  //Aufruf einer Methode aus Klasse1
            int nTest = Test1_1();      //indirekter Aufruf einer Methode aus Klasse2
            bool bTest = Test1();       //indirekter Aufruf einer Methode aus Klasse2
 
        }
        #endregion
        #endregion
    }
}

C++-Dll Nr. 1 (Klasse1.h) (Name des SubProjekt: TestDll1)

#ifndef KLASSE1_H
#define KLASSE1_H

#include "..\TestDll2\Klasse2.h"

// Klasse1.h : Header-Datei
//

class _declspec(dllexport) CKlasse1
{
// Konstruktion
public:
CKlasse1();
~CKlasse1();
BOOL Test1();
int Test1_1();
int TestInt1();

// Attribute
public:
CKlasse2 m_Klasse2;
CKlasse2 m_Klasse2_2;
};

/////////////////////////////////////////////////////////////////////////////

#endif // KLASSE1_H

C++-Dll Nr. 1 (Klasse1.cpp)

// Klasse1.cpp: Implementierungsdatei
//
#include "stdafx.h"
#include "Klasse1.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = FILE;
#endif

/////////////////////////////////////////////////////////////////////////////
// CKlasse1

CKlasse1::CKlasse1()
{

}

CKlasse1::~CKlasse1()
{
}

BOOL CKlasse1::Test1()
{
return m_Klasse2.Test2(_T("Hallo"));
}

int CKlasse1::Test1_1()
{
return m_Klasse2_2.Test2_2(2, 8);
}

int CKlasse1::TestInt1()
{
int a=2;
int b=9;

return a + b;  

}

C++-Dll Nr. 2 (Klasse2.h) (Name des SubProjekt: TestDll2)

#ifndef KLASSE2_H
#define KLASSE2_H

// Klasse2.h : Header-Datei
//

class __declspec( dllexport ) CKlasse2
{
// Konstruktion
public:
CKlasse2();
~CKlasse2();
BOOL Test2(CString strTest);
int Test2_2(int a, int b);

// Attribute
public:
CString m_strTest;
int m_nTest;
};

/////////////////////////////////////////////////////////////////////////////

#endif // KLASSE1_H

C++-Dll Nr. 2 (Klasse2.cpp)

// Klasse2.cpp: Implementierungsdatei
//
#include "stdafx.h"
#include "Klasse2.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = FILE;
#endif

/////////////////////////////////////////////////////////////////////////////
// CKlasse2

CKlasse2::CKlasse2()
{
int m_nTest= 0;
}

CKlasse2::~CKlasse2()
{
}

BOOL CKlasse2::Test2(CString strTest)
{
m_strTest = strTest;

return TRUE;  

}

int CKlasse2::Test2_2(int a, int b)
{
m_nTest = (a + b);
return m_nTest;
}

Ich gehe wie folgt vor:
-import der Methoden der "Klasse1" per dllimport
-Aufruf der Methoden der "Klasse1" aus der C#-Anwendung (int Test1_1())
-diese ruft Ihrerseits eine Methode der "Klasse2" über eine Membervariable der
"Klasse2" auf.
-der Aufruf der Methode der "Klasse2" funktioniert:

int CKlasse2::Test2_2(int a, int b)
{
m_nTest = (a + b);
return m_nTest;
}

jedoch schlägt die Zuweisung der Summe (a+b) an m_nTest fehlt mit dem Hinweis:
"System.AccessViolationException"
Zusätzliche Informationen: Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist.

Tut mir leid für den langen Text. Ich hatte die Befürchtung das ich das Problem sonst nicht richtig erklären könnte und jemand sich die Mühe macht für eine Aufgabenstellung die auf einem Missverständnis beruht.

Achtung: sollte Jemand den Code kopieren. In der Klasse1.cpp ist eine "()" als Smiley interpretiert worden. Ich weis nicht wie sich da die Zwischenablage verhält.

Sollte jemand diesen ganzen Test gelesen haben und kann dennoch nicht weiterhelfen Danke ich zumidest für die Mühe.

S
8.746 Beiträge seit 2005
vor 16 Jahren

Kommt es dir nicht komisch vor, dass du eine Methode (!) einer Klasse aufrufen kannst, ohne dass du irgendwo ein Objekt hast?

Suche mal "C++" in der Forumssuche und dir wirst einige Antworten auf deine Frage bekommen. Kurz gesagt: Es geht nur mit einiger Handarbeit und Einschränkungen um C++-Methoden aufzurufen.

Grundsätzlich sich C-DLLs so ohne weiteres nur von C (und dem gleichen Compiler) aus aufrufbar.