• Advertisement
Sign in to follow this  

[.net] importing a DLL, unmanaged code to C#(part 2)

This topic is 3873 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

hi all, i had a previous post about importing some C API functions and got most of it to work and i have a problem here now with a certain function of the API and need some help as to what could be causing the problem. the API i am using is here pay special attention to the definition of SHPObject and SHPReadObject and this is what is in question. as you know since C# doesnt have includes i had to recreate the SHPObject structure myself. i did that here:
[StructLayout(LayoutKind.Sequential)]
    struct SHPObject
    {
	 public int	nSHPType;	
	 public int	nShapeId; 	
	 public int	nParts;		
	 public IntPtr	panPartStart;  
	 public IntPtr	panPartType;    
	 public int	nVertices;
         public IntPtr	padfX;		
	 public IntPtr	padfY;
	 public IntPtr	padfZ;		
	 public IntPtr	padfM;	
	 public double	dfXMin;
         public double	dfYMin;
	 public double	dfZMin;
	 public double	dfMMin;
	 public double	dfXMax;
	 public double	dfYMax;
	 public double	dfZMax;
	 public double	dfMMax;
    };



next is my DLL import code/function definition and implementation
[DllImport("shapelib.dll", CharSet = CharSet.Ansi, EntryPoint = "SHPReadObject", CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr internalSHPReadObject(SHPInfo hSHP, int ishape);
............
............
public static SHPObject SHPReadObject(SHPInfo hSHP, int ishape)
{  
 IntPtr ptr = internalSHPReadObject(hSHP, ishape);
 return (SHPObject)Marshal.PtrToStructure(ptr, typeof(SHPObject));
}
....
....



i have checked the export DEF file to see and make sure SHPReadObject is defined and it is so no problem there. if you notice the SHPReadObject from the link that i gave you you see it is returning a pointer. this may be why i am having the problem. going through the debug process ptr returns a NULL so naturally when i try to return it it crashes with a system NULL reference. There is only one place in the original C API function though where it returns NULL. here is the piece of relevant code from that:
SHPObject SHPAPI_CALL1(*)
SHPReadObject( SHPHandle psSHP, int hEntity )

{
 SHPObject *psShape;

/* -------------------------------------------------------------------- */
/*      Validate the record/entity number.                              */
/* -------------------------------------------------------------------- */
    if( hEntity < 0 || hEntity >= psSHP->nRecords )
        return( NULL );
  .........
  .........
  psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
  psShape->nShapeId = hEntity;
  ........
  ........
 
  return (psShape);


through my debugging hEntity is 0 or greater and the number of records are also greater then hEntity which is just a shape number that i am reading in. SHPAPI_CALL1(*) - i am not sure about this - whether its an old stdcall but it is defined in the C API header file as such:
//#define SHPAPI_CALL __declspec(dllexport) __stdcall       
//#define SHPAPI_CALL1 __declspec(dllexport) * __stdcall

#ifdef SHAPELIB_DLLEXPORT
#  define SHPAPI_CALL __declspec(dllexport)
#  define SHPAPI_CALL1(x)  __declspec(dllexport) x
#endif

#ifndef SHPAPI_CALL
#  define SHPAPI_CALL
#endif

#ifndef SHPAPI_CALL1
#  define SHPAPI_CALL1(x)      x SHPAPI_CALL
#endif



do i need to change my calling convention? is my function defintion appropriate? any help would be appreciated or from anyone that helped me last time. thanks!

Share this post


Link to post
Share on other sites
Advertisement
What is SHPInfo defined as ? because the API suggests this should be a handle.

Share this post


Link to post
Share on other sites
Quote:
Original post by Niksan2
What is SHPInfo defined as ? because the API suggests this should be a handle.


SHPInfo is a handle defined in the API as:


typedef struct
{
FILE *fpSHP;
FILE *fpSHX;
int nShapeType; /* SHPT_* */
int nFileSize; /* SHP file */
int nRecords;
int nMaxRecords;
int *panRecOffset;
int *panRecSize;
double adBoundsMin[4];
double adBoundsMax[4];
int bUpdated;
unsigned char *pabyRec;
int nBufSize;
} SHPInfo;

typedef SHPInfo * SHPHandle;




in my C# code i have to recreate the struct and i did here:

[StructLayout(LayoutKind.Sequential)]
struct SHPInfo
{
public IntPtr fpSHP;
public IntPtr fpSHX;
public int nShapeType; /* SHPT_* */
public int nFileSize; /* SHP file
public int nRecords;
public int nMaxRecords;
public IntPtr panRecOffset;
public IntPtr panRecSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public double[] adBoundsMin;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public double[] adBoundsMax;
public int bUpdated;
string pabyRec;
int nBufSize;
};



i successfully created a handle of this type by overriding the SHPOpen function that is also on the link at the beginning of this. i guess i could give you the whole C# code that i have and you can see for yourself. once i enter the SHPReadObject, in the debugger the handle is passed in successfully and data is in it and its correct so i dont think that this is the problem. here is the whole C# code. please let me know if you have any questions. you can also refer back to my first thread about DLL imports to get some code screenshots. the link to that first thread is here .

Share this post


Link to post
Share on other sites
So you're passing in a structure, when it requires a pointer to a structure ?

Share this post


Link to post
Share on other sites
Quote:
Original post by Niksan2
So you're passing in a structure, when it requires a pointer to a structure ?


sorry, yesterday the site went down(i think?) and i could add my code that i had intended to add in the first place. basically when you open a file you create a handle to that file, which gives you all the attributes that you can access from the file via the members of the struct. you will see in the code below that i did that and then i pass that handle in there. when debugging the data is all there and nothing seems to be corrupted.

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

namespace shapeFileReader
{
[StructLayout(LayoutKind.Sequential)]
struct SHPInfo
{
public IntPtr fpSHP;
public IntPtr fpSHX;
public int nShapeType;
public int nFileSize;
public int nRecords;
public int nMaxRecords;
public IntPtr panRecOffset;
public IntPtr panRecSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public double[] adBoundsMin;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public double[] adBoundsMax;
public int bUpdated;
string pabyRec;
int nBufSize;
};

[StructLayout(LayoutKind.Sequential)]
struct SHPObject
{
public int nSHPType;
public int nShapeId;
public int nParts;
public IntPtr panPartStart;
public IntPtr panPartType;
public int nVertices;
public IntPtr padfX;
public IntPtr padfY;
public IntPtr padfZ;
public IntPtr padfM;
public double dfXMin;
public double dfYMin;
public double dfZMin;
public double dfMMin;
public double dfXMax;
public double dfYMax;
public double dfZMax;
public double dfMMax;
};

[StructLayout(LayoutKind.Sequential)]
struct DBFInfo
{
public IntPtr fp;
public int nRecords;
public int nRecordLength;
public int nHeaderLength;
public int nFields;
public IntPtr panFieldOffset;
public IntPtr panFieldSize;
public IntPtr panFieldDecimals;
public string pachFieldType;
public string pszHeader;
public int nCurrentRecord;
public int bCurrentRecordModified;
public string pszCurrentRecord;
public int bNoHeader;
public int nBufSize;
};

class Class1
{
[DllImport("shapelib.dll", EntryPoint = "SHPOpen")]
private static extern IntPtr internalSHPOpen(string pszLayer, string pszAccess);
[DllImport("shapelib.dll", CharSet = CharSet.Ansi, EntryPoint = "SHPReadObject", CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr internalSHPReadObject(SHPInfo hSHP, int ishape);
[DllImport("shapelib.dll", EntryPoint = "DBFOpen")]
private static extern IntPtr internalDBFOpen(string pszShapeFile, string pszAccess);

public static SHPInfo SHPOpen(string pszLayer, string pszAccess)
{
IntPtr ptr = internalSHPOpen(pszLayer, pszAccess);
return (SHPInfo)Marshal.PtrToStructure(ptr, typeof(SHPInfo));
}

public static SHPObject SHPReadObject(SHPInfo hSHP, int ishape)
{
IntPtr ptr = internalSHPReadObject(hSHP, ishape);
return (SHPObject)Marshal.PtrToStructure(ptr, typeof(SHPObject));
}

public static DBFInfo DBFOpen(string pszShapeFile, string pszAccess)
{
IntPtr ptr = internalDBFOpen(pszShapeFile, pszAccess);
return (DBFInfo)Marshal.PtrToStructure(ptr, typeof(DBFInfo));
}


static void Main(string[] args)
{
SHPInfo mySHPHandle;
DBFInfo myDBFHandle;
SHPObject myObject;


mySHPHandle = SHPOpen("BG2k_clip.shp", "rb");
myDBFHandle = DBFOpen("BG2k_clip.dbf", "rb");
myObject = SHPReadObject(mySHPHandle, 0);

} //main
} //class
}//namespace




i am able to open the files and get the handles correctly and even probe the members of the structs to get values and they are correct.

the 2 API's(SHP and DBF) are here and here respectively. like i said before the Stdcall stuff seems to be different than the other 2 structs that i am redefining, so i was thinking this was the difference/???

Share this post


Link to post
Share on other sites
typedef struct
{
////
} SHPInfo;

typedef SHPInfo * SHPHandle;


The API you originally linked to takes SHPHandle as its first parameter, you pass in SHPInfo, so you're passing through an actual structure rather than a pointer to a structure, that's your problem.

yours
public static SHPObject SHPReadObject(SHPInfo hSHP, int ishape)

theirs

SHPObject *SHPReadObject( SHPHandle hSHP, int iShape );

Share this post


Link to post
Share on other sites
Quote:
Original post by Niksan2
typedef struct
{
////
} SHPInfo;

typedef SHPInfo * SHPHandle;


The API you originally linked to takes SHPHandle as its first parameter, you pass in SHPInfo, so you're passing through an actual structure rather than a pointer to a structure, that's your problem.

yours
public static SHPObject SHPReadObject(SHPInfo hSHP, int ishape)

theirs

SHPObject *SHPReadObject( SHPHandle hSHP, int iShape );


yes i understand that but i had to recreate the struct in C# since C# doesnt have pointers. so how do i recreate my current SHPInfo to match the C API version? or better yet how should i have my internalSHPReadObject defined? i thought IntPtr took care of that regardless of how the struct was created?

my previous thread initially when i was having problems is here to see what others were saying. my problem you'll see though from the beginning is that C# couldnt locate the file outside the bin/Debug directory.

Share this post


Link to post
Share on other sites
Niksan2 is incorrect (sort of). I think he overlooked the internal version of the function that returns a pointer.

You're right, the returned IntPtr contains the pointer to the c struct in memory. Then you convert the data at that location to the c# version. That part should work fine.

But he's also correct, that the first argument has to be passed in as a pointer. This is why it's a good way to store the IntPtr returned by functions before marshaling to a struct.

Might I suggest that SHPReadObject be redefined as follows? I should have described it this way in the original thread, but you were getting inundated with details and didn't want to make things worse. In hind sight that was a mistake. This change should be made to all the functions that return or receive a pointer.


[DllImport("shapelib.dll", CharSet = CharSet.Ansi, EntryPoint = "SHPReadObject", CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr internalSHPReadObject(IntPtr hSHP, int ishape);
............
............
public static IntPtr SHPReadObject(IntPtr hSHP, int ishape, out SHPObject shpObject)
{
IntPtr ptr = internalSHPReadObject(hSHP, ishape);
shpObjcet = (SHPObject)Marshal.PtrToStructure(ptr, typeof(SHPObject));
return ptr;
}





This way you have both the pointer (which you can pass to other functions) and a struct representing the data it points to.

Like I said in the original thread, in c# the pointer and the struct are NOT directly connected in any way. So if you have a function that expects a pointer to data created by a c lib, and pass it a c# struct, it won't be able to do anything with it. The c# struct is simply a representation, or copy, of the data stored at the pointer. However it doesn't point to the actual location in memory used by the c lib.

Hope this makes sense. Interoping code is a royal pain at first, but once you get the fundamentals down it's an extremely powerfull tool.

Share this post


Link to post
Share on other sites
Quote:
Original post by gharen2
Niksan2 is incorrect (sort of). I think he overlooked the internal version of the function that returns a pointer.

You're right, the returned IntPtr contains the pointer to the c struct in memory. Then you convert the data at that location to the c# version. That part should work fine.

But he's also correct, that the first argument has to be passed in as a pointer. This is why it's a good way to store the IntPtr returned by functions before marshaling to a struct.

Might I suggest that SHPReadObject be redefined as follows? I should have described it this way in the original thread, but you were getting inundated with details and didn't want to make things worse. In hind sight that was a mistake. This change should be made to all the functions that return or receive a pointer.

*** Source Snippet Removed ***

This way you have both the pointer (which you can pass to other functions) and a struct representing the data it points to.

Like I said in the original thread, in c# the pointer and the struct are NOT directly connected in any way. So if you have a function that expects a pointer to data created by a c lib, and pass it a c# struct, it won't be able to do anything with it. The c# struct is simply a representation, or copy, of the data stored at the pointer. However it doesn't point to the actual location in memory used by the c lib.

Hope this makes sense. Interoping code is a royal pain at first, but once you get the fundamentals down it's an extremely powerfull tool.



thanks Gharen,
so you are saying for instance -- lets back up to when i actually open a SHP file. this is what i have now. the only question is is how to pass in an out parameter in main? this is my DLL imports now and their function w/main.


[DllImport("shapelib.dll", EntryPoint = "SHPOpen")]
private static extern IntPtr internalSHPOpen(string pszLayer, string pszAccess);

[DllImport("shapelib.dll", CharSet = CharSet.Ansi, EntryPoint = "SHPReadObject", ExactSpelling=true, CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr internalSHPReadObject(IntPtr hSHP, int ishape);

[DllImport("shapelib.dll", EntryPoint = "DBFOpen")]
private static extern IntPtr internalDBFOpen(string pszShapeFile, string pszAccess);

public static IntPtr SHPOpen(string pszLayer, string pszAccess, out SHPInfo mySHPHandle)
{
IntPtr ptr = internalSHPOpen(pszLayer, pszAccess);
mySHPHandle = (SHPInfo)Marshal.PtrToStructure(ptr, typeof(SHPInfo));
return ptr;
}

public static IntPtr DBFOpen(string pszShapeFile, string pszAccess, out DBFInfo myDBFHandle)
{
IntPtr ptr = internalDBFOpen(pszShapeFile, pszAccess);
myDBFHandle = (DBFInfo)Marshal.PtrToStructure(ptr, typeof(DBFInfo));
return ptr;
}

public static IntPtr SHPReadObject(IntPtr mySHPHandlePtr, int ishape, out SHPObject myObjectHandle)
{
IntPtr ptr = internalSHPReadObject(mySHPHandlePtr, ishape);
myObjectHandle = (SHPObject)Marshal.PtrToStructure(ptr, typeof(SHPObject));
return ptr;
}





//then in main

static void Main(string[] args)
{
SHPInfo mySHPHandle;
DBFInfo myDBFHandle;
SHPObject myObject;

IntPtr mySHPHandlePtr;
IntPtr myDBFHandlePtr;
IntPtr myObjectPtr;

mySHPHandlePtr = SHPOpen("BG2k_clip.shp", "rb", mySHPHandle); //compiler error here, how to send in an out type?
myDBFHandlePtr = DBFOpen("BG2k_clip.dbf", "rb", myDBFHandle);
myObjectPtr = SHPReadObject(mySHPHandlePtr, 4, myObject);
}






is this what you are referring to? does this look right to what you were trying to describe to me? i am getting a compiler error regarding sending in an out parameter....thanks again gharen....

//compiler errors:
Quote:
c:\apps\microsoft visual studio .net 2003\vc#\csharpprojects\shapefilereader\class1.cs(142,28): error CS1502: The best overloaded method match for 'shapeFileReader.Class1.SHPOpen(string, string, out shapeFileReader.SHPInfo)' has some invalid arguments
c:\apps\microsoft visual studio .net 2003\vc#\csharpprojects\shapefilereader\class1.cs(142,59): error CS1503: Argument '3': cannot convert from 'shapeFileReader.SHPInfo' to 'out shapeFileReader.SHPInfo'
c:\apps\microsoft visual studio .net 2003\vc#\csharpprojects\shapefilereader\class1.cs(143,28): error CS1502: The best overloaded method match for 'shapeFileReader.Class1.DBFOpen(string, string, out shapeFileReader.DBFInfo)' has some invalid arguments
c:\apps\microsoft visual studio .net 2003\vc#\csharpprojects\shapefilereader\class1.cs(143,59): error CS1503: Argument '3': cannot convert from 'shapeFileReader.DBFInfo' to 'out shapeFileReader.DBFInfo'
c:\apps\microsoft visual studio .net 2003\vc#\csharpprojects\shapefilereader\class1.cs(144,25): error CS1502: The best overloaded method match for 'shapeFileReader.Class1.SHPReadObject(System.IntPtr, int, out shapeFileReader.SHPObject)' has some invalid arguments
c:\apps\microsoft visual studio .net 2003\vc#\csharpprojects\shapefilereader\class1.cs(144,58): error CS1503: Argument '3': cannot convert from 'shapeFileReader.SHPObject' to 'out shapeFileReader.SHPObject'



EDIT - nevermind i wasnt sending in the parameters as out. DOH! it seems to work now -- for now haha. thanks again gharen for your help.

[Edited by - OpenGL_Guru on June 14, 2007 2:42:16 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement