Calling unmanged DLL
-
When you have to call an unmanged API method that requires a structure that contains a fixed length string, how should you express that in C# EG: A method I want to call has this unmanaged structure:
typedef struct foo { char szSymbol[20]; DWORD dwVolume; } FOO;
So in C# I think I would do this:[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Ansi)] struct FOO { public string szSymbol = new String(' ', 20); public UInt32 dwVolume; }
Am I on the right track? Andy -
When you have to call an unmanged API method that requires a structure that contains a fixed length string, how should you express that in C# EG: A method I want to call has this unmanaged structure:
typedef struct foo { char szSymbol[20]; DWORD dwVolume; } FOO;
So in C# I think I would do this:[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Ansi)] struct FOO { public string szSymbol = new String(' ', 20); public UInt32 dwVolume; }
Am I on the right track? AndyCrap - it would appear that you can't initalise structures like that. He who knows and knows that he knows, is wise; follow him_He who knows and knows not that he knows, is asleep;_ wake him_He who knows not, and knows that he knows not, is simple;_ teach him_He whoe knows not and knows not that he knows not, is a fool;_ kick him
-
When you have to call an unmanged API method that requires a structure that contains a fixed length string, how should you express that in C# EG: A method I want to call has this unmanaged structure:
typedef struct foo { char szSymbol[20]; DWORD dwVolume; } FOO;
So in C# I think I would do this:[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Ansi)] struct FOO { public string szSymbol = new String(' ', 20); public UInt32 dwVolume; }
Am I on the right track? AndyUse the
MarshalAs
attribute on the struct's fields to specify how it should convert the string. Untested/uncompiled:[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Ansi)]
struct FOO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=20)]
public string szSymbol;
public uint dwVolume;
}Now if szSymbol is going to be modified by the P/Invoke'd method then you need to use a StringBuilder rather than string to avoid any problems that may occur due to string interning. James "It is self repeating, of unknown pattern" Data - Star Trek: The Next Generation
-
Use the
MarshalAs
attribute on the struct's fields to specify how it should convert the string. Untested/uncompiled:[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Ansi)]
struct FOO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=20)]
public string szSymbol;
public uint dwVolume;
}Now if szSymbol is going to be modified by the P/Invoke'd method then you need to use a StringBuilder rather than string to avoid any problems that may occur due to string interning. James "It is self repeating, of unknown pattern" Data - Star Trek: The Next Generation
Thanks heaps - I had just discovered this attribute myself but was going to use UnmanagedType.LPStr James T. Johnson wrote: Now if szSymbol is going to be modified by the P/Invoke'd method then you need to use a StringBuilder rather than string to avoid any problems that may occur due to string interning Rrrright. Could you elaborate on the StringBuilder part? The szSymbol is going to be modified by the P/Invoke'd method. TIA Andy
-
Thanks heaps - I had just discovered this attribute myself but was going to use UnmanagedType.LPStr James T. Johnson wrote: Now if szSymbol is going to be modified by the P/Invoke'd method then you need to use a StringBuilder rather than string to avoid any problems that may occur due to string interning Rrrright. Could you elaborate on the StringBuilder part? The szSymbol is going to be modified by the P/Invoke'd method. TIA Andy
Andy Davey wrote: I had just discovered this attribute myself but was going to use UnmanagedType.LPStr I'm not that familiar with interop but to me LPStr means a pointer to a string rather than embedding one within a struct. I think this is also how the Interop Marshaller will interpret it. Andy Davey wrote: Could you elaborate on the StringBuilder part? Sure. Strings in .NET are considered immutable; this allows for some optimizations to be made in both memory requirements and when comparing strings. Because they are immutable you can have two strings that contain the same value point to the same block of memory. And rather than compare every single character to see if they are equal you can just compare whether the two strings point to the same object. That is where string-interning comes in, it takes a string and adds it to an internal table which allows you to make use of those optimizations. But there is a down side to strings being immutable. When you want to do lots of string operations (such as concatenation) you create a lot of new string objects using up memory that isn't used after the next operation. That is where the StringBuilder comes in (full name:
System.Text.StringBuilder
) it maintains an internal buffer/array of characters (similar to C style strings) and does various operations on the array rather than creating new string objects. When you are done modifying the StringBuilder you can then call .ToString() on it and get out the string that was built. To link this in with P/Invoke, since you have a buffer that needs a string the proper usage is to supply that buffer with a StringBuilder object rather than a string (which isn't supposed to change). To change the first struct (going from memory):[StructLayout(Layout.Sequential, Pack=4, CharSet=CharSet.Ansi)]
struct FOO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=20)]
public System.Text.StringBuilder szSymbol;
public uint dwVolume;
}Then, because structs can't have default constructors you need to initialize szSymbol to
szSymbol = new System.Text.StringBuilder(20);
To provide a default constructor you can change FOO from a struct to a class (despite its name StructLayout can be applied to classes :)) HTH, James "It is self repeating, of unknown pattern" Data - Star Trek: The Next Generation -
Thanks heaps - I had just discovered this attribute myself but was going to use UnmanagedType.LPStr James T. Johnson wrote: Now if szSymbol is going to be modified by the P/Invoke'd method then you need to use a StringBuilder rather than string to avoid any problems that may occur due to string interning Rrrright. Could you elaborate on the StringBuilder part? The szSymbol is going to be modified by the P/Invoke'd method. TIA Andy