0

How can I call the following method from C#, which is in a C++ dll? How Can I recreate the following structure in C#?


Original

Method:

LONG MyMethod (P_HOLO_INFO pInfo, LPVOID pBuffer, LPUSHORT pTracksWritten);

Structure: This method uses the following structure:

typedef struct _HOLO_INFO
{
    LONG     lHoloType;
    LONG     lStatus;
    HANDLE   lThreadHandle;
    LONG     lErrorCode;
    LONG     lDriveIndex;
    LONG     lHeight;
    SHORT    iFormat;
    INT      iStartingTrack;
    LONG     lWrite;
    LONG     lSkip;
    BOOL     bSkipZero;
    BOOL     bInvert;
    LONG     lMaxConsecutiveErrors;
    LONG     lMaxTotalErrors;
    LONG     lMostConsecutiveErrors; 
    LONG     lTotalErrors;
    LPBYTE   pBuffer;
    LPUSHORT pTracksWritten;
    LONG     bUpsideDown;
} HOLO_INFO, *P_HOLO_INFO;

I worked in C# like this

Method:

[DllImport("My.dll", EntryPoint = "_MyMethod@12")]
public unsafe static extern long MyMethod(ref HOLO_INFO pInfo, Byte[] pBuffer,ref ushort pTracksWritten);

Structure:

This method uses the following structure:

unsafe public  struct HOLO_INFO 
{
    public long  lHoloType;
    public long  lStatus;
    public long  lThreadHandle;
    public ulong lErrorCode;
    public long  lDriveIndex;
    public long  lHeight;
    public short iFormat;
    public int   iStartingTrack;
    public long  lWrite;
    public long  lSkip;
    public bool  bSkipZero;
    public bool  bInvert;
    public long  lMaxConsecutiveErrors;
    public long  lMaxTotalErrors;
    public long  lMostConsecutiveErrors; 
    public long  lTotalErrors;
    public Byte* pBuffer;
    public long* pTracksWritten;
    public long  bUpsideDown;
};

I made a call to the method like this:

  do
  {
    result = MyMethod(ref pInfo,ptrBuf,pTracksWritten);
  } while (result ==1 );

Because, it returns 1, if it is Active 0, if it completed successfully 3, if it stopped because of error. if the method is in running state(Active-1). it modifies pInfo and pTracksWritten to update the status information.

aWebdesigner09
  • 257
  • 2
  • 5
  • 17
  • 1
    I had to do something similar a while back. What I did was wrap the dll in a CLRC++ class and get the C++ struct on one side and returned the CLR compatible struct on the other side, but this was a while back, maybe there is a better method now. – Elad Lachmi Apr 03 '11 at 14:55
  • You are clearly on the right track here. If I were you I would write a special test function in C++ to iron out the details. Get it to write out the input to a file. You can then check that what you are sending from C# is correct. For any data pass in the other direction, get the test C# program to log to file also. – David Heffernan Apr 03 '11 at 16:27

3 Answers3

3

Lots of issues:

  • LONG should be declared as int in C#
  • HANDLE is IntPtr.
  • pTracksWritten is missing. You probably need to make it, and pBuffer, an IntPtr and use Marshal.AllocHGlobal to allocate memory for them, depends.
  • You need the CallingConvention in the [DllImport] declaration to use Cdecl.

Odds of getting this to work are not great if you can't debug the unmanaged code. One basic sanity test is to make sure that Marshal.SizeOf() returns the same length as sizeof() in the unmanaged code. Next verify that passed arguments look good when debugging the native code. Next triple-check the pointer usage in the native code and verify that they are not getting copied.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0

See Using a Very C# DLL in C++

You can do a 'simple' trick [this answer](answer Using a Very C# DLL in C++) or you can have a look at fullblown embedding as per my answer

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
0

Give this a shot:

[DllImport("My.dll", EntryPoint = "_MyMethod@12")]
int MyMethod (HOLO_INFO pInfo, IntPtr pBuffer, IntPtr pTracksWritten);

public class HOLO_INFO
{
    public int    lHoloType;
    public int    lStatus;
    public IntPtr lThreadHandle;
    public int    lErrorCode;
    public int    lDriveIndex;
    public int    lHeight;
    public short  iFormat;
    public int    iStartingTrack;
    public int    lWrite;
    public int    lSkip;
    public bool   bSkipZero;
    public bool   bInvert;
    public int    lMaxConsecutiveErrors;
    public int    lMaxTotalErrors;
    public int    lMostConsecutiveErrors; 
    public int    lTotalErrors;
    public IntPtr pBuffer;
    public IntPtr pTracksWritten;
    public int    bUpsideDown;
}

Depending on how they're allocated, you may need to use Marshal.Copy to access HOLO_INFO.pBuffer and Marshal.PtrToStructure to access HOLO_INFO.pTracksWritten (or Marshal.Copy if it's an array vs. a pointer to a singular value).

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • calling convention will be stdcall – David Heffernan Apr 03 '11 at 16:22
  • And you surely mean: `int MyMethod (ref HOLO_INFO Info, IntPtr pBuffer, out UInt16 TracksWritten);` – David Heffernan Apr 03 '11 at 16:25
  • @David Heffernan : No on all counts. `DllImport`'s default calling convention is stdcall, but VC++'s default is cdecl (see http://msdn.microsoft.com/en-us/library/46t77ak2.aspx). Regarding `ref`, `HOLO_INFO` is a `class` so it is already passed by reference; using `ref` there would be akin to `HOLO_INFO**` or `P_HOLO_INFO*` in C++. Regarding `out ushort`, it appears to me that `HOLO_INFO.pTracksWritten` may end up holding that pointer value, which will be invalid after the function returns if an explicit heap allocation isn't used. – ildjarn Apr 03 '11 at 16:29
  • function name with @12 suggests strongly that stdcall is used. Surely you use a struct rather than a class? HOLO_INFO.pTracksWritten is very odd though. This is one of those questions that is impossible to answer fully because so much detail is missing. – David Heffernan Apr 03 '11 at 16:32
  • @David Heffernan : Rereading the other comments now, I see your point regarding the mangled name; I'll edit to omit the `CallingConvention`. – ildjarn Apr 03 '11 at 16:32
  • @David Heffernan : Regarding `struct` vs. `class`, Rico Mariani's long standing recommendation is to not create `struct`s greater than 16 bytes in size. Given that the semantics of passing a `struct` with `ref` vs. passing a `class` without `ref` should be the same, a type of this size seems to me more reasonable as a `class` in CLR-land. – ildjarn Apr 03 '11 at 16:35
  • @ildjarn I didn't know that. What's wrong with struct's >16 bytes? – David Heffernan Apr 03 '11 at 16:38
  • @David Heffernan : Purely a performance recommendation on his part, nothing technically "wrong". – ildjarn Apr 03 '11 at 16:42
  • @ildjarn I reproduced like this. But it is not working. I am editing the Question for further understanding. – aWebdesigner09 Apr 04 '11 at 04:46
  • I am getting garbage values as retrun values into the varible 'result' – aWebdesigner09 Apr 04 '11 at 05:12