471,342 Members | 1,825 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,342 software developers and data experts.

ref parameter

Hi group,

I have a class (TheClass) with static methods from an external DDL.

private void Test(ref byte param)
{
byte a = 0, b = 0;
param = 1;
TheClass.CallToDLLFunction(ref a, ref b);
param = 2;
}

private void Test2(ref byte param)
{
byte a = 0, b = 0;
param = 1;
TheClass.CallToOtherDLLFunction(ref a, ref b);
param = 2;
}

....
v=0
Test(ref v)
// v = 2, as expected

Test2(ref v)
// v = 1. There is no exception thrown
....

------------

I don't understand how a extern function can have any influence on the
assignment of the ref parameters.

Can someone explain this behaviour, please?

Thanks.
Jun 27 '08 #1
14 1696
On Jun 6, 11:25 am, Mathijs <math...@ongeldig.nlwrote:

<snip>
I don't understand how a extern function can have any influence on the
assignment of the ref parameters.

Can someone explain this behaviour, please?
If you put some other kind of diagnostics in Test2 before and after
the call to CallToOtherDLLFunction, what do you see? My guess is that
it *may* be trampling on the stack somehow, but it's only a guess.

Jon
Jun 27 '08 #2
Jon Skeet [C# MVP] schreef:
If you put some other kind of diagnostics in Test2 before and after
the call to CallToOtherDLLFunction, what do you see?
Thanks for your reply. I'm not experienced with C# and I'm not sure what
you mean with 'other kind of diagnostics'.

The platform is Compact Framework, by the way.

You mean to check if the line 'param = 2' is actually reached?

private void Test2(ref byte param)
{
byte a = 0, b = 0;
param = 1;
MessageBox.Show("1:"+param.ToString());
TheClass.CallToOtherDLLFunction(ref a, ref b);
MessageBox.Show("2:"+param.ToString());
MessageBox.Show("3:"+param.ToString());
param = 2;
}

Running Test2 shows three messageboxes with 1:1, 2:1, 3:2 (so, inside
Test2, the param value is 2). However, in the code which calls Test2,
the value of the parameter is 1 (after calling 2).
My guess is that
it *may* be trampling on the stack somehow, but it's only a guess.
Ah... not sure what it means, but there there a way how I can solve it?
Or is there something wrong with how I use the ref parameters?

Cheers,
Mathijs.
Jun 27 '08 #3
Jon Skeet [C# MVP] schreef:
My guess is that
it *may* be trampling on the stack somehow, but it's only a guess.
I found the cause, but still I don't understand how it could influence
the assignement of parameters in the calling function...

What causes this, is that b is actually a array of bytes.

byte a = 0
byte[] b = new byte[7];
TheClass.CallToDLLFunction(ref a, ref b[0]);

Cheers,
Mathijs.
Jun 27 '08 #4
On Jun 6, 1:12 pm, Mathijs <math...@ongeldig.nlwrote:
Jon Skeet [C# MVP] schreef:
If you put some other kind of diagnostics in Test2 before and after
the call to CallToOtherDLLFunction, what do you see?

Thanks for your reply. I'm not experienced with C# and I'm not sure what
you mean with 'other kind of diagnostics'.
Console.WriteLine tends to be my favourite. MessageBox.Show if
necessary. Appending to a file if that helps. Nothing terribly
inventive or hi-tech :)
The platform is Compact Framework, by the way.

You mean to check if the line 'param = 2' is actually reached?
Exactly.
private void Test2(ref byte param)
{
byte a = 0, b = 0;
param = 1;
MessageBox.Show("1:"+param.ToString());
TheClass.CallToOtherDLLFunction(ref a, ref b);
MessageBox.Show("2:"+param.ToString());
MessageBox.Show("3:"+param.ToString());
param = 2;
}

Running Test2 shows three messageboxes with 1:1, 2:1, 3:2 (so, inside
Test2, the param value is 2).
In that case I suspect you haven't posted the actual code - I assume
in your real code, the final call to MessageBox.Show is *after*
param=2?
However, in the code which calls Test2,
the value of the parameter is 1 (after calling 2).
That's very odd indeed.
My guess is that
it *may* be trampling on the stack somehow, but it's only a guess.

Ah... not sure what it means, but there there a way how I can solve it?
Or is there something wrong with how I use the ref parameters?
I don't think so.

Okay, slightly odd thing to do: is the value of the parameter a member
variable (or can you make it one, temporarily)? If so, can you make a
call to another method at the places you've currently got
MessageBox.Show, to see what the value is? In theory it should always
be the same as param.

Jon
Jun 27 '08 #5
On Jun 6, 2:19 pm, Mathijs <math...@ongeldig.nlwrote:
My guess is that
it *may* be trampling on the stack somehow, but it's only a guess.

I found the cause, but still I don't understand how it could influence
the assignement of parameters in the calling function...

What causes this, is that b is actually a array of bytes.

byte a = 0
byte[] b = new byte[7];
TheClass.CallToDLLFunction(ref a, ref b[0]);
That code looks quite different to the code you've posted before. Can
you post a short but complete (other than the DLL code itself) program
which demonstrates it on your box? I expect I won't be able to run it
easily myself, but I'd be interested to see more realistic code.

If you're able to reproduce it in a non-CF app, that would be even
better :)

Jon
Jun 27 '08 #6
Jon Skeet [C# MVP] schreef:
That code looks quite different to the code you've posted before. Can
you post a short but complete (other than the DLL code itself) program
which demonstrates it on your box? I expect I won't be able to run it
easily myself, but I'd be interested to see more realistic code.
Just for the record: it is solved now. However, I still can't explain
the behaviour.

1. After calling Test1(ref p), p==1 and three messageboxes appear
(1:1,2:1,3:2).

2. After calling Test2(ref p), p==2 and three messageboxes appear
(1:1,2:1,3:2).

The difference between Test1 and Test2 (see below) is the declaration of
SerialNo. I didn't RTFM and passed a byte in stead of the first element
of an array of bytes. Stupid, I know. But how can that ever have any
influence on ref parameters? I can't reproduce this, because the dll
function communicated with hardware attached to the mobile device.

private void Test1(ref byte param)
{
byte TagType = 0, SerialNo = 0;
param = 1;
MessageBox.Show("1:" + param.ToString());
T4CE.T4SelectTag(ref TagType, ref SerialNo);
MessageBox.Show("2:" + param.ToString());
param = 2;
MessageBox.Show("3:" + param.ToString());
}

private void Test2(ref byte param)
{
byte TagType = 0;
byte[] SerialNo = new byte[8];
param = 1;
MessageBox.Show("1:" + param.ToString());
T4CE.T4SelectTag(ref TagType, ref SerialNo[0]);
MessageBox.Show("2:" + param.ToString());
param = 2;
MessageBox.Show("3:" + param.ToString());
}
Jun 27 '08 #7
On Jun 6, 3:34 pm, Mathijs <math...@ongeldig.nlwrote:

<snip>
The difference between Test1 and Test2 (see below) is the declaration of
SerialNo. I didn't RTFM and passed a byte in stead of the first element
of an array of bytes. Stupid, I know. But how can that ever have any
influence on ref parameters?
What you're passing is the address of some data to change. So, when
you do this the right way, you're passing it the address of the start
of the array, and so the function started writing over the top of the
array.

When you did it the wrong way, you passed it the address of the byte
on the stack. Now, I'm going to *guess* that the stack looked
something like:

[value of TagType]
[value of SerialNo]
[address of param]

(because param is passed in by reference)

The DLL then merrily writes over SerialNo *and then part of 'address
of param'*. Not a problem so far, but...

param = 2;

then takes the address of param (which is now screwed) and writes the
value 2 to some random place in memory - in particular, *not* the
place that the caller associates with the parameter. Thus you still
see changes while you're in the method, but not when you're back at
the caller.

Does that make any sense? I'm slightly surprised at param being after
SerialNo on the stack instead of before it, but I really don't know
much about the details at that level. That's one way I could imagine
things going bad though.

Jon
Jun 27 '08 #8
Hey Gang,

My guess here is that the DLL is using pointers and that they are trampling
over your other data. That is the danger with pointers and why MSFT is
pushing Managed Code.

I'm sure Mr. Skeet is aware of this; he just never explicitly said it.

So, the DLL is treating "b" as a pointer and writing to "b"; however, "b"
wasn't large enough to hold everything. The DLL kept writing without a way to
check that it wasn't overwriting anything. Net result: It was overwriting
something: The byte you passed in, "param".

Pointer errors can be really hard to figure out, and they make your code
seem like it is doing some very weird stuff when they start pointing in the
wrong directions.

"Mathijs" wrote:
Jon Skeet [C# MVP] schreef:
That code looks quite different to the code you've posted before. Can
you post a short but complete (other than the DLL code itself) program
which demonstrates it on your box? I expect I won't be able to run it
easily myself, but I'd be interested to see more realistic code.

Just for the record: it is solved now. However, I still can't explain
the behaviour.

1. After calling Test1(ref p), p==1 and three messageboxes appear
(1:1,2:1,3:2).

2. After calling Test2(ref p), p==2 and three messageboxes appear
(1:1,2:1,3:2).

The difference between Test1 and Test2 (see below) is the declaration of
SerialNo. I didn't RTFM and passed a byte in stead of the first element
of an array of bytes. Stupid, I know. But how can that ever have any
influence on ref parameters? I can't reproduce this, because the dll
function communicated with hardware attached to the mobile device.

private void Test1(ref byte param)
{
byte TagType = 0, SerialNo = 0;
param = 1;
MessageBox.Show("1:" + param.ToString());
T4CE.T4SelectTag(ref TagType, ref SerialNo);
MessageBox.Show("2:" + param.ToString());
param = 2;
MessageBox.Show("3:" + param.ToString());
}

private void Test2(ref byte param)
{
byte TagType = 0;
byte[] SerialNo = new byte[8];
param = 1;
MessageBox.Show("1:" + param.ToString());
T4CE.T4SelectTag(ref TagType, ref SerialNo[0]);
MessageBox.Show("2:" + param.ToString());
param = 2;
MessageBox.Show("3:" + param.ToString());
}
Jun 27 '08 #9
Jon Skeet [C# MVP] wrote:
I'm slightly surprised at param being after
SerialNo on the stack instead of before it, but I really don't know
much about the details at that level.
That makes perfect sense. The stack grows downwards in memory.

The reason for this is historic. Back in DOS time, when an application
was started, it would get all available memory, with the program code
loaded at the beginning, and the stack growing from the end of the
available memory and downwards.

There just hasn't been any reason to change the direction of the stack
since then... :)

--
Göran Andersson
_____
http://www.guffa.com
Jun 27 '08 #10
On Jun 6, 4:57 pm, Göran Andersson <gu...@guffa.comwrote:
I'm slightly surprised at param being after
SerialNo on the stack instead of before it, but I really don't know
much about the details at that level.

That makes perfect sense. The stack grows downwards in memory.

The reason for this is historic. Back in DOS time, when an application
was started, it would get all available memory, with the program code
loaded at the beginning, and the stack growing from the end of the
available memory and downwards.

There just hasn't been any reason to change the direction of the stack
since then... :)
Excellent. I think that just about wraps up all the mysteries then,
yes?

It's always nice when something nonsensical finally makes sense at the
end :)

Jon
Jun 27 '08 #11
On Fri, 06 Jun 2008 08:57:42 -0700, Göran Andersson <gu***@guffa.com>
wrote:
[...]
The reason for this is historic. Back in DOS time, when an application
was started, it would get all available memory, with the program code
loaded at the beginning, and the stack growing from the end of the
available memory and downwards.
For what it's worth, downward-growing stacks predate DOS (actually,
predate the 8088 -- the first CPU DOS was used with -- as this was a CPU
architecture issue not an OS issue) by quite a bit, and continue to exist
today even in other relatively modern architectures. :)

