Memory Allication (Managed VC++ DLL to Unmanaged DLL) [Second Try]

Discussion in 'ASP .Net' started by Weston Fryatt, Dec 29, 2004.

  1. (Sorry for spamming multiple groups, But I need a solution to this problem)

    I think this should be a simple question on Memory Allocation in a managed DLL and passing a memory pointer over to an unmanaged DLL.

    I have a "Unmanaged" Client DLL that I'm creating a Managed "wrapper" to be used in VB.Net and/or C#..

    In the my Client DLL (unmanaged), There are several functions where I need to allocate a "client side" memory buffer to marshall data into from the server side of the application.

    Normally in VC++ 7.1 (unmanaged) I would normally just malloc the memory that I needed:

    UBYTE *pDataPtr = NULL;
    DWORD dwDataSize = 0;

    try
    {
    GetServerSideDataSize(dwDataSize);
    pDataPtr = (UBYTE *)malloc(dwDataSize);
    if (pDataPtr)
    {
    GetServerSideData(pDataPtr);
    // Do something with pData Now..
    free(pDataPtr);
    pDataPtr = NULL;
    }
    }
    catch(...)
    {
    if (pDataPtr)
    free(pDataPtr);
    }

    In my Managed DLL wrapper I've done this so far which fails when I call "new" on the VB.Net side..

    namespace CDMSClient_SQL
    {
    [DllImport("CDMSClient.dll", EntryPoint = "SQLGetRows", CharSet = Ansi)]
    STATUS SQLGetRows(const Int32 RowCount, const Int32 DataSize, UBYTE *Data, String *Columns, String *From, String *Where, String *OrderBy);

    [DllImport("CDMSClient.dll", EntryPoint = "SQLGetRowInfo", CharSet = Ansi)]
    STATUS SQLGetRowInfo(Int32 &RowCount, Int32 &ColumnCount, Int32 &DataSize, String *Columns, String *From, String *Where);
    }


    Array *CCDMSClientInterface::SQLGetRows(const Int32 MaxRows, String *Columns, String *From, String *Where, String *OrderBy)
    {
    STATUS Status = EC_OK;
    Int32 dwRowCount = 0;
    Int32 dwColumnCount = 0;
    Int32 dwDataSize = 0;
    Array *Data = NULL;
    UBYTE __pin *pDataPtr = NULL;

    Status = CDMSClient_SQL::SQLGetRowInfo(dwRowCount, dwColumnCount, dwDataSize, Columns, From, Where);
    if (Status == EC_OK)
    {
    pDataPtr = (UBYTE *)new char[dwDataSize]; // <--- This is the line that fails in the Managed VC++ Side
    Status = CDMSClient_SQL::SQLGetRows(dwRowCount, dwDataSize, pDataPtr, "*", "CDMS_Config", "", "");
    if (Status == EC_OK)
    Data = Convert_CDBArray_2_Object(pDataPtr);
    }

    return (Data);
    }

    VB.Net Side of code:

    Imports ClientInterface = CDMSClientDotNet.CCDMSClientInterface

    Module Module1

    Sub Main()
    Dim ci As ClientInterface = New ClientInterface
    Dim Data As Array
    Dim Status As Int32

    Status = ci.SetApplicationID("43C1F2F1-CB68-4B89-A8C8-5E0C42CE4866")
    If Status = 1 Then
    Status = ci.SetDomain("Primary")
    If Status = 1 Then
    Status = ci.CreateServerContext()
    If Status = 1 Then
    Status = ci.Login("admin", "optical")
    If Status = 1 Then
    Console.WriteLine("Logged In!")

    Data = ci.SQLGetRows(1000, "*", "CDMS_Config", "", "") // <---This is the line that fails in the VB.Net side

    ci.Logout()
    Else
    Console.WriteLine("Failed to Login!")
    End If
    ci.DeleteServerContext()
    Else
    Console.WriteLine("Failed to Create Server Context!")
    End If
    Else
    Console.WriteLine("Failed to Set Domain!")
    End If
    Else
    Console.WriteLine("Failed to Set ApplicationID!")
    End If
    End Sub

    End Module


    I have many functions that pass data from the server side to the client side via a memory buffer. This data in the memory buffer is parsed and put back into a dynamic array (Custom C++ class called a CDBArray). The first function that I'm trying to get to work, fetches a database table from the server side and marshalls it back over to the client side. The Server and Client is using RPC to marshall data back and forth. (Just a side note, The client side can not access the database directly via ODBC or anything else. It must go thought my client dll. This is done for security reason to keep the client side locked down.)

    Any ideas or suggestion are more than welcomed!!

    Thanks Again!
    Weston Fryatt
    Weston Fryatt, Dec 29, 2004
    #1
    1. Advertising

  2. Weston Fryatt" <wfryatt "at wrote:
    > In my Managed DLL wrapper I've done this so far which fails when I
    > call "new" on the VB.Net side..


    > namespace CDMSClient_SQL
    > {
    > [DllImport("CDMSClient.dll", EntryPoint = "SQLGetRows", CharSet =
    > Ansi)]
    > STATUS SQLGetRows(const Int32 RowCount, const Int32 DataSize,
    > UBYTE *Data, String *Columns, String *From, String *Where, String
    > *OrderBy);
    >
    > [DllImport("CDMSClient.dll", EntryPoint = "SQLGetRowInfo",
    > CharSet = Ansi)]
    > STATUS SQLGetRowInfo(Int32 &RowCount, Int32 &ColumnCount,
    > Int32 &DataSize, String *Columns, String *From, String *Where);
    > }


    This is managed C++? If so, why are you using platform invoke to access the
    methods. Why don't you use an import library (.lib) and use IJW?

    > Array *CCDMSClientInterface::SQLGetRows(const Int32 MaxRows, String
    > *Columns, String *From, String *Where, String *OrderBy)


    Note that const means nothing to .NET and since you are calling this class
    with VB.NET the constness of MaxRows is ignored. What is Array? Is this
    System::Array? If so, why are you returning an untyped array?

    > pDataPtr = (UBYTE *)new char[dwDataSize]; // <--- This is
    > the line that fails in the Managed VC++ Side


    Since you are using unmanaged pointers this should call the unmanaged new.
    To make absolutely sure you can write it as

    pDataPtr = (UBYTE *)__nogc new char[dwDataSize];

    are you sure that this is where the error lies? What is the error message?
    have you tried testing dwDataSize to see if it is not zero, and pDataPtr to
    see if it is not null?

    > Status = CDMSClient_SQL::SQLGetRows(dwRowCount, dwDataSize,
    > pDataPtr, "*", "CDMS_Config", "", "");
    > if (Status == EC_OK)
    > Data = Convert_CDBArray_2_Object(pDataPtr);
    > }


    Where do you call delete []?

    If SQLGetRows does not care where the memory comes from you can use
    Marshal::AllocHGlobal or Marshal::AllocCoTaskMem and then use the
    appropriate FreeCoTaskMem and FreeHGlobal. Both of these returns a IntPtr
    which can be cast to a void* and then assigned to a pinning pointer.
    However, the C++ new should work fine as long as you call delete []
    somewhere. (Personally I would bracket the code in try/__finally and call
    delete [] in the __finally clause.)

    >
    > Imports ClientInterface = CDMSClientDotNet.CCDMSClientInterface


    This is poor naming, it's a class, not an interface, you cannot call New on
    an interface!

    > Module Module1
    >
    > Dim Data As Array
    >
    > Data = ci.SQLGetRows(1000, "*",
    > "CDMS_Config", "", "") // <---This is the line that fails in the VB.Net
    > side


    Again System.Array is not much use. You should write the managed C++ to
    return a typed array instead.

    > I have many functions that pass data from the server side to the
    > client side via a memory buffer. This data in the memory buffer is
    > parsed and put back into a dynamic array (Custom C++ class called a
    > CDBArray). The first function that I'm trying to get to work,
    > fetches a database table from the server side and marshalls it back
    > over to the client side. The Server and Client is using RPC to
    > marshall data back and forth. (Just a side note, The client side can


    RPC should handle any type of memory you pass to it, the RPC layer will
    allocate the appropriate buffer. The only time that you'll get a problem is
    if you're returning a new array from the RPC method. Are any of the pointers
    in SQLGetRows [in,out]?

    Richard
    --
    www.richardgrimes.com
    my email is encrypted with ROT13 (www.rot13.org)
    Richard Grimes [MVP], Dec 30, 2004
    #2
    1. Advertising

  3. Thanks Richard!

    Marshal::AllocHGlobal() did the trick!




    "Richard Grimes [MVP]" <read my sig> wrote in message
    news:...
    > Weston Fryatt" <wfryatt "at wrote:
    > > In my Managed DLL wrapper I've done this so far which fails when I
    > > call "new" on the VB.Net side..

    >
    > > namespace CDMSClient_SQL
    > > {
    > > [DllImport("CDMSClient.dll", EntryPoint = "SQLGetRows", CharSet =
    > > Ansi)]
    > > STATUS SQLGetRows(const Int32 RowCount, const Int32 DataSize,
    > > UBYTE *Data, String *Columns, String *From, String *Where, String
    > > *OrderBy);
    > >
    > > [DllImport("CDMSClient.dll", EntryPoint = "SQLGetRowInfo",
    > > CharSet = Ansi)]
    > > STATUS SQLGetRowInfo(Int32 &RowCount, Int32 &ColumnCount,
    > > Int32 &DataSize, String *Columns, String *From, String *Where);
    > > }

    >
    > This is managed C++? If so, why are you using platform invoke to access

    the
    > methods. Why don't you use an import library (.lib) and use IJW?
    >
    > > Array *CCDMSClientInterface::SQLGetRows(const Int32 MaxRows, String
    > > *Columns, String *From, String *Where, String *OrderBy)

    >
    > Note that const means nothing to .NET and since you are calling this class
    > with VB.NET the constness of MaxRows is ignored. What is Array? Is this
    > System::Array? If so, why are you returning an untyped array?
    >
    > > pDataPtr = (UBYTE *)new char[dwDataSize]; // <--- This is
    > > the line that fails in the Managed VC++ Side

    >
    > Since you are using unmanaged pointers this should call the unmanaged new.
    > To make absolutely sure you can write it as
    >
    > pDataPtr = (UBYTE *)__nogc new char[dwDataSize];
    >
    > are you sure that this is where the error lies? What is the error message?
    > have you tried testing dwDataSize to see if it is not zero, and pDataPtr

    to
    > see if it is not null?
    >
    > > Status = CDMSClient_SQL::SQLGetRows(dwRowCount, dwDataSize,
    > > pDataPtr, "*", "CDMS_Config", "", "");
    > > if (Status == EC_OK)
    > > Data = Convert_CDBArray_2_Object(pDataPtr);
    > > }

    >
    > Where do you call delete []?
    >
    > If SQLGetRows does not care where the memory comes from you can use
    > Marshal::AllocHGlobal or Marshal::AllocCoTaskMem and then use the
    > appropriate FreeCoTaskMem and FreeHGlobal. Both of these returns a IntPtr
    > which can be cast to a void* and then assigned to a pinning pointer.
    > However, the C++ new should work fine as long as you call delete []
    > somewhere. (Personally I would bracket the code in try/__finally and call
    > delete [] in the __finally clause.)
    >
    > >
    > > Imports ClientInterface = CDMSClientDotNet.CCDMSClientInterface

    >
    > This is poor naming, it's a class, not an interface, you cannot call New

    on
    > an interface!
    >
    > > Module Module1
    > >
    > > Dim Data As Array
    > >
    > > Data = ci.SQLGetRows(1000, "*",
    > > "CDMS_Config", "", "") // <---This is the line that fails in the VB.Net
    > > side

    >
    > Again System.Array is not much use. You should write the managed C++ to
    > return a typed array instead.
    >
    > > I have many functions that pass data from the server side to the
    > > client side via a memory buffer. This data in the memory buffer is
    > > parsed and put back into a dynamic array (Custom C++ class called a
    > > CDBArray). The first function that I'm trying to get to work,
    > > fetches a database table from the server side and marshalls it back
    > > over to the client side. The Server and Client is using RPC to
    > > marshall data back and forth. (Just a side note, The client side can

    >
    > RPC should handle any type of memory you pass to it, the RPC layer will
    > allocate the appropriate buffer. The only time that you'll get a problem

    is
    > if you're returning a new array from the RPC method. Are any of the

    pointers
    > in SQLGetRows [in,out]?
    >
    > Richard
    > --
    > www.richardgrimes.com
    > my email is encrypted with ROT13 (www.rot13.org)
    >
    >
    Weston Fryatt, Jan 1, 2005
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Arvind Ghai

    Drag n drop from managed to unmanaged apps

    Arvind Ghai, Feb 15, 2005, in forum: ASP .Net
    Replies:
    0
    Views:
    581
    Arvind Ghai
    Feb 15, 2005
  2. =?Utf-8?B?U2FuZHk=?=

    Managed and Unmanaged Code Confusion

    =?Utf-8?B?U2FuZHk=?=, Oct 26, 2004, in forum: ASP .Net
    Replies:
    2
    Views:
    522
    Kevin Spencer
    Oct 26, 2004
  3. bienwell
    Replies:
    4
    Views:
    3,718
    bienwell
    May 27, 2005
  4. Dave
    Replies:
    2
    Views:
    420
    Deming He
    Dec 13, 2003
  5. Laurent Bugnion
    Replies:
    0
    Views:
    441
    Laurent Bugnion
    Dec 6, 2006
Loading...

Share This Page