[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.dotnet.framework.interop

HELP! C# calling C++ dll calling back to C# with struct data

Joel

3/20/2007 7:01:00 PM


Hello.

I am writing a C# application to generate a PowerBuilder PBL library.
This is done by making calls to "ORCA", a vendor-provided pborc100.dll
(which was written in C or C++.)

I have successfully called pborc100 to open and close a session, and
create an empty library. Now I want to make a call to open an
existing library and list its contents, i.e. I want to list a Library
Directory.

This feature is implemented by passing several parameters to the dll's
function "PBORCA_LibraryDirectory". One of these parameters is a
pointer to a callback function. I have successfully coerced the dll
into calling my C# callback function one time for each of the objects
in the PBL.

The callback function takes two parameters: a pointer to a structure
and a pointer to a buffer. The struct pointer I'm interested in now;
the buffer pointer doesn't concern me at this point.

You can see in my code below (Program.cs) where I've entered the line:
Console.WriteLine("breakpoint");

If I insert a breakpoint on this line and run the application, I can
browse values for pDirEntry. This tells me that my callback is
running and that I'm getting data. The first parameter (szComments)
in the structure pDirEntry are showing up correctly. The rest of the
values are garbage. If I let the program continue it prints
"breakpoint" to the console window and then gives me a runtime error:

The runtime has encountered a fatal error. [snip] This error may be a
bug in the CLR or in the unsafe or non-verifiable portions of user
code. Common sources of this bug include user marshaling errors for
COM-interop or PInvoke, which may corrupt the stack.

If I change my callback routine to accept two IntPtrs like this:

public delegate void PBORCA_LIBDIRCALLBACK(
IntPtr pDirEntry,
IntPtr lpUserData);

(and change my other declarations to agree with these types) then the
application will run but I can't get to the data through calls like
Marshal.PtrToStructure().

I can only guess that's it's due to the way I'm marshaling those
callback parameters. Any guidance you can provide is appreciated!

Thanks,
Joel


================================ Program.cs
================================

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices; // DllImport
using System.Diagnostics;

namespace ConsoleApplication5
{
public class orcaUtils
{

public static int PBORCA_MAXCOMMENT = 255;

[DllImport("pborc100.dll", EntryPoint = "PBORCA_SessionOpen",
CharSet = CharSet.Unicode, SetLastError = true)]
public unsafe static extern int PBORCA_SessionOpen();

[DllImport("pborc100.dll", EntryPoint = "PBORCA_SessionClose",
CharSet = CharSet.Unicode, SetLastError = true)]
public unsafe static extern void PBORCA_SessionClose(int
hORCASession);

[StructLayout(LayoutKind.Sequential)]
public struct PBORCA_DIRENTRY
{
[MarshalAs(UnmanagedType.LPTStr)]
public string szComments;
public int lCreateTime; /* Time of entry create/
mod */
public int lEntrySize; /* Size of entry */
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszEntryName; /* Pointer to entry
name */
public int otEntryType; /* Entry type */
}

public delegate void PBORCA_LIBDIRCALLBACK(
PBORCA_DIRENTRY pDirEntry,
IntPtr lpUserData);

[DllImport("pborc100.dll", EntryPoint =
"PBORCA_LibraryDirectory",
CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int PBORCA_LibraryDirectory(int
hORCASession,
[MarshalAs(UnmanagedType.LPTStr)] string lpszLibName,
[MarshalAs(UnmanagedType.LPTStr)] string lpszLibComments,
int iCmntsBufflen,
PBORCA_LIBDIRCALLBACK pListProc,
IntPtr pUserData
);
}

class Program
{

public static void
PBORCA_LibDirCallback(orcaUtils.PBORCA_DIRENTRY pDirEntry, IntPtr
lpUserData)
{
Console.WriteLine("breakpoint");
}

static void Main(string[] args)
{

int iORCASession = orcaUtils.PBORCA_SessionOpen();

orcaUtils.PBORCA_LIBDIRCALLBACK
PBORCA_LibraryDirectoryCallback = new
orcaUtils.PBORCA_LIBDIRCALLBACK(PBORCA_LibDirCallback);
string myName = "C:\\my.pbl";
string myComment = "";
IntPtr myDummy = new IntPtr();
iRetVal = orcaUtils.PBORCA_LibraryDirectory(iORCASession,
myName, myComment, 256, PBORCA_LibraryDirectoryCallback, myDummy);

orcaUtils.PBORCA_SessionClose(iORCASession);
}
}

}


================================ pborc100.dll
================================

INT PBORCA_LibraryDirectory ( HPBORCA hORCASession,
LPTSTR lpszLibName,
LPTSTR lpszLibComments,
INT iCmntsBuffLen,
PBORCA_LISTPROC pListProc,
LPVOID pUserData );
Argument Description:
- hORCASession Handle to previously established ORCA session.
- lpszLibName Pointer to a string whose value is the file name of the
library for which you want directory information.
- lpszLibComments Pointer to a buffer in which ORCA will put comments
stored with the library.
- iCmntsBuffLen Length of the buffer (specified in TCHARs) pointed to
by lpszLibComments. The recommended length is PBORCA_MAXCOMMENTS + 1.
- pListProc Pointer to the PBORCA_LibraryDirectory callback function.
The callback function is called for each entry in the library.
- The information ORCA passes to the callback function is entry name,
comments, size of entry, and modification time, stored in a structure
of type PBORCA_DIRENTRY.
- pUserData Pointer to user data to be passed to the
PBORCA_LibraryDirectory callback function. The user data typically
includes the buffer or a pointer to the buffer in which the callback
function formats the directory information as well as information
about the size of the buffe



typedef void (CALLBACK *PBORCA_LISTPROC) ( PPBORCA_DIRENTRY, LPVOID );
Usage:
You provide the code for the callback function. The callback function
generally reads the information about the library entry passed in the
PBORCA_DIRENTRY structure, extracts whatever is wanted, and formats it
in the user data buffer pointed to by LPVOID.
The user data buffer is allocated in the calling program and can be
structured any way you want. It might include a structure that counts
the entries and an array or text block in which you format information
about all the entries.
Argument Description:
- PPBORCA_DIRENTRY Pointer to the structure PBORCA_DIRENTRY



typedef struct pborca_direntry {
TCHAR szComments[PBORCA_MAXCOMMENT + 1];
LONG lCreateTime;
LONG lEntrySize;
LPTSTR lpszEntryName;
PBORCA_TYPE otEntryType;
} PBORCA_DIRENTRY, FAR *PPBORCA_DIRENTRY;
Member Description:
- szComments Comments stored in the library for the object
- lCreateTime The time the object was created
- lEntrySize The size of the object, including its source code and
the compiled object
- lpszEntryName The name of the object for which information is being
returned
- otEntryType A value of the enumerated data type PBORCA_TYPE
specifying the data type of the object

1 Answer

Joel

3/24/2007 5:38:00 PM

0

Here's the working code:

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices; // DllImport
using System.Diagnostics;

namespace ConsoleApplication5
{
public class orcaUtils
{

public static int PBORCA_MAXCOMMENT = 255;

public enum PBORCA_ENTRY_TYPE
{
PBORCA_APPLICATION,
PBORCA_DATAWINDOW,
PBORCA_FUNCTION,
PBORCA_MENU,
PBORCA_QUERY,
PBORCA_STRUCTURE,
PBORCA_USEROBJECT,
PBORCA_WINDOW,
PBORCA_PIPELINE,
PBORCA_PROJECT,
PBORCA_PROXYOBJECT,
PBORCA_BINARY
}

[DllImport("pborc100.dll", EntryPoint = "PBORCA_SessionOpen",
CharSet = CharSet.Unicode, SetLastError = true)]
public unsafe static extern int PBORCA_SessionOpen();

[DllImport("pborc100.dll", EntryPoint = "PBORCA_SessionClose",
CharSet = CharSet.Unicode, SetLastError = true)]
public unsafe static extern void PBORCA_SessionClose(int
hORCASession);

[DllImport("pborc100.dll", EntryPoint =
"PBORCA_LibraryCreate",
CharSet = CharSet.Unicode, SetLastError = true)]
public unsafe static extern int PBORCA_LibraryCreate(int
hORCASession,
[MarshalAs(UnmanagedType.LPTStr)] string lpszLibName,
[MarshalAs(UnmanagedType.LPTStr)] string lpszLibComment);

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct PBORCA_DIRENTRY
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szComments;
public int lCreateTime; /* Time of entry create/
mod */
public int lEntrySize; /* Size of entry */
public string lpszEntryName; /* Pointer to entry
name */
public PBORCA_ENTRY_TYPE otEntryType; /* Entry
type */
}

public delegate void PBORCA_LIBDIRCALLBACK(
IntPtr pDirEntry,
IntPtr lpUserData);

[DllImport("pborc100.dll", EntryPoint =
"PBORCA_LibraryDirectory",
CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int PBORCA_LibraryDirectory(int
hORCASession,
[MarshalAs(UnmanagedType.LPTStr)] string lpszLibName,
[MarshalAs(UnmanagedType.LPTStr)] string lpszLibComments,
int iCmntsBufflen,
PBORCA_LIBDIRCALLBACK pListProc,
IntPtr pUserData
);

}

class Program
{

public static void PBORCA_LibDirCallback(IntPtr pDirEntry,
IntPtr lpUserData)
{
orcaUtils.PBORCA_DIRENTRY myDirEntry =
(orcaUtils.PBORCA_DIRENTRY)Marshal.PtrToStructure(pDirEntry,
typeof(orcaUtils.PBORCA_DIRENTRY));
DateTime myDateTime = new DateTime(1970, 01, 01, 00, 00,
00).AddSeconds((double) myDirEntry.lCreateTime);
}

static void Main(string[] args)
{

int iORCASession = orcaUtils.PBORCA_SessionOpen();
Console.WriteLine("ORCA session opened");

int iRetVal;

orcaUtils.PBORCA_LIBDIRCALLBACK
PBORCA_LibraryDirectoryCallback = new
orcaUtils.PBORCA_LIBDIRCALLBACK(PBORCA_LibDirCallback);
string myName = "C:\\my.pbl";
string myComment = "";
IntPtr myDummy = new IntPtr();
iRetVal = orcaUtils.PBORCA_LibraryDirectory(iORCASession,
myName, myComment, 0, PBORCA_LibraryDirectoryCallback, myDummy);

if (iRetVal == 0)
{
Console.WriteLine("Library directory successful.");
}
else
{
Console.WriteLine("Error generating library directory:
" + iRetVal);
}

orcaUtils.PBORCA_SessionClose(iORCASession);

}
}

}