?
Solved

passing linked list from c# to c++ dll

Posted on 2003-02-21
14
Medium Priority
?
790 Views
Last Modified: 2007-12-19
I have a x.lib which was made in c++.
In the vc ++ appln we created linked lists (out of structures) and passed them on to these libraries.

We want to expose the functionality available in this library as a web service.

I started constructing a linked list in C#.

[StructLayoutLayoutKind.Sequential,CharSet=CharSet.Ansi )]
public unsafe struct MarketData
{
     public int AuthorId;
     public string LastName;
     public string FirstName;
     public unsafe MarketData * next;
}

In my web service method I write

unsafe
{
     MarketData MktData ;
     MarketData * mkt2;
     MarketData * mkt1 ;
       // acceess database to load rdrCache
        MktData = new MarketData();
        MktData->x = RdrCache.getInt32(0);
        MktData->next = null;
        Mkt1 = MktData;
        while(RdrCache.read())
        {
          Mkt1->next = new MarketData();
          Mkt1 = Mkt1->next ;
          Mkt1->x = RdrCache.getInt32(0);
          Mkt1->next = null;
        }

}

How can I implement this ?

regards
janar

 
0
Comment
Question by:janar
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 5
14 Comments
 
LVL 14

Expert Comment

by:AvonWyss
ID: 7998087
I think you can avoid all of the "unsafe" stuff and just use the marshalling provided by the .NET framework.

Declare the structure like this:

[StructLayoutLayoutKind.Sequential,CharSet=CharSet.Ansi )]
public struct MarketData
{
    public int AuthorId;
    public string LastName;
    public string FirstName;
    public IntPtr Next;
}

And make the call to the DLL so that you receive a IntPtr to the first MarketData in your list. Now, use this code to convert it to a managed struct:

public MarketData GetMarketData(IntPtr pointerToData)
{
    return (MarketData)Marshal.PtrToStructure(pointerToData, typeof(MarketData));
}

I think that this should get you going...
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 7998099
I just noticed that I might have answered the question the for the wrong way of converting the list. My code is for reading the list, but you want to create it, right? In this case, use a similar approach:

Use the same struct as in my provious post, then use Marshal.StructureToPtr to copy the first struct into memory. Assign the IntPtr to the next struct in the list, and call Marshal.StructureToPtr again, etc...

Note that you have to allocate the unmanaged memory on which you operate with StructureToPtr.
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 7998108
Sorry, one more hint: you can allocate and release unmanaged memory using Marshal.AllocHGlobal and Marshal.FreeHGlobal...
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:janar
ID: 8012418
The following is the code I came up with :
MarketData MktData;
RdrCache.Read();  // this will get data from SQLDataReader
     IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(MktData) ); // this assigns a new block of memory
     Marshal.PtrToStructure(buffer,MktData);
     MktData.AuthorId = RdrCache.GetInt32(0) ;
     MktData.LastName = RdrCache.GetString(1);
     MktData.FirstName = RdrCache.GetString(2);
     Marshal.StructureToPtr(MktData,buffer,false);
     MarketData_Head = buffer;
     MarketData_Temporary = buffer;
     while(RdrCache.Read())
     {
     IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(MktData) );                         Marshal.PtrToStructure(buffer,MktData);               MktData.AuthorId = RdrCache.GetInt32(0) ;
     MktData.LastName = RdrCache.GetString(1);
     MktData.FirstName = RdrCache.GetString(2);          Marshal.StructureToPtr(MktData,buffer,false);      
        .......
       }

This is the kind of code that I could come up with ? I have not compiled it yet becoz of the following points:
I am still not able to figure out the second parameter for Marshal.PtrToStructure.

how should I declare it

a. MarketData MktData
b. MarketData MktData = new MarketData

The next thing is when should I assign the next pointer for the structure

regards
janar
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 8014429
To create and free the linked list, I would use code like this:

          public IntPtr CreateLinkedList() {
               MarketData md=new MarketData(); // create temporary item
               IntPtr buf=IntPtr.Zero; // last item in the linked list will point to null
               while (RdrCache.Read()) {
                    md.Next=buf; // assign previous buffer of the list
                    md.AuthorId=RdrCache.GetInt32(0); // get data....
                    md.FirstName=RdrCache.GetString(1);
                    md.LastName=RdrCache.GetString(2);
                    buf=Marshal.AllocHGlobal(Marshal.SizeOf(md)); // allocate unmanaged memory
                    Marshal.StructureToPtr(md, buf, false); // copy data to unmanaged memory
               }
               return buf; // return pointer to head of the linked list
          }
         
          public void DestroyLinkedList(IntPtr list) {
               MarketData md=new MarketData(); // create temporary item
               while (list!=IntPtr.Zero) { // linked list not empty?
                    Marshal.PtrToStructure(list, md); // copy head item to mb
                    Marshal.FreeHGlobal(list); // free unmanaged memory
                    list=md.Next; // get the next item from mb
               }
          }

Note: freeing is absolutely required to avoid memory leaks; the memory used by the list is not managed memory and therefore never collected by the garbage collector.
0
 

Author Comment

by:janar
ID: 8014491
/*this is what I have in c#*/
     [DllImport("webkrmdl.dll")]
     static extern int FRACalculateOav(ref SecurityDetails secData,MarketData * data);

/*this is what I have in the c++ side*/
extern "C" __declspec(dllexport) int __stdcall   FRACalculateOav(SecurityDetails &secDetails,struct MktData * mktData)

/*these are the errors I am getting*/
c:\inetpub\wwwroot\KRMWbSrv\Service1.asmx.cs(89): Pointers may only be used in an unsafe context
c:\inetpub\wwwroot\KRMWbSrv\Service1.asmx.cs(89): Cannot take the address or size of a variable of a managed type ('KRMWbSrv.Service1.MarketData')
c:\inetpub\wwwroot\KRMWbSrv\Service1.asmx.cs(244): The best overloaded method match for 'KRMWbSrv.Service1.FRACalculateOav(ref KRMWbSrv.Service1.SecurityDetails, KRMWbSrv.Service1.MarketData*)' has some invalid arguments
c:\inetpub\wwwroot\KRMWbSrv\Service1.asmx.cs(244): Argument '2': cannot convert from 'System.IntPtr' to 'KRMWbSrv.Service1.MarketData*'

I am not sure of the signatures ?
regards
janar
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 8014545
Note that we got rid of the pointers by using Marshal and IntPtr's. Therefore, your declaration should be:

    [DllImport("webkrmdl.dll")]
    static extern int FRACalculateOav(ref SecurityDetails secData, IntPtr data);
0
 

Author Comment

by:janar
ID: 8019709
ReturnValue = FRACalculateOav(ref SecurityDet,buf);
This line is giving me a Object reference not set to an instance of an object, when run from the client.

Any ideas ?

regards
janar

THE CODE FOLLOWS
[DllImport("webkrmdl.dll")]
     static extern int FRACalculateOav(ref SecurityDetails secData,IntPtr buffer);

[WebMethod()]
public int executeFRACalculateOav()
{
int ReturnValue;
StreamWriter sr = File.CreateText("c:\\newcall.txt");  
// try getting data from the cache
SqlDataReader RdrCache = (SqlDataReader)Context.Cache.Get("KrmAuthors");
if(RdrCache == null)
{
// if data not in cache read the data afresh from the database
string DBConnection = "";
DBObject dataLayer = new DBObject(DBConnection);

RdrCache = dataLayer.RunCommand("Select au_lname,au_fname from authors");
sr.WriteLine ("trying to connect to datalayer");
// add the data to the cache
Context.Cache.Add("KrmAuthors", RdrCache, null, Cache.NoAbsoluteExpiration,Cache.NoSlidingExpiration, CacheItemPriority.High,null);
sr.WriteLine ("finished caching");
}
sr.WriteLine("about to create linked list");
MarketData md=new MarketData(); // create temporary item
IntPtr buf=IntPtr.Zero; // last item in the linked list will point to null
while (RdrCache.Read())
{
md.Next=buf; // assign previous buffer of the list
md.FirstName=RdrCache.GetString(0);
md.LastName=RdrCache.GetString(1);
buf=Marshal.AllocHGlobal(Marshal.SizeOf(md)); // allocate unmanaged memory
Marshal.StructureToPtr(md, buf, false); // copy data to unmanaged memory
}

// temporarily set the new security details
sr.WriteLine ("instantiating security data structure");
SecurityDetails SecurityDet = new SecurityDetails() ;
SecurityDet.Cusip = "CMO1";
SecurityDet.PrincipalAmount = 10000.123;
sr.WriteLine("About to call dll");
ReturnValue = FRACalculateOav(ref SecurityDet,buf);
sr.WriteLine("finished calling dll");
sr.Close();
//return buf; // return pointer to head of the linked list
return ReturnValue;
}
0
 

Author Comment

by:janar
ID: 8020863
Using the code below I was able to create a linked list that can be passed to c++ dll.

Still I am getting the last item repeatedly ? I am working on this.

Is there a better way of doing this ?

MarketData md=new MarketData(); // create temporary item
IntPtr buf1=IntPtr.Zero;
IntPtr buf2=IntPtr.Zero;
if (RdrCache.Read())
{
buf1=Marshal.AllocHGlobal(Marshal.SizeOf(md)); // allocate unmanaged memory
buf2=Marshal.AllocHGlobal(Marshal.SizeOf(md)); // allocate unmanaged memory
md.FirstName=RdrCache.GetString(0);
md.LastName=RdrCache.GetString(1);
sr.Write(md.FirstName.ToString());
sr.WriteLine(md.LastName.ToString());
md.Next = buf2;
Marshal.StructureToPtr(md, buf1, false); // copy data to unmanaged memory
MarketData_Head = buf1;
buf1 = buf2;
}

while (RdrCache.Read())
{
buf2=Marshal.AllocHGlobal(Marshal.SizeOf(md)); // allocate unmanaged memory
md.FirstName=RdrCache.GetString(0);
md.LastName=RdrCache.GetString(1);
sr.Write(md.FirstName.ToString());
sr.WriteLine(md.LastName.ToString());
md.Next = buf2;
Marshal.StructureToPtr(md, buf1, false); // copy data to unmanaged memory
buf1 = buf2;
}
/*this is because we are creating two memory locations at the same time
  At the end of the recordset the last node pointer will not be having
  a null value instead will be pointing to a valid memory address.The
  next three lines will set the last node pointers value to null*/
buf2 = IntPtr.Zero;
md.Next = buf2;
Marshal.StructureToPtr(md, buf1, false);

regards
janar
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 8024021
Janar, your code creates a memory leak. Also, it is much more complicated than necessary... why aren't you using my code? Probably because it returns the list in reversed order, right? But that should not be an issue since you can have your SQL command reverse the order of your list.

You have the last item twice since you have a note "too much" lying around. In fact, you would have to do the call to Read() before that and depending on the result put IntPtr.Zero into the md structure before the call to StructToPtr(). This code should do what you expect:

               if (!RdrCache.Read())
                    return IntPtr.Zero; // return null if 0 items in list
               MarketData md=new MarketData(); // create temporary item
               IntPtr head=Marshal.AllocHGlobal(Marshal.SizeOf(md)); // allocate unmanaged memory
               IntPtr next=head; // next buffer is the same as the head
               do {
                    md.FirstName=RdrCache.GetString(0); // fill in data bafore Read() is called
                    md.LastName=RdrCache.GetString(1);
                    IntPtr current=next; // store current pointer for marshaling call
                    next=RdrCache.Read() ? Marshal.AllocHGlobal(Marshal.SizeOf(md)) : IntPtr.Zero; // allocate next if any data is available
                    md.Next=next; // set next member in structure (will be null id Read() returned false)
                    Marshal.StructureToPtr(md, current, false); // copy data to current unmanaged memory block
               }     while (buf!=IntPtr.Zero); // if no memory was allocated, the list has reached its end
               return head; // this is the beginning of the list

And, *please* do indent your code. It would make it much easier for me and other people to understand your code!
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 8024029
Oops. Mistyped a line...:

              }     while (buf!=IntPtr.Zero); // if no memory was allocated, the list has reached its end

should read

              }     while (next!=IntPtr.Zero); // if no memory was allocated, the list has reached its end
0
 
LVL 14

Accepted Solution

by:
AvonWyss earned 400 total points
ID: 8024053
Or even smaller code:

               if (!RdrCache.Read())
                    return IntPtr.Zero; // return null if 0 items in list
               MarketData md=new MarketData(); // create temporary item
               IntPtr head=md.Next=Marshal.AllocHGlobal(Marshal.SizeOf(md)); // allocate unmanaged memory
               do {
                    IntPtr current=md.Next; // store current pointer for marshaling call
                    md.FirstName=RdrCache.GetString(0); // fill in data before Read() is called
                    md.LastName=RdrCache.GetString(1);
                    md.Next=RdrCache.Read() ? Marshal.AllocHGlobal(Marshal.SizeOf(md)) : IntPtr.Zero; // allocate next if any data is available
                    Marshal.StructureToPtr(md, current, false); // copy data to current unmanaged memory block
               }     while (nd.Next!=IntPtr.Zero); // if no memory was allocated, the list has reached its end
               return head; // this is the beginning of the list

:-) (just saw these optimizations after posting the code)
0
 

Author Comment

by:janar
ID: 8039323
hi,

Thanks for all the help. I have got my code working now.

Indenting -- when I copied over into this area from my actual code the indenting was not okay. That is the reason I had to remove all the indents and paste the code here.

I could not use your exact code becoz in the actual implementation the circumstances are different.

Thanks for the execellent help.

regards
janar
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 8040354
Janar, thank you for the feedback! I'm happy to hear that you could use my suggestions to write your own working code.

Since you seem to be quite new to EE (with only few questions graded so far), please let me suggest that when you receive extensive help from an expert on a question, it would be a nice move of you to raise the points to what you feel appropriate before grading the question in order to show your gratitude to the expert.

Of course, this is not a requirement, but it helps to motivate the experts to deliver more than just the bare response to the question since points are the only reward we are getting.
0

Featured Post

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article introduced a TextBox that supports transparent background.   Introduction TextBox is the most widely used control component in GUI design. Most GUI controls do not support transparent background and more or less do not have the…
We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …
In this video you will find out how to export Office 365 mailboxes using the built in eDiscovery tool. Bear in mind that although this method might be useful in some cases, using PST files as Office 365 backup is troublesome in a long run (more on t…
Suggested Courses
Course of the Month9 days, 6 hours left to enroll

764 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question