[C#] Attempted to read or write protected memory

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik ben bezig een functie aan te roepen uit een geleverde dll. De beschrijving die ik bij de dll heb geeft van de functie de volgende beschrijving:
get_tuner_driver_version
The get_tuner_driver_version function returns a string containing the revision number and release date
of the DLL.

void get_tuner_driver_version (char version_string[ ])

Parameters
version_string

A string containing the version and release date of the tuner driver DLL. This string should be at least 80
characters long.

Return Value
None.

Mijn C# code is als volgt:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
string sDriverVersion;

//Call drivers version function from DLL file.
[DllImport("MLibTuner.dll", EntryPoint = "get_tuner_driver_version", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern void mauryGetDriverVersion(string sVersion);
        
public frmMain()
{
      InitializeComponent();

      mauryGetDriverVersion(sDriverVersion);
      lblDriverVersion.Text = sDriverVersion;
      //lblStatus.Text = Convert.ToString(Marshal.GetLastWin32Error());
}


Nu krijg ik de volgende foutmelding:

System.AccessViolationException was unhandled
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Ik heb al wat rond gelezen en ben niet de enige met dit probleem ;) maar ben er nog niet uit.

C#:
1
2
3
[DllImport("MLibTuner.dll", EntryPoint = "get_tuner_driver_version", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[SecurityPermission(SecurityAction.Assert, Unrestricted = true)]
public static extern void mauryGetDriverVersion(string sVersion);

Heb ik geprobeerd maar blijft dezelfde fout geven.

Iemand tips hoe ik dit op kan lossen?

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16-09 09:15

Janoz

Moderator Devschuur®

!litemod

sDriverVersion is nog helemaal niet geinitialiseerd. Waarschijnlijk probeert de externe dll nu iets weg te schrijven naar null.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Jeroen V
  • Registratie: Februari 2004
  • Laatst online: 16-09 21:23

Jeroen V

yadda yadda yadda

Wat voor type is die sVersion in de originele library? Misschien dat je een andere marshaling moet gebruiken? (bv UnmanagedType.LPStr of UnmanagedType.BStr?)

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Je moet een new StringBuilder(80) aangeven, voor de zekerheid zou ik zelfs min. 100 nemen, en de declaratie aanpassen naar StringBuilder.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bedankt voor de hulp het gebruiken van de stringbuilder werkte inderdaad!

Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Verwijderd schreef op maandag 06 september 2010 @ 16:31:
Bedankt voor de hulp het gebruiken van de stringbuilder werkte inderdaad!
Maar belangrijker: snap je nu ook waarom?

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Woy schreef op maandag 06 september 2010 @ 18:37:
[...]

Maar belangrijker: snap je nu ook waarom?
Ik neem aan zoals janoz zei dat de string nog niet geinitialiseerd was en dat je daardoor naar een stuk geheugen wil schrijven waar je geen toegang tot hebt.
De stringbuilder initialiseerd de string wel goed waardoor de dll er wel wat in weg kan zetten

Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Het punt is, dat het in C of C++ gebruikelijk is dat een caller van een functie al een stuk geheugen reserveert, waar het geheugen in C# vaak door de callee gereserveerd word.

Er word dus geschreven naar de plek waar jij een verwijzing naar geeft. Aangezien je string reference nog NULL is verwijst hij nog niet naar een geldig stuk geheugen. Op het moment dat je daar naar schrijft, krijg je dus die AccessViolationException. Een stringbuilder initialiseert intern gewoon een array van characters en reserveert daar dus ook al geheugen voor. Bij het marshallen van die StringBuilder word het adres van die array mee-gegeven en dan kan er dus wel in het geheugen geschreven worden. Je moet er dus wel op letten dat de StringBuilder genoeg geheugen reserveert anders kan het nog steeds zo zijn dat er dingen fout gaan, want dan kan er buiten de grenzen van die array geschreven worden.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Woy schreef op dinsdag 07 september 2010 @ 10:25:
Bij het marshallen van die StringBuilder word het adres van die array mee-gegeven en dan kan er dus wel in het geheugen geschreven worden.
Dit gebeurd nadat het adres pinned gemaakt is (afhankelijk van het string-type trouwens). Bij strings (niet StringBuilder) wordt altijd een copy gemaakt, omdat string immutable is, vandaar dat je hier StringBuilder moet gebruiken.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Volgens mij snap ik het toch nog niet helemaal. Ik roep nu een andere functie van dezelfde dll aan.

De beschrijving:
add_tuner
The add_tuner function adds or updates one tuner in the tuner driver object.
short add_tuner(short tuner_number, char model[ ], short serial_number, short ctlr_num,
short ctlr_port, short *no_of_motors, long max_range[ ], double *fmin, double *fmax,
double *fcrossover, char error_string[ ])


Parameters
tuner_number
The index to the tuner to be added within the tuner driver object. This should be a number from 0 through 7.
model
A string containing the tuner model.
serial_number
The serial number of the tuner.
ctlr_num
The index of the controller to which the tuner will be connected. This should be a number from 0 through 7.
ctlr_port
The port number on the controller to which the tuner will be connected.
no_of_motors
This parameter returns the number of motors in the specified tuner model.
max_range
This parameter returns an array of three numbers, which are the max position ranges for the motors in the
specified tuner model.
fmin
This parameter returns the minimum operating frequency in GHz of the specified tuner model.
fmax
This parameter returns the maximum operating frequency in GHz of the specified tuner model.
fcrossover
This parameter returns the crossover frequency in GHz where operation switches between the low and
high frequency probes of the specified tuner model.
error_string
A null terminated string which may contain an error message when an error has occurred. This string
should be at least 80 characters long.
Return Value
An error flag. This will be zero if the function completes normally. If this is non-zero, an error has
occurred.


mijn code:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
short iErrorFlag = new short(), iTunerNumber, iSerialNumber, iControllerNumber, iControllerPort, iMotors = new short();
string sModel;
        
StringBuilder sError = new StringBuilder(100);
long lMaxRange = new long();
double dFmin = new double(), dFmax = new double(), dFcross = new double();

[DllImport("MLibTuner.dll", EntryPoint = "add_tuner", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern unsafe short mauryAddTuner(short tuner_number, string model, 
          short serial_number, short ctlr_num, short ctlr_port, short* no_of_motors,
          long max_range, double* fmin, double* fmax, double* fcrossover, 
          StringBuilder error_string);

iTunerNumber = 1;
sModel = Convert.ToString(cbTuner.SelectedItem);
iSerialNumber = 2;
iControllerNumber = 0;
iControllerPort = Convert.ToInt16(cbTunerport.SelectedItem);

unsafe
{
    fixed (short* pMotors = &iMotors)
    {
        fixed (double* pFmin = &dFmin, pFmax = &dFmax, pFcross = &dFcross)
        {
            iErrorFlag = mauryAddTuner(iTunerNumber, sModel, iSerialNumber, 
                              iControllerNumber, iControllerPort, pMotors, lMaxRange, 
                              pFmin, pFmax, pFcross, sError);
        }
    }
}


Zelfde fout :(

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Kijk eens naar "long max_range[ ]", dat is een array. Verder is dit een vreemde combinatie tussen unsafe-code en marshalling/p/invoke; waarom die unsafe code eromheen? Je kunt je inlezen via die linkjes in mijn vorige post.

offtopic:
Dingen als new long/short/double() doen niet zoveel. En ik ben geen fan van hungarian notation :p

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Het was idd de lMaxRange :S heb er pas anderhalf uur overheen gekeken ;)

Ik heb die links door gekeken maar snapte het nog niet helemaal.

Toen heb ik gekeken naar pointers in c# en gevonden dat je daarvoor unsafe moet gebruiken.

Ik heb nog wel zitten kijken naar de GCHandle. Snap ik het goed dat als je een variabele pinned dat er dan ook een pointer naar gemaakt wordt?
Ik kreeg het daarmee niet aan de praat en met de unsafe code wel ;)

De new long/short/double() was een wanhopige poging om het werkend te krijgen :o

[ Voor 8% gewijzigd door Verwijderd op 07-09-2010 17:10 ]


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Verwijderd schreef op dinsdag 07 september 2010 @ 17:08:
Toen heb ik gekeken naar pointers in c# en gevonden dat je daarvoor unsafe moet gebruiken.
In principe heb je geen pointers nodig, dat word door de marshalling geregeld. Je moet goed kijken wat voor parameters het zijn. In, In/Out of Out.

Als iets alleen een In parameter is, dan kun je in principe gewoon by value doorgeven. Als er ook iets terug gegeven moet worden, dan kan dat niet zomaar met value types. Je kunt dan een parameter als "out" of als "ref" parameter specificeren.

Verder is het ook van belang om te kijken of er een array verwacht word, of een enkele value ( wat natuurlijk gelijk staat aan een array met 1 element ). Als er een array verwacht word, dan zul je dus ook een array door moeten geven die groot genoeg is voor de methode die hem gaat vullen.
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[DllImport("MLibTuner.dll", EntryPoint = "add_tuner", SetLastError = true, CallingConvention = CallingConvention.StdCall)] 
public static extern short mauryAddTuner(short tuner_number, string model,  
          short serial_number, short ctlr_num, short ctlr_port, out short no_of_motors, 
          long max_range, out double fmin, out double fmax, out double fcrossover,  
          StringBuilder error_string);

...

short no_of_motors;
double fmin, fmax, fcrossover;

// ... zelf invulen met values die je wil
mauryAddTuner( ... out no_of_motors, .., out fmin, out fmax, out fcrossover ... );

//Hier zijn no_of_motos, fmin, fmax en fcrossover gevuld door mauryAddTuner


Het is overigens handig als je je wat beter inleest in hoe het marshallen en P/Invoke werkt. Hier staat bijvoorbeeld een artikel van MS: http://msdn.microsoft.com/en-us/magazine/cc164123.aspx

[ Voor 52% gewijzigd door Woy op 07-09-2010 18:46 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bedankt voor de hulp en de links, het is allemaal nog nieuw en alle informatie is welkom ;)

Ik heb wel met c en c++ gewerkt en daar pointers gebruikt dus vandaar dat daar mijn gedachte als eerst naar toe ging.

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-09 22:43
Woy schreef op dinsdag 07 september 2010 @ 10:25:
Het punt is, dat het in C of C++ gebruikelijk is dat een caller van een functie al een stuk geheugen reserveert, waar het geheugen in C# vaak door de callee gereserveerd word.
Meestal echter mag je wel de lengte opgeven van de string die je had gereserveerd. Het komt trouwens ook best vaak voor dat de callee geheugen malloced die vervolgens door de caller moet worden vrijgegeven.

De manier waarop het hier gedaan wordt ben ik nog niet vaak tegengekomen eigenlijk. ( Dwz, bot ervan uitgaan dat er minstens 80 characters beschikbaar zijn )

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1