Laden...
O
oskar27 myCSharp.de - Member
Entwickler Solingen Dabei seit 07.05.2018 12 Beiträge
Benutzerbeschreibung

Forenbeiträge von oskar27 Ingesamt 12 Beiträge

31.01.2023 - 11:50 Uhr

Hey,

vielen Dank für deine abschließende Korrektur - es läuft jetzt! Klasse!!!
Es ist auch nur das char Array nötig und beide Anwendungen können jetzt drauf zu greifen. So simpel wie einfach😉))
Hab vielen Dank, Th69, für deine Tipps, die haben es auf den Weg gebracht und mir die Bäume aus der Sicht geräumt🙂

Hier der abschließende und funktionierende DLL- und C#-Code:

DLL-Code:


#define MAXSYSTEM 1000
#define STRLENGTH 4

#pragma data_seg( ".IPC" ) 										
char strArr[MAXSYSTEM][STRLENGTH] = { "" };
#pragma data_seg()

#pragma region SET STR
float __stdcall SetStr(int gvId, const char* gvStr)
{
	if (gvId >= 0)
	{	
		std::strcpy(strArr[gvId], gvStr);

		return 1.0;
	}
	else
		return -1111114.0;
}
#pragma endregion

#pragma region GET STR
const char* __stdcall GetStr(int gvId)
{
	if (gvId >= 0)
        {
		return strArr[gvId];
	}
	else
		return "Null";
}
#pragma endregion

C#-Code:


[DllImport("C:\\Windows\\SysWOW64\\Alert.DLL", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetStr(int system);

private string IntPtrToStr(int i)
{
        IntPtr ptr = IntPtr.Zero;
        ptr = GVData.GetStr(0);
        string results = Marshal.PtrToStringAnsi(ptr);            
        return results;
}

Gruß
Oskar

26.01.2023 - 23:00 Uhr

Dankeschön Th69 für deine Mühe. Es geht in kleinen Schritten voran🙂
Mit folgendem Code bekomme ich in C#-Anwendung etwas zurück, das jedoch noch nicht dem gespeicherten string entspricht. Ist das jetzt der rechte Weg und nur noch eine "Kleinigkeit" oder generell weiterhin Murks. Denn es erfolgt mit const char*.

Die Anwendung A speichert wie zuvor den string (der aus 1 bis maximal 4 Buchstaben besteht, vergaß ich noch zu erwähnen) per const char* und beschreibt ein statisches string array.
Auf das string array soll jetzt die C#-Anwendung zugreifen. Das könnte doch klappen, von der Idee her zumindest. Wo liegts jetzt noch im Argen?
Die Ausgabe hätte "TEST" lauten müssen.


#pragma data_seg( ".IPC" ) 

float valArr[MAXSYSTEM][MAXCAT][MAXPARAM] = { 0.0 };				//ValArray								
const char* strArrMC[MAXSYSTEM] = { "" };						//ArrayMC			
char strArrCA[MAXSYSTEM][STRLENGTH] = { "" };					//ArrayCA

#pragma data_seg()


#pragma region SET STR ->>wird nur von Anwendung A aufgerufen
float __stdcall SetStr(int gvId, const char* gvStr)
{
	if (gvId >= 0)
	{	
		strArrMC[gvId] = &gvStr[0];

		//FÜR CA
		int size = std::strlen(&gvStr[0]);
		memset(strArrCA[gvId], '\0', STRLENGTH);
		std::memcpy(strArrCA[gvId], &gvStr[0], size);
		//

		return 1.0;
	}
	else
		return -1111114.0;
}
#pragma endregion

#pragma region GET STR MC ->> wird nur von Anwendung A aufgerufen ->>fehlerfrei
const char* __stdcall GetStrMC(int gvId)
{
	if (gvId >= 0)
	{		
		return strArrMC[gvId];

	}
	else
		return "Nulll";
}
#pragma endregion

#pragma region GET STR CA ->> wird nur von C#-Anwendung aufgerufen -> nicht korrekte Zeichen aktuell (siehe screen)
const char* __stdcall GetStrCA(int gvId)
{
	if (gvId >= 0)
	{
		const char* myData = "";
		for (int j = 0; j < 5; j++)
		{
			myData += strArrCA[gvId][j];
		}
		return myData;

	}
	else
		return "Nulll";
}
#pragma endregion

C#-Aufruf an sich wie zuvor:


[DllImport("C:\\Windows\\SysWOW64\\Alert.DLL", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
 public static extern IntPtr GetStrCA(int system);

private string IntPtrToStr(int i)
        {
            IntPtr ptr = IntPtr.Zero;
            ptr = GVData.GetStrCA(0);
            string results = Marshal.PtrToStringAnsi(ptr);            
            return results;
        }

24.01.2023 - 19:12 Uhr

Hey,

dankeschön, ein neuer Schritt: mit der fixen Initialisierung rufen beide Anwendungen diese strings korrekt und vollständig ab. Ab nicht-initialisiertem Index wird null zurückgegeben. Klar soweit.
Jedoch: wenn Anwendung A jetzt einen der initialisierten Indizes neu beschreibt, ruft die C#-Anwendung wieder einen Leer-String ab. Anwendung A ruft den neu geschriebenen string korrekt ab. Unklar für mich.


#pragma data_seg( ".IPC" ) 

float valArr[MAXSYSTEM][MAXCAT][MAXPARAM] = { 0.0 };				//ValArray
const char* strArr[MAXSYSTEM] = { "test1", "test2", "test3"};		        //StrArray

#pragma data_seg()

Gruß
Oskar

23.01.2023 - 19:39 Uhr

Hey,

bei -1 gibt er den korrekten und vollständigen String aus, yeah. Supi! (habe in der DLL "Nulll" für diesen Fall eingetragen).
Bei ≥ 0 gibt er nur einen leeren String zurück. Der Zugriff auf die gespeicherten Strings im Array klappt also noch nicht. Die sind jedoch beschrieben. Die Anwendung A gibt die gespeicherten strings aus dem Array intern korrekt zurück.

Das gleiche Ergebnis (leerer String in C#-Anwendung) erhalte ich mit der Deklaration des strArrays und des Rückgabetyps der GetStr-Methode als const char*


#pragma data_seg( ".IPC" ) 

float valArr[MAXSYSTEM][MAXCAT][MAXPARAM] = { 0.0 };				//ValArray
const char* strArr[MAXSYSTEM] = { "" };							//StrArray

#pragma data_seg()

const char* __stdcall GetStr(int gvId)
{
	if (gvId >= 0)
	{
		return strArr[gvId];
	}
	else
		return "Nulll";
}

Gruß
Oskar

23.01.2023 - 17:04 Uhr

Hi Th69,

vielen Dank für diese Infos. Die C#-Anwendung ist auch eine 64Bit-Anwendung. Da die Pfade absolut eingestellt sind, macht es keinen Unterschied. Habe auch System32 getestet, mit dem gleichen Resultat. Aber stimmt, die 64-Bit-DLLs müssen ins System32 👍

Den IntPtr Tipp habe ich implementiert. Entweder nicht korrekt implementiert, oder es fehlt noch etwas am "Glück". Die Übersetzung in string gibt einen leeren string zurück. Ohne Fehlermeldung. In der DLL ist der Rückgabetyp LPSTR. Das passt vermutlich nicht?

C#-Code:


[DllImport("C:\\Windows\\System32\\ChartAlert.DLL", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetStr(int system);

private string IntPtrToStr(int i)
        {
            IntPtr ptr = IntPtr.Zero;
            ptr = GVData.GetStr(i);
            string results = Marshal.PtrToStringAnsi(ptr);
            return results;
        }

Gruß
Oskar

23.01.2023 - 16:56 Uhr

Hallo M.L.,

bis auf die STR-Option funktioniert alles wie angedacht. Compiler ist VS2019.

Gruß
Oskar

20.01.2023 - 20:27 Uhr

Hi Th69 nochmal,

bei -1 sollte "0" zurück gegeben werden. Hierbei stürzt Anwendung B ab und schließt ohne Exception.

20.01.2023 - 18:45 Uhr

Hallo Th69,

Ja sorry, da hatte ich LPStr auch getestet, oben falsch gepostet. Die Fehlermeldung ist die bekannte:

Fehlermeldung:
System.AccessViolationException
HResult=0x80004003
Nachricht = 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.
Quelle = mscorlib
Stapelüberwachung:
at System.String..ctor(SByte* value)
at System.StubHelpers.CSTRMarshaler.ConvertToManaged(IntPtr cstr)
....

Hast du eine andere Anwendung, welche auch diese DLL nutzt? Dann hat aber jede Anwendung ihre eigenen Daten.

Ja. Diese Anwendung A schreibt hauptsächlich das Array. Die Anwendung B (C#-Anwendung) soll neben float auch strings auslesen. float-Werte klappt einwandfrei. Die Daten aus der DLL können von beiden Anwendungen geschrieben und gelesen werden. Nur string klappt noch nicht.

GetStr(i) - i soll der Array-Index sein. -1 funktioniert daher nicht. Auf jedem Index ist ein String gespeichert. Das Speichern funktioniert, weil die Anwendung A diese schreibt und auch selbst auslesen kann. Die Anwendung B soll jetzt auch auf diesen String-Speicher von Anwendung A zugreifen können und per Array-Index diese abrufen können.

20.01.2023 - 16:53 Uhr

Hallo spook, Th69 und M.L.,

vielen Dank! Das habe ich implementiert.
@M.L. ja, das kann auch per string Typ laufen, siehe unten const char*...
@spook, Th69: Die Deklaration im C#-Import sieht jetzt so aus:


[DllImport("C:\\Windows\\SysWOW64\\Alert.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStruct)]
public static extern string GetStr(int system);

Die Fehlermeldung ist jetzt folgende (durch return: MarshalAs bedingt. CharSet-Angabe hat hierbei keine Änderung bewirkt):

Fehlermeldung:
Message = "&quot;return value&quot; kann nicht gemarshallt werden: Ungültige verwaltete/nicht verwaltete Typenkombination (Zeichenfolgenparameter müssen mit LPStr, LPWStr, LPTStr, BStr, TBStr, VBByRefStr oder AnsiBStr kombiniert werden).."

Die eine Anwendung kann string und LPSTR verarbeiten. Per string hatte ich in der DLL das Array als const char* Typ deklariert, was in dieser Anwendung wie auch LPSTR funktioniert.
Wäre der C#-Abruf per const char* einfacher zu händeln? (in der C#-Anwendung soll nur string-Abruf funktionieren, das Schreiben des Strings übernimmt ausschließlich die andere Anwendung).

DLL-Deklaration mit const char* Array:


const char* strArr[1000] = { "" };

const char* __stdcall GetStr(int gvId)
{
	if (gvId >= 0)
	{
		return strArr[gvId];
	}
	else
		return "0";
}

C#-Code:


[DllImport("C:\\Windows\\SysWOW64\\Alert.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern string GetStr(int gvId);

Fehlermeldung:
System.AccessViolationException
HResult=0x80004003
Nachricht = 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.
Quelle = mscorlib
Stapelüberwachung:
at System.String..ctor(SByte* value)
at System.StubHelpers.CSTRMarshaler.ConvertToManaged(IntPtr cstr)
...

Was könnte noch helfen? Vielen Dank!

Gruß
Oskar

19.01.2023 - 19:48 Uhr

Hallo zusammen,

ich kämpfe mit einer "AccessViolationException" ("Es wurde versucht, geschützten Speicher zu lesen oder zu schreiben...") in meiner C#-Anwendung, die einen String aus einem Array aus einer DLL abrufen soll. Das string-Array wird von einer weiteren Anwendung gefüttert. Innerhalb jener Anwendung erfolgt der Zugriff auf das Array ohne Probleme (schreiben und abrufen).
Die DLL händelt auch noch int/double-Werte, die problemlos in beiden Anwendungen geschrieben und abgerufen werden. Nur string macht mir die Sorgen:

DLL-Methode:


LPSTR strArr[1000] = { "" };

LPSTR __stdcall GetStr(int gvId)
{
	if (gvId >= 0)
	{
		return strArr[gvId];
	}
	else
		return "0";
}

C#-Aufruf:


 [DllImport("C:\\Windows\\SysWOW64\\Alert.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern string GetStr(int gvId);

string str = GetStr(i);

Wie kann ich den Zugriff auf diesen "geschützten Speicher" in C# realisieren? Vermutlich marshallen? Bin hauptsächlich in C# "zu hause". Es fällt mir schwer, in C++ zu arbeiten....
Auch eine Deklaration z.B. mit const char* (statt LPSTR) lässt die Fehlermeldung beim C#-Zugriff aufkommen, wobei die zweite Anwendung auch hiermit funktioniert und das const char* Array beschreibt und ausliest.

Besten Dank und Grüße
Oskar

10.05.2018 - 23:05 Uhr

Hallo herbivore, vielen Dank für Deine Ausführung! Da ich momentan nur einen BW habe, der die Daten getimed ausliest und ggf. einen DBInsert anstößt, muss also z.B. ein zweiter getimeter BW dauerhaft mit dem Dequeing beschäftigt werden? Kollisionen irgendeiner Art mit dem Queuing sind da nicht zu erwarten?
Gibt es ev. noch eine weitere Möglichkeit, ähnlich dem ThreadPool, nur eben sequentiell, wo ich ohne zusätzliches Dequeuing auskomme? (.Net 2.0 ...)
VG

07.05.2018 - 13:38 Uhr

Hallo liebe Community,

ich bin neu im Forum und würde mich über einen Tipp freuen, der mir in folgender Problemstellung weiter helfen kann: Ich nutze einen Backgroundworker der per Timer Daten abruft. Wunderbar soweit. Bei Auftreten bestimmter Daten soll ein Datenbankeintrag erfolgen. Das Auftreten von neuen Daten erfolgt z.T. schneller als der DBInsert. Es muss sicher gestellt werden, dass immer ein DBInsert erfolgt. Also nix "verloren" geht bzw. wegen Fehlzugriff ("Meldung Zugriff auf geschützten Speicher...") nicht erfolgt. Es können maximal 20 "gleichzeitige" Zugriffe sein, die geregelt werden müssten.
Ich habe mir dazu die SyncQueue >> von #herbivore angesehen, die möglicherweise passt, allerdings die Schleife des WorkerThreads passt bei mir nicht ins Konzept. Die Aufrufe "DBInsert" müssten sich in die Queue einfügen und direkt selbst dequeuen, bis alle Inserted sind.
Welches Konzept wäre für diesen Fall das beste? Habe es ebenfalls mit Threadpool und Autoreset probiert. Aber hier wohl nicht korrekt umgesetzt. Es gab auch hierbei die Exception "Zugriff auf geschützten Speicher...".
Also in einem Satz, die DBInserts sollen parallel zum BackgroundworkerThread sequentiell abgearbeitet werden können, ohne sich zu behindern oder verloren zu gehen.
Edit: Was die Vielfalt an Möglichkeiten etwas einschränkt ist: Targeting auf .Net Framework 2.0.
Vielen Dank im voraus.

Beste Grüße
Oskar