472,962 Members | 2,438 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

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

An interesting/annoying problem involving callbacks and native C and C#

An interesting/annoying problem.

I created a small example to provoke an exception I keep getting.
Basically I have a C-struct (Container) with a function-pointer in it.
I perform repeated calls to the function in the container. I allocate
(and free in c) arrays to get garbage collection in both C and C#.
After a few seconds I get an exception.

I assume GarbageCollector moves the delegate (or collects is) and when
I don't find it in C (since it has moved) I get an exception. In my
real life problem this behaviour is not stopped by adding
GC.KeepAlive(X), but here it seems to be. Can the callback/delegate/
functionpointer still be moved by the garbage collector even if I add
GC.KeepAlive(X)?

I have somewhere seen that I can map a C-struct to a C#-struct but I
have had problems with it so that is not an option for me at this
point.

Can my problems be solved with the keyword fixed? If so how? I tried
fixed (DateCallBack* pdcb = &dcb) {...} but I am told that I cannot
take the address of, get the size of, or declare a pointer to a
managed type.

Any help is appreciated,
Per

//
// Contents of files below
//
// --------------------------------------------

/*
* File: CallBackExample.cs
*
* Compiled with:
* csc CallBackExample.cs
* Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
*
* Purpose:
* Illustrates problems with callback function
* when using it in combination with
* CallBackCaller.c
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace PER9000.CallBackExample
{
class CallBackExample
{
// delegate "class" to match my TimeStamper
delegate void DateCallBack(ref int y, ref int m, ref int d, ref
int h,
ref int min, ref int s, ref int ms);

// sets year, month, day, hour, minute, second and millisecond
public static void TimeStamper(ref int y, ref int m, ref int d,
ref int h, ref int min, ref int s, ref int ms)
{
DateTime dt = DateTime.Now;
y = dt.Year;
m = dt.Month;
d = dt.Day;
h = dt.Hour;
min = dt.Minute;
s = dt.Second;
ms = dt.Millisecond;

double[] tempArr = new double[y * (m + d)];

for (int i = 0; i < tempArr.Length; i++)
tempArr[i] = Math.Sin(((double)y) / (d + m)) +
Math.Sqrt((double)(ms + s + min));
}

[DllImport("CallBackCaller.dll")]
extern static void InitStruct(DateCallBack func, ref IntPtr Ctr);

[DllImport("CallBackCaller.dll")]
extern static void KillStruct(IntPtr Ctr);

[DllImport("CallBackCaller.dll")]
extern static void RunFuncs(IntPtr Ctr, int i, int j);
// testprogram does the tests
unsafe static void Main(string[] args)
{
// say: Hell o World
Console.WriteLine("C# >start");

// Create IntPtr to manage C-struct
IntPtr Ctr = new IntPtr();

// Create callback to put in Container
DateCallBack dcb = new DateCallBack(
PER9000.CallBackExample.CallBackExample.TimeStampe r);

fixed (DateCallBack* pdcb = &dcb)
{
// ...
}

// create container
InitStruct(dcb, ref Ctr);

// Call RunFuncs

int i = 5;
int j = 5;
try
{
i = int.Parse(args[0]);
j = int.Parse(args[1]);
}
catch(Exception)
{
i = 5;
j = 5;
}

Console.WriteLine("C# >calling with (i,j) == ({0},{1})", i,
j);
RunFuncs(Ctr, i, j);

// Kill Container
KillStruct(Ctr);

// keep alive and see the same (?) behaviour
// GC.KeepAlive(dcb);

// say that's it folks
Console.WriteLine("C# >done");

}
}
}

/* CallBackExample.cs EOF */

// --------------------------------------------

/*
* File: CallBackCaller.c
*
* Compiled with:
* cl CallBackCaller.c /LD /Zi
* Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804
for 80x86
*
* Purpose:
* Illustrates movement (?) of callback function
* when using it in combination with
* CallBackExample.cs
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* should match C# delegate DateCallBack */
typedef void (__stdcall * DateCallBack)(int*, int*, int*, int*, int*,
int*, int*);
/* struct with function pointer */
typedef struct ContainerStruct
{
DateCallBack F;
} Container;
/* Creates a struct with a callback */
void __declspec(dllexport) InitStruct(DateCallBack Function,
Container ** Ctr)
{
*Ctr = (Container *) malloc(1 * sizeof(Container));
(*Ctr)->F = Function;
}
/* Frees the struct */
void __declspec(dllexport) KillStruct(Container * Ctr)
{
free(Ctr);
}
/* Evil tester - runs the callback very many times */
void __declspec(dllexport) RunFuncs(Container * Ctr, int i, int j)
{
/* p,q are loop counters */
int p, q;

/* year, month and so on */
int y, m, d, h, min, s, ms;

/* for creation of temporary array */
int l, len;
int * tempArr;
len = i+j+500;

/*
* 1 - call the callback and print result on console
* 2 - allocate memory (int-array)
* 3 - fill array with annoying stuff that
* a - takes time
* b - provoces memory management
* 4 - free memory
*/
for (p = 1; p < i; p++)
{
/* 1 */
Ctr->F(&y, &m, &d, &h, &min, &s, &ms);
printf("C >(p,q) = (%d,%d) of (%d,%d): %d-%d-%d, %d:%d:%d,%d
\n",
p, q, i, j, y, m, d, h, min, s, ms);
fflush(stdout);

/* 2 */
tempArr = (int * ) malloc((len)*sizeof(int));

/* 3 */
for (l = 1; l < len; l++)
for (q = 1; q < j; q++)
tempArr[l] = (q+(l*l/l)/l+123)-(321+(l*l/l)/l)*l/l;

/* 4 */
free(tempArr);
}
}

/* CallBackCaller.c EOF */

Feb 12 '07 #1
6 3203
"per9000" <pe*****@gmail.comwrote in message
news:11*********************@l53g2000cwa.googlegro ups.com...
An interesting/annoying problem.

I created a small example to provoke an exception I keep getting.
Basically I have a C-struct (Container) with a function-pointer in it.
I perform repeated calls to the function in the container. I allocate
(and free in c) arrays to get garbage collection in both C and C#.
After a few seconds I get an exception.

I assume GarbageCollector moves the delegate (or collects is) and when
I don't find it in C (since it has moved) I get an exception. In my
real life problem this behaviour is not stopped by adding
GC.KeepAlive(X), but here it seems to be. Can the callback/delegate/
functionpointer still be moved by the garbage collector even if I add
GC.KeepAlive(X)?

I have somewhere seen that I can map a C-struct to a C#-struct but I
have had problems with it so that is not an option for me at this
point.

Can my problems be solved with the keyword fixed? If so how? I tried
fixed (DateCallBack* pdcb = &dcb) {...} but I am told that I cannot
take the address of, get the size of, or declare a pointer to a
managed type.

Any help is appreciated,
Per

//
// Contents of files below
//
// --------------------------------------------

/*
* File: CallBackExample.cs
*
* Compiled with:
* csc CallBackExample.cs
* Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
*
* Purpose:
* Illustrates problems with callback function
* when using it in combination with
* CallBackCaller.c
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace PER9000.CallBackExample
{
class CallBackExample
{
// delegate "class" to match my TimeStamper
delegate void DateCallBack(ref int y, ref int m, ref int d, ref
int h,
ref int min, ref int s, ref int ms);

// sets year, month, day, hour, minute, second and millisecond
public static void TimeStamper(ref int y, ref int m, ref int d,
ref int h, ref int min, ref int s, ref int ms)
{
DateTime dt = DateTime.Now;
y = dt.Year;
m = dt.Month;
d = dt.Day;
h = dt.Hour;
min = dt.Minute;
s = dt.Second;
ms = dt.Millisecond;

double[] tempArr = new double[y * (m + d)];

for (int i = 0; i < tempArr.Length; i++)
tempArr[i] = Math.Sin(((double)y) / (d + m)) +
Math.Sqrt((double)(ms + s + min));
}

[DllImport("CallBackCaller.dll")]
extern static void InitStruct(DateCallBack func, ref IntPtr Ctr);

[DllImport("CallBackCaller.dll")]
extern static void KillStruct(IntPtr Ctr);

[DllImport("CallBackCaller.dll")]
extern static void RunFuncs(IntPtr Ctr, int i, int j);
// testprogram does the tests
unsafe static void Main(string[] args)
{
// say: Hell o World
Console.WriteLine("C# >start");

// Create IntPtr to manage C-struct
IntPtr Ctr = new IntPtr();

// Create callback to put in Container
DateCallBack dcb = new DateCallBack(
PER9000.CallBackExample.CallBackExample.TimeStampe r);

fixed (DateCallBack* pdcb = &dcb)
{
// ...
}

// create container
InitStruct(dcb, ref Ctr);

// Call RunFuncs

int i = 5;
int j = 5;
try
{
i = int.Parse(args[0]);
j = int.Parse(args[1]);
}
catch(Exception)
{
i = 5;
j = 5;
}

Console.WriteLine("C# >calling with (i,j) == ({0},{1})", i,
j);
RunFuncs(Ctr, i, j);

// Kill Container
KillStruct(Ctr);

// keep alive and see the same (?) behaviour
// GC.KeepAlive(dcb);

// say that's it folks
Console.WriteLine("C# >done");

}
}
}

/* CallBackExample.cs EOF */

// --------------------------------------------

/*
* File: CallBackCaller.c
*
* Compiled with:
* cl CallBackCaller.c /LD /Zi
* Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804
for 80x86
*
* Purpose:
* Illustrates movement (?) of callback function
* when using it in combination with
* CallBackExample.cs
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* should match C# delegate DateCallBack */
typedef void (__stdcall * DateCallBack)(int*, int*, int*, int*, int*,
int*, int*);
/* struct with function pointer */
typedef struct ContainerStruct
{
DateCallBack F;
} Container;
/* Creates a struct with a callback */
void __declspec(dllexport) InitStruct(DateCallBack Function,
Container ** Ctr)
{
*Ctr = (Container *) malloc(1 * sizeof(Container));
(*Ctr)->F = Function;
}
/* Frees the struct */
void __declspec(dllexport) KillStruct(Container * Ctr)
{
free(Ctr);
}
/* Evil tester - runs the callback very many times */
void __declspec(dllexport) RunFuncs(Container * Ctr, int i, int j)
{
/* p,q are loop counters */
int p, q;

/* year, month and so on */
int y, m, d, h, min, s, ms;

/* for creation of temporary array */
int l, len;
int * tempArr;
len = i+j+500;

/*
* 1 - call the callback and print result on console
* 2 - allocate memory (int-array)
* 3 - fill array with annoying stuff that
* a - takes time
* b - provoces memory management
* 4 - free memory
*/
for (p = 1; p < i; p++)
{
/* 1 */
Ctr->F(&y, &m, &d, &h, &min, &s, &ms);
printf("C >(p,q) = (%d,%d) of (%d,%d): %d-%d-%d, %d:%d:%d,%d
\n",
p, q, i, j, y, m, d, h, min, s, ms);
fflush(stdout);

/* 2 */
tempArr = (int * ) malloc((len)*sizeof(int));

/* 3 */
for (l = 1; l < len; l++)
for (q = 1; q < j; q++)
tempArr[l] = (q+(l*l/l)/l+123)-(321+(l*l/l)/l)*l/l;

/* 4 */
free(tempArr);
}
}

/* CallBackCaller.c EOF */

Above code (C#) is missing a GC.KeepAlive(dcb), but seems like you know that.
The problem is not a "moving" delegate instance, in above case it's because of a pre-mature
collection of the delegate. The CLR is unaware of what the unmanaged code will do with the
delegate, the JIT reports the delegate as garbage after the call to "InitStruct", the next
run of the GC will collect the instance and at the next callback the interop layer will call
into invalid memory ....

Other than this, your C function signatures should all use the stdcall calling convention
like:
__declspec(dllexport) void __stdcall RunFuncs(Container * Ctr, int i, int j)

Don't know what exactly is you real-life problem though...

Willy.

Feb 12 '07 #2
Hi Willy,

thanks for the hint on using stdcall, but it seems I have added that
it to the real-life problem.

My real life problem is pretty much a mix of .NET C# and MC++; and
native C and Fortran. I have methods written in C# that are stored in
structs (delegates to them anyway) in C (from objects in C#). C and
Fortran talks a lot and runs the methods from time to time given lots
of conditions.

I've had two major problems. The first one is the one you mention,
that is fixed by GC.KeepAlive(..), the typical exception was Unhandled
Exception: System.AccessViolationException.

The other one, that I suspected was caused by a moved delegate, is
that my program just shuts down - no exception or anything. A major
problem for me here is if this is caused by some old exit condition in
some obscure Fortran code that no one has touched for years or is it
caused by the delegate that has moved. My idea was the following: IF
the delegate has moved THEN any call to it (to some random memory I
guess) could do about anything - for example shut down. Also this
behavior only occurred after several seconds (almost a minute) or
loops that really torture my native code so I suspected the garbage
man.

In a blog at http://blogs.msdn.com/sebby1234/arch...05/565090.aspx
I found (under Pinned object): "The garbage collector has the
possibility of physically moving the objects for which it is
responsible..." This is pretty much why I started suspecting the
garbage man.

So, my problem distills to a number of questions:
- When, why and how is the garbage man moving objects?
- Is pinning the answer to my prayers? Why/Why not?
- Can I "pin" managed objects? How/Why not?

Of course, any other help is also useful,

thanks,
Per
----------------------

Ever wanted a list of logical drives? Check out
Per Erik Strandberg's DRIVES:
http://www.pererikstrandberg.se/projects/drives/

On Feb 12, 7:10 pm, "Willy Denoyette [MVP]"
<willy.denoye...@telenet.bewrote:
"per9000" <per9...@gmail.comwrote in message

news:11*********************@l53g2000cwa.googlegro ups.com...
An interesting/annoying problem.
I created a small example to provoke an exception I keep getting.
Basically I have a C-struct (Container) with a function-pointer in it.
I perform repeated calls to the function in the container. I allocate
(and free in c) arrays to get garbage collection in both C and C#.
After a few seconds I get an exception.
I assume GarbageCollector moves the delegate (or collects is) and when
I don't find it in C (since it has moved) I get an exception. In my
real life problem this behaviour is not stopped by adding
GC.KeepAlive(X), but here it seems to be. Can the callback/delegate/
functionpointer still be moved by the garbage collector even if I add
GC.KeepAlive(X)?
I have somewhere seen that I can map a C-struct to a C#-struct but I
have had problems with it so that is not an option for me at this
point.
Can my problems be solved with the keyword fixed? If so how? I tried
fixed (DateCallBack* pdcb = &dcb) {...} but I am told that I cannot
take the address of, get the size of, or declare a pointer to a
managed type.
Any help is appreciated,
Per
//
// Contents of files below
//
// --------------------------------------------
/*
* File: CallBackExample.cs
*
* Compiled with:
* csc CallBackExample.cs
* Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
*
* Purpose:
* Illustrates problems with callback function
* when using it in combination with
* CallBackCaller.c
*
* Created 20070212
* by Per Erik Strandberg,www.pererikstrandberg.se
* for use in newsgroups
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace PER9000.CallBackExample
{
class CallBackExample
{
// delegate "class" to match my TimeStamper
delegate void DateCallBack(ref int y, ref int m, ref int d, ref
int h,
ref int min, ref int s, ref int ms);
// sets year, month, day, hour, minute, second and millisecond
public static void TimeStamper(ref int y, ref int m, ref int d,
ref int h, ref int min, ref int s, ref int ms)
{
DateTime dt = DateTime.Now;
y = dt.Year;
m = dt.Month;
d = dt.Day;
h = dt.Hour;
min = dt.Minute;
s = dt.Second;
ms = dt.Millisecond;
double[] tempArr = new double[y * (m + d)];
for (int i = 0; i < tempArr.Length; i++)
tempArr[i] = Math.Sin(((double)y) / (d + m)) +
Math.Sqrt((double)(ms + s + min));
}
[DllImport("CallBackCaller.dll")]
extern static void InitStruct(DateCallBack func, ref IntPtr Ctr);
[DllImport("CallBackCaller.dll")]
extern static void KillStruct(IntPtr Ctr);
[DllImport("CallBackCaller.dll")]
extern static void RunFuncs(IntPtr Ctr, int i, int j);
// testprogram does the tests
unsafe static void Main(string[] args)
{
// say: Hell o World
Console.WriteLine("C# >start");
// Create IntPtr to manage C-struct
IntPtr Ctr = new IntPtr();
// Create callback to put in Container
DateCallBack dcb = new DateCallBack(
PER9000.CallBackExample.CallBackExample.TimeStampe r);
fixed (DateCallBack* pdcb = &dcb)
{
// ...
}
// create container
InitStruct(dcb, ref Ctr);
// Call RunFuncs
int i = 5;
int j = 5;
try
{
i = int.Parse(args[0]);
j = int.Parse(args[1]);
}
catch(Exception)
{
i = 5;
j = 5;
}
Console.WriteLine("C# >calling with (i,j) == ({0},{1})", i,
j);
RunFuncs(Ctr, i, j);
// Kill Container
KillStruct(Ctr);
// keep alive and see the same (?) behaviour
// GC.KeepAlive(dcb);
// say that's it folks
Console.WriteLine("C# >done");
}
}
}
/* CallBackExample.cs EOF */
// --------------------------------------------
/*
* File: CallBackCaller.c
*
* Compiled with:
* cl CallBackCaller.c /LD /Zi
* Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804
for 80x86
*
* Purpose:
* Illustrates movement (?) of callback function
* when using it in combination with
* CallBackExample.cs
*
* Created 20070212
* by Per Erik Strandberg,www.pererikstrandberg.se
* for use in newsgroups
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* should match C# delegate DateCallBack */
typedef void (__stdcall * DateCallBack)(int*, int*, int*, int*, int*,
int*, int*);
/* struct with function pointer */
typedef struct ContainerStruct
{
DateCallBack F;
} Container;
/* Creates a struct with a callback */
void __declspec(dllexport) InitStruct(DateCallBack Function,
Container ** Ctr)
{
*Ctr = (Container *) malloc(1 * sizeof(Container));
(*Ctr)->F = Function;
}
/* Frees the struct */
void __declspec(dllexport) KillStruct(Container * Ctr)
{
free(Ctr);
}
/* Evil tester - runs the callback very many times */
void __declspec(dllexport) RunFuncs(Container * Ctr, int i, int j)
{
/* p,q are loop counters */
int p, q;
/* year, month and so on */
int y, m, d, h, min, s, ms;
/* for creation of temporary array */
int l, len;
int * tempArr;
len = i+j+500;
/*
* 1 - call the callback and print result on console
* 2 - allocate memory (int-array)
* 3 - fill array with annoying stuff that
* a - takes time
* b - provoces memory management
* 4 - free memory
*/
for (p = 1; p < i; p++)
{
/* 1 */
Ctr->F(&y, &m, &d, &h, &min, &s, &ms);
printf("C >(p,q) = (%d,%d) of (%d,%d): %d-%d-%d, %d:%d:%d,%d
\n",
p, q, i, j, y, m, d, h, min, s, ms);
fflush(stdout);
/* 2 */
tempArr = (int * ) malloc((len)*sizeof(int));
/* 3 */
for (l = 1; l < len; l++)
for (q = 1; q < j; q++)
tempArr[l] = (q+(l*l/l)/l+123)-(321+(l*l/l)/l)*l/l;
/* 4 */
free(tempArr);
}
}
/* CallBackCaller.c EOF */

Above code (C#) is missing a GC.KeepAlive(dcb), but seems like you know that.
The problem is not a "moving" delegate instance, in above case it's because of a pre-mature
collection of the delegate. The CLR is unaware of what the unmanaged code will do with the
delegate, the JIT reports the delegate as garbage after the call to "InitStruct", the next
run of the GC will collect the instance and at the next callback the interop layer will call
into invalid memory ....

Other than this, your C function signatures should all use the stdcall calling convention
like:
__declspec(dllexport) void __stdcall RunFuncs(Container * Ctr, int i, int j)

Don't know what exactly is you real-life problem though...

Willy.

Feb 13 '07 #3
"per9000" <pe*****@gmail.comwrote in message
news:11**********************@l53g2000cwa.googlegr oups.com...
Hi Willy,

thanks for the hint on using stdcall, but it seems I have added that
it to the real-life problem.

My real life problem is pretty much a mix of .NET C# and MC++; and
native C and Fortran. I have methods written in C# that are stored in
structs (delegates to them anyway) in C (from objects in C#). C and
Fortran talks a lot and runs the methods from time to time given lots
of conditions.

I've had two major problems. The first one is the one you mention,
that is fixed by GC.KeepAlive(..), the typical exception was Unhandled
Exception: System.AccessViolationException.

The other one, that I suspected was caused by a moved delegate, is
that my program just shuts down - no exception or anything. A major
problem for me here is if this is caused by some old exit condition in
some obscure Fortran code that no one has touched for years or is it
caused by the delegate that has moved. My idea was the following: IF
the delegate has moved THEN any call to it (to some random memory I
guess) could do about anything - for example shut down. Also this
behavior only occurred after several seconds (almost a minute) or
loops that really torture my native code so I suspected the garbage
man.

In a blog at http://blogs.msdn.com/sebby1234/arch...05/565090.aspx
I found (under Pinned object): "The garbage collector has the
possibility of physically moving the objects for which it is
responsible..." This is pretty much why I started suspecting the
garbage man.

So, my problem distills to a number of questions:
- When, why and how is the garbage man moving objects?
- Is pinning the answer to my prayers? Why/Why not?
- Can I "pin" managed objects? How/Why not?

Of course, any other help is also useful,

thanks,
Per

Moving delegates can never be a problem, so there is no need to pin, all you need to care
about is the life-time of your delegate(s), otherwise said, you need to make sure the
delegate(s) doesn't get GC'd. Make also sure that the "containers" in which you store the
delegate references don't get collected, this would also cause the delegates to get
collected (assumed there are no other references to the delegates).
Note also that what's passed to unmanaged code is a "function pointer", the function it
points to never moves (and doesn't go away with the delegate), code belongs to the 'type',
it's not part of the object!.

Willy.
Feb 13 '07 #4
This is interesting to me:
Note also that what's passed to unmanaged code is a "function pointer", the function it
points to never moves (and doesn't go away with the delegate), code belongs to the 'type',
it's not part of the object!.
Thanks for making that clear, it helps. I will accept my fate and try
to add GC.KeepAlive(...) and see if it helps.

But I still do not really understand when it is a good idea to use
"fixed"/"pinning" (and please don't say never even if it true :-D ).
Is it just a way to use C-style pointer arithmetic in C#?

/Per

Feb 13 '07 #5
What exception do you get?
Have any stack trace when it happens?

"per9000" <pe*****@gmail.comha scritto nel messaggio
news:11*********************@l53g2000cwa.googlegro ups.com...
An interesting/annoying problem.

I created a small example to provoke an exception I keep getting.
Basically I have a C-struct (Container) with a function-pointer in it.
I perform repeated calls to the function in the container. I allocate
(and free in c) arrays to get garbage collection in both C and C#.
After a few seconds I get an exception.

I assume GarbageCollector moves the delegate (or collects is) and when
I don't find it in C (since it has moved) I get an exception. In my
real life problem this behaviour is not stopped by adding
GC.KeepAlive(X), but here it seems to be. Can the callback/delegate/
functionpointer still be moved by the garbage collector even if I add
GC.KeepAlive(X)?

I have somewhere seen that I can map a C-struct to a C#-struct but I
have had problems with it so that is not an option for me at this
point.

Can my problems be solved with the keyword fixed? If so how? I tried
fixed (DateCallBack* pdcb = &dcb) {...} but I am told that I cannot
take the address of, get the size of, or declare a pointer to a
managed type.

Any help is appreciated,
Per

//
// Contents of files below
//
// --------------------------------------------

/*
* File: CallBackExample.cs
*
* Compiled with:
* csc CallBackExample.cs
* Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
*
* Purpose:
* Illustrates problems with callback function
* when using it in combination with
* CallBackCaller.c
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace PER9000.CallBackExample
{
class CallBackExample
{
// delegate "class" to match my TimeStamper
delegate void DateCallBack(ref int y, ref int m, ref int d, ref
int h,
ref int min, ref int s, ref int ms);

// sets year, month, day, hour, minute, second and millisecond
public static void TimeStamper(ref int y, ref int m, ref int d,
ref int h, ref int min, ref int s, ref int ms)
{
DateTime dt = DateTime.Now;
y = dt.Year;
m = dt.Month;
d = dt.Day;
h = dt.Hour;
min = dt.Minute;
s = dt.Second;
ms = dt.Millisecond;

double[] tempArr = new double[y * (m + d)];

for (int i = 0; i < tempArr.Length; i++)
tempArr[i] = Math.Sin(((double)y) / (d + m)) +
Math.Sqrt((double)(ms + s + min));
}

[DllImport("CallBackCaller.dll")]
extern static void InitStruct(DateCallBack func, ref IntPtr Ctr);

[DllImport("CallBackCaller.dll")]
extern static void KillStruct(IntPtr Ctr);

[DllImport("CallBackCaller.dll")]
extern static void RunFuncs(IntPtr Ctr, int i, int j);
// testprogram does the tests
unsafe static void Main(string[] args)
{
// say: Hell o World
Console.WriteLine("C# >start");

// Create IntPtr to manage C-struct
IntPtr Ctr = new IntPtr();

// Create callback to put in Container
DateCallBack dcb = new DateCallBack(
PER9000.CallBackExample.CallBackExample.TimeStampe r);

fixed (DateCallBack* pdcb = &dcb)
{
// ...
}

// create container
InitStruct(dcb, ref Ctr);

// Call RunFuncs

int i = 5;
int j = 5;
try
{
i = int.Parse(args[0]);
j = int.Parse(args[1]);
}
catch(Exception)
{
i = 5;
j = 5;
}

Console.WriteLine("C# >calling with (i,j) == ({0},{1})", i,
j);
RunFuncs(Ctr, i, j);

// Kill Container
KillStruct(Ctr);

// keep alive and see the same (?) behaviour
// GC.KeepAlive(dcb);

// say that's it folks
Console.WriteLine("C# >done");

}
}
}

/* CallBackExample.cs EOF */

// --------------------------------------------

/*
* File: CallBackCaller.c
*
* Compiled with:
* cl CallBackCaller.c /LD /Zi
* Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804
for 80x86
*
* Purpose:
* Illustrates movement (?) of callback function
* when using it in combination with
* CallBackExample.cs
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* should match C# delegate DateCallBack */
typedef void (__stdcall * DateCallBack)(int*, int*, int*, int*, int*,
int*, int*);
/* struct with function pointer */
typedef struct ContainerStruct
{
DateCallBack F;
} Container;
/* Creates a struct with a callback */
void __declspec(dllexport) InitStruct(DateCallBack Function,
Container ** Ctr)
{
*Ctr = (Container *) malloc(1 * sizeof(Container));
(*Ctr)->F = Function;
}
/* Frees the struct */
void __declspec(dllexport) KillStruct(Container * Ctr)
{
free(Ctr);
}
/* Evil tester - runs the callback very many times */
void __declspec(dllexport) RunFuncs(Container * Ctr, int i, int j)
{
/* p,q are loop counters */
int p, q;

/* year, month and so on */
int y, m, d, h, min, s, ms;

/* for creation of temporary array */
int l, len;
int * tempArr;
len = i+j+500;

/*
* 1 - call the callback and print result on console
* 2 - allocate memory (int-array)
* 3 - fill array with annoying stuff that
* a - takes time
* b - provoces memory management
* 4 - free memory
*/
for (p = 1; p < i; p++)
{
/* 1 */
Ctr->F(&y, &m, &d, &h, &min, &s, &ms);
printf("C >(p,q) = (%d,%d) of (%d,%d): %d-%d-%d, %d:%d:%d,%d
\n",
p, q, i, j, y, m, d, h, min, s, ms);
fflush(stdout);

/* 2 */
tempArr = (int * ) malloc((len)*sizeof(int));

/* 3 */
for (l = 1; l < len; l++)
for (q = 1; q < j; q++)
tempArr[l] = (q+(l*l/l)/l+123)-(321+(l*l/l)/l)*l/l;

/* 4 */
free(tempArr);
}
}

/* CallBackCaller.c EOF */

Feb 13 '07 #6
"per9000" <pe*****@gmail.comwrote in message
news:11*********************@j27g2000cwj.googlegro ups.com...
This is interesting to me:
>Note also that what's passed to unmanaged code is a "function pointer", the function it
points to never moves (and doesn't go away with the delegate), code belongs to the
'type',
it's not part of the object!.

Thanks for making that clear, it helps. I will accept my fate and try
to add GC.KeepAlive(...) and see if it helps.

But I still do not really understand when it is a good idea to use
"fixed"/"pinning" (and please don't say never even if it true :-D ).
Is it just a way to use C-style pointer arithmetic in C#?

/Per

When using PInvoke to call into unmanaged code there are very few case in which you need to
pin, the interop layer will pin all objects passed as argument in the call, for the
*duration* of the call.
The latter is important, for instance, say you pass an array to unmanaged, and unmanaged
keeps a pointer to the array to be used (say in another thread or in another call from
managed) after the call returned to managed code, then you MUST pin the array. Note that
this can be tricky, because managed has no idea for how long it should pin the array.
So, please search your unmanaged code to make sure you aren't using/referring to objects
passed to unmanaged after the call returned, if that's the case you need to pin this object
using GCHandle.

Willy.
Feb 13 '07 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Melissa Wallis | last post by:
I have a class with 5 callbacks. Two of the callbacks work fine but the others don't. The main difference is that the callbacks that don't work are composed of a sequence of structs. I noticed a...
15
by: Nick Coghlan | last post by:
Thought some folks here might find this one interesting. No great revelations, just a fairly sensible piece on writing readable code :) The whole article:...
1
by: vijaya | last post by:
I've to invoke a unmanaged dll fucntion in C# which uses a callback fucntion.The unmanaged dll fucntion returns a pointer to a structure to its callback fucntion.The user should collect those...
0
by: ck388 | last post by:
For some reason when I enable the callback feature of the gridview I still get a page refresh, that is it seems like there is a postback that occurs, not a callback which is just supposed to update...
5
by: Christopher Jastram | last post by:
I'm a self-taught programmer, so this might be a pretty dumb question. If it is, please point me in the right direction and I shall apologize profusely. I have a question regarding C++ and...
4
by: R. MacDonald | last post by:
Hello, all, I have a .NET application (VB) that passes the address of a delegate to unmanaged code in a DLL. The unmanaged code then uses the delegate as a call-back. This seems to work...
0
by: hynek.cihlar | last post by:
A strange behaviour thatI found in ASP.NET 2.0. I am trying to issue a callback request (handled by ICallbackEventHandler and RaiseCallbackEvent) and a regular GET request in the client browser...
27
by: Frederick Gotham | last post by:
I thought it might be interesting to share experiences of tracking down a subtle or mysterious bug. I myself haven't much experience with tracking down bugs, but there's one in particular which...
8
by: DavidT | last post by:
Hello, at first, exuse if the following question is simple to solve, but i normaly coding with C# and now have to use C++/CLI for one project. My Problem is that i have to use a native c++ sdk...
0
by: lllomh | last post by:
Define the method first this.state = { buttonBackgroundColor: 'green', isBlinking: false, // A new status is added to identify whether the button is blinking or not } autoStart=()=>{
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 4 Oct 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
0
tracyyun
by: tracyyun | last post by:
Hello everyone, I have a question and would like some advice on network connectivity. I have one computer connected to my router via WiFi, but I have two other computers that I want to be able to...
4
NeoPa
by: NeoPa | last post by:
Hello everyone. I find myself stuck trying to find the VBA way to get Access to create a PDF of the currently-selected (and open) object (Form or Report). I know it can be done by selecting :...
1
by: Teri B | last post by:
Hi, I have created a sub-form Roles. In my course form the user selects the roles assigned to the course. 0ne-to-many. One course many roles. Then I created a report based on the Course form and...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 1 Nov 2023 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM) Please note that the UK and Europe revert to winter time on...
0
NeoPa
by: NeoPa | last post by:
Introduction For this article I'll be focusing on the Report (clsReport) class. This simply handles making the calling Form invisible until all of the Reports opened by it have been closed, when it...
2
by: GKJR | last post by:
Does anyone have a recommendation to build a standalone application to replace an Access database? I have my bookkeeping software I developed in Access that I would like to make available to other...

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.