Global C Struct

  • Thread starter Thread starter Erialc Berts
  • Start date Start date
E

Erialc Berts

Forgive me for being a C# newbie.

I have read all of the newsgroup posts, MSDN articles, and other websites on
interop and marshalling to send and receive a C struct as a parameter or
return value in a C function call from C#.

My situation is slightly different, because the C struct I have is global.

I have tried different approaches/techniques (string, String, std::string,
IntPtr, Marshal, class, etc.), but none work. I was given a DLL written in
C. It defines a structure and declares a global instance of it, such as:

typedef struct errInfo
{
long number;
char* description;
} ERRINFOSTRUCT;

PUBLIC ERRINFOSTRUCT ERRINFO = { 0, " " };

It also has two global functions:
__declspec(dllexport) char * GetErrDesc();
__declspec(dllexport) void SetErr(long, char *);

I need to set the members of ERRINFO directly from C# (not using SetErr),
because :

- I am writing a unit test for GetErrDesc
- The two functions are in two different files ("units")
- We must keep our tests isolated (they cannot depend on other tests or
functions)

In all of my failed attempts to set ERRINFO.description, all of my calls to
GetErrDesc returned a space (" "). So, it was obvious that my code did not
actually set ERRINFO. As a test, I called SetErr(1, "Test"), and GetErrDesc
then returned a garbled string, so I know SetErr actually set ERRINFO.

I declared GetErrDesc using the [DllImport("C.dll")] and wish there was a
similar attribute I could use for declaring ERRINFO, but all I could find
was to create my own ERRINFO structure and marshal a copy of it. However, I
do not understand how this concept should even work, since nothing is tying
the managed C# ERRINFO memory to the unmanaged C ERRINFO memory, because there
is no passing of the struct in a function call.

I need to know: 1) how to set the members of C's global ERRINFO from C#; and
2) why I am getting a garbled string returned. My only recourse is to use
C++.NET, and I would rather stick with C#. I am also being forced to use
VS.NET 2002 right now.

Is there someone out there willing to help me with this, please?
(I have spent two 15-hour days on this and am at a stopping point with C#.)
 
Sorry, but what you're looking to do isn't going to happen (from C# anyway).
P/Invoke works only for functions. Data members can't be accessed directly
(you have to provide one or more functions for passing data in or out).

As for the garbled string, you might have to set attribute values on the
P/Invoke definition to specify whether the string is ANSI/ASCII or Unicode.
The marshaller is probably assuming the wrong type of string.

-Rob Teixeira


Erialc Berts said:
Forgive me for being a C# newbie.

I have read all of the newsgroup posts, MSDN articles, and other websites on
interop and marshalling to send and receive a C struct as a parameter or
return value in a C function call from C#.

My situation is slightly different, because the C struct I have is global.

I have tried different approaches/techniques (string, String, std::string,
IntPtr, Marshal, class, etc.), but none work. I was given a DLL written in
C. It defines a structure and declares a global instance of it, such as:

typedef struct errInfo
{
long number;
char* description;
} ERRINFOSTRUCT;

PUBLIC ERRINFOSTRUCT ERRINFO = { 0, " " };

It also has two global functions:
__declspec(dllexport) char * GetErrDesc();
__declspec(dllexport) void SetErr(long, char *);

I need to set the members of ERRINFO directly from C# (not using SetErr),
because :

- I am writing a unit test for GetErrDesc
- The two functions are in two different files ("units")
- We must keep our tests isolated (they cannot depend on other tests or
functions)

In all of my failed attempts to set ERRINFO.description, all of my calls to
GetErrDesc returned a space (" "). So, it was obvious that my code did not
actually set ERRINFO. As a test, I called SetErr(1, "Test"), and GetErrDesc
then returned a garbled string, so I know SetErr actually set ERRINFO.

I declared GetErrDesc using the [DllImport("C.dll")] and wish there was a
similar attribute I could use for declaring ERRINFO, but all I could find
was to create my own ERRINFO structure and marshal a copy of it. However, I
do not understand how this concept should even work, since nothing is tying
the managed C# ERRINFO memory to the unmanaged C ERRINFO memory, because there
is no passing of the struct in a function call.

I need to know: 1) how to set the members of C's global ERRINFO from C#; and
2) why I am getting a garbled string returned. My only recourse is to use
C++.NET, and I would rather stick with C#. I am also being forced to use
VS.NET 2002 right now.

Is there someone out there willing to help me with this, please?
(I have spent two 15-hour days on this and am at a stopping point with
C#.)
 
Erialc,

This is a little tricky. First, let's address the method declarations.
The GetErrDesc function is a problem, because it returns a pointer to a
character string. Now, it could be doing one of two things. It could be
returning a copy of the string in the description field, or it could be
returning a pointer to the description field itself. My assumption is that
it is doing the former (assuming it is coded correctly).

Now, if this is the case, then you won't be able to do anything without
modifying the original C code. Basically, you would have to write a
function that would return the address of the pointer in memory, like so:

ERRINFOSTRUCT * GetErrInfoPointer()

You would import this into C# as this:

[DllImport("C.dll")]
static extern IntPtr GetErrInfoPointer();

And then you would declare your structure and marshal it back and forth
with a call to the static PtrToStructure method on the Marshal class.

You will NOT want to call GetErrDesc in this case because it will be
allocating memory, and you will have to deallocate it. The thing is, if it
is using new or malloc, you will have to create a wrapper for delete or free
and call that from C# to make sure you dispose of the memory from the string
properly.

Now, if it is not really coded correctly and it returns the pointer in
memory of the description field, then you can just declare the function like
this:

[DllImport("C.dll")]
static extern IntPtr GetErrDesc();

And call it once, storing the IntPtr in memory. Then, whenever you want
to get the result, just call the static PtrToStringAnsi to get the string.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)


Erialc Berts said:
Forgive me for being a C# newbie.

I have read all of the newsgroup posts, MSDN articles, and other websites
on
interop and marshalling to send and receive a C struct as a parameter or
return value in a C function call from C#.

My situation is slightly different, because the C struct I have is global.

I have tried different approaches/techniques (string, String, std::string,
IntPtr, Marshal, class, etc.), but none work. I was given a DLL written
in
C. It defines a structure and declares a global instance of it, such as:

typedef struct errInfo
{
long number;
char* description;
} ERRINFOSTRUCT;

PUBLIC ERRINFOSTRUCT ERRINFO = { 0, " " };

It also has two global functions:
__declspec(dllexport) char * GetErrDesc();
__declspec(dllexport) void SetErr(long, char *);

I need to set the members of ERRINFO directly from C# (not using SetErr),
because :

- I am writing a unit test for GetErrDesc
- The two functions are in two different files ("units")
- We must keep our tests isolated (they cannot depend on other tests or
functions)

In all of my failed attempts to set ERRINFO.description, all of my calls
to
GetErrDesc returned a space (" "). So, it was obvious that my code did
not
actually set ERRINFO. As a test, I called SetErr(1, "Test"), and
GetErrDesc
then returned a garbled string, so I know SetErr actually set ERRINFO.

I declared GetErrDesc using the [DllImport("C.dll")] and wish there was a
similar attribute I could use for declaring ERRINFO, but all I could find
was to create my own ERRINFO structure and marshal a copy of it. However,
I
do not understand how this concept should even work, since nothing is
tying
the managed C# ERRINFO memory to the unmanaged C ERRINFO memory, because
there
is no passing of the struct in a function call.

I need to know: 1) how to set the members of C's global ERRINFO from C#;
and
2) why I am getting a garbled string returned. My only recourse is to use
C++.NET, and I would rather stick with C#. I am also being forced to use
VS.NET 2002 right now.

Is there someone out there willing to help me with this, please?
(I have spent two 15-hour days on this and am at a stopping point with
C#.)
 
Thank you Rob and Nicholas for replying.
I'm not a happy camper, but that is because of C#'s shortcomings, not
you guys!
Global C structs are pretty common in legacy DLLs whose source code
has fallen by the wayside.
Ah well, back to C++/C++.NET.
 
Back
Top