Pete
Jun 27 '08 #12
On Jun 6, 11:07 am, "Jon Skeet [C# MVP]" <sk...@pobox.comwrote:
On Jun 6, 3:34 pm, Mathijs <math...@ongeldig.nlwrote:

<snip>
The difference between Test1 and Test2 (see below) is the declaration of
SerialNo. I didn't RTFM and passed a byte in stead of the first element
of an array of bytes. Stupid, I know. But how can that ever have any
influence on ref parameters?

What you're passing is the address of some data to change. So, when
you do this the right way, you're passing it the address of the start
of the array, and so the function started writing over the top of the
array.

When you did it the wrong way, you passed it the address of the byte
on the stack. Now, I'm going to *guess* that the stack looked
something like:

[value of TagType]
[value of SerialNo]
[address of param]

(because param is passed in by reference)

The DLL then merrily writes over SerialNo *and then part of 'address
of param'*. Not a problem so far, but...
Why would the dll overwrite the address of param? Just curious..
param = 2;

then takes the address of param (which is now screwed) and writes the
value 2 to some random place in memory - in particular, *not* the
place that the caller associates with the parameter. Thus you still
see changes while you're in the method, but not when you're back at
the caller.

Does that make any sense? I'm slightly surprised at param being after
SerialNo on the stack instead of before it, but I really don't know
much about the details at that level. That's one way I could imagine
things going bad though.

Jon
Jun 27 '08 #13
On Fri, 06 Jun 2008 10:09:32 -0700, parez <ps*****@gmail.comwrote:
[...]
>When you did it the wrong way, you passed it the address of the byte
on the stack. Now, I'm going to *guess* that the stack looked
something like:

[value of TagType]
[value of SerialNo]
[address of param]

(because param is passed in by reference)

The DLL then merrily writes over SerialNo *and then part of 'address
of param'*. Not a problem so far, but...

Why would the dll overwrite the address of param? Just curious..
Let's make sure we understand what is meant by "param" first. :)
Technically, "param" is the local argument to the method. The "address"
of that is simply an offset relative to the current stack frame. But I
don't think that's what's meant in the text quoted.

Rather, in that context, "param" refers to the original parameter, which
is passed as an address stored in a location that is referenced as an
offset relative to the current stack frame. Here, the phrase "address of
param" refers not to the actual address of the local argument relative to
the current stack frame, but rather to the value stored in that location,
the address of the original parameter..

Confused yet? :)

What happens is that when the DLL is called, the OP incorrectly passed the
address of a single byte of storage, rather than an array of bytes of
storage. Furthermore, that single byte was stored on the stack as a local
variable, rather than in the heap. So, when the called function went to
write data to what it thought was an array of bytes, it was actually
writing to that local variable.

But the address of the original parameter passed as "param" was stored in
the stack just past that local variable. Since the local variable didn't
use enough of the stack to accomodate all of the data that the called
function tried to write to it, the address of "param" winds up getting
overwritten as well.

Ironically, the code could have failed in much more spectacular ways. The
OP is fortunate to have had such a clearly incorrect result, but one which
was still testable. :)

Pete

(Bonus reading: of course, the "single byte" variable actually takes up
more room on the stack than just the one byte. I haven't checked, but
probably it's consuming a full 4 bytes, due to alignment requirements.
But that's still not enough for the called function, which apparently was
expecting 7 bytes [why 7? I don't know...that's a weird size for a
buffer]. So three more bytes get written into the "address of param"
storage...just one byte would have been enough to cause the later
assignment to fail, and so it does).
Jun 27 '08 #14
On Jun 6, 1:45 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com>
wrote:
On Fri, 06 Jun 2008 10:09:32 -0700, parez <psaw...@gmail.comwrote:
[...]
When you did it the wrong way, you passed it the address of the byte
on the stack. Now, I'm going to *guess* that the stack looked
something like:
[value of TagType]
[value of SerialNo]
[address of param]
(because param is passed in by reference)
The DLL then merrily writes over SerialNo *and then part of 'address
of param'*. Not a problem so far, but...
Why would the dll overwrite the address of param? Just curious..

Let's make sure we understand what is meant by "param" first. :)
Technically, "param" is the local argument to the method. The "address"
of that is simply an offset relative to the current stack frame. But I
don't think that's what's meant in the text quoted.

Rather, in that context, "param" refers to the original parameter, which
is passed as an address stored in a location that is referenced as an
offset relative to the current stack frame. Here, the phrase "address of
param" refers not to the actual address of the local argument relative to
the current stack frame, but rather to the value stored in that location,
the address of the original parameter..

Confused yet? :)

What happens is that when the DLL is called, the OP incorrectly passed the
address of a single byte of storage, rather than an array of bytes of
storage. Furthermore, that single byte was stored on the stack as a local
variable, rather than in the heap. So, when the called function went to
write data to what it thought was an array of bytes, it was actually
writing to that local variable.

But the address of the original parameter passed as "param" was stored in
the stack just past that local variable. Since the local variable didn't
use enough of the stack to accomodate all of the data that the called
function tried to write to it, the address of "param" winds up getting
overwritten as well.

Ironically, the code could have failed in much more spectacular ways. The
OP is fortunate to have had such a clearly incorrect result, but one which
was still testable. :)

Pete

(Bonus reading: of course, the "single byte" variable actually takes up
more room on the stack than just the one byte. I haven't checked, but
probably it's consuming a full 4 bytes, due to alignment requirements.
But that's still not enough for the called function, which apparently was
expecting 7 bytes [why 7? I don't know...that's a weird size for a
buffer]. So three more bytes get written into the "address of param"
storage...just one byte would have been enough to cause the later
assignment to fail, and so it does).
That makes sense. I thought the original problem was somthing else.

v=0
Test(ref v)
// v = 2, as expected

Test2(ref v)
// v = 1. There is no exception thrown

that makes sense.. I thought it was uninitialised ref variable vs
initialised ref. Silly of me. that shouldnt compile..
Jun 27 '08 #15

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

16 posts views Thread by hzmonte | last post: by
3 posts views Thread by Ken Cox [Microsoft MVP] | last post: by
5 posts views Thread by Trevisc | last post: by
reply views Thread by rosydwin | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.