janar
asked on
passing linked list from c# to c++ dll
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.Se quential,C harSet=Cha rSet.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
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.Se
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
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.
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.
Sorry, one more hint: you can allocate and release unmanaged memory using Marshal.AllocHGlobal and Marshal.FreeHGlobal...
ASKER
The following is the code I came up with :
MarketData MktData;
RdrCache.Read(); // this will get data from SQLDataReader
IntPtr buffer = Marshal.AllocHGlobal(Marsh al.SizeOf( MktData) ); // this assigns a new block of memory
Marshal.PtrToStructure(buf fer,MktDat a);
MktData.AuthorId = RdrCache.GetInt32(0) ;
MktData.LastName = RdrCache.GetString(1);
MktData.FirstName = RdrCache.GetString(2);
Marshal.StructureToPtr(Mkt Data,buffe r,false);
MarketData_Head = buffer;
MarketData_Temporary = buffer;
while(RdrCache.Read())
{
IntPtr buffer = Marshal.AllocHGlobal(Marsh al.SizeOf( MktData) ); Marshal.PtrToStructure(buf fer,MktDat a); MktData.AuthorId = RdrCache.GetInt32(0) ;
MktData.LastName = RdrCache.GetString(1);
MktData.FirstName = RdrCache.GetString(2); Marshal.StructureToPtr(Mkt Data,buffe r,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
MarketData MktData;
RdrCache.Read(); // this will get data from SQLDataReader
IntPtr buffer = Marshal.AllocHGlobal(Marsh
Marshal.PtrToStructure(buf
MktData.AuthorId = RdrCache.GetInt32(0) ;
MktData.LastName = RdrCache.GetString(1);
MktData.FirstName = RdrCache.GetString(2);
Marshal.StructureToPtr(Mkt
MarketData_Head = buffer;
MarketData_Temporary = buffer;
while(RdrCache.Read())
{
IntPtr buffer = Marshal.AllocHGlobal(Marsh
MktData.LastName = RdrCache.GetString(1);
MktData.FirstName = RdrCache.GetString(2); Marshal.StructureToPtr(Mkt
.......
}
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
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.GetIn t32(0); // get data....
md.FirstName=RdrCache.GetS tring(1);
md.LastName=RdrCache.GetSt ring(2);
buf=Marshal.AllocHGlobal(M arshal.Siz eOf(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(lis t, 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.
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.GetIn
md.FirstName=RdrCache.GetS
md.LastName=RdrCache.GetSt
buf=Marshal.AllocHGlobal(M
Marshal.StructureToPtr(md,
}
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(lis
Marshal.FreeHGlobal(list);
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.
ASKER
/*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(SecurityDe tails &secDetails,struct MktData * mktData)
/*these are the errors I am getting*/
c:\inetpub\wwwroot\KRMWbSr v\Service1 .asmx.cs(8 9): Pointers may only be used in an unsafe context
c:\inetpub\wwwroot\KRMWbSr v\Service1 .asmx.cs(8 9): Cannot take the address or size of a variable of a managed type ('KRMWbSrv.Service1.Market Data')
c:\inetpub\wwwroot\KRMWbSr v\Service1 .asmx.cs(2 44): The best overloaded method match for 'KRMWbSrv.Service1.FRACalc ulateOav(r ef KRMWbSrv.Service1.Security Details, KRMWbSrv.Service1.MarketDa ta*)' has some invalid arguments
c:\inetpub\wwwroot\KRMWbSr v\Service1 .asmx.cs(2 44): Argument '2': cannot convert from 'System.IntPtr' to 'KRMWbSrv.Service1.MarketD ata*'
I am not sure of the signatures ?
regards
janar
[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(SecurityDe
/*these are the errors I am getting*/
c:\inetpub\wwwroot\KRMWbSr
c:\inetpub\wwwroot\KRMWbSr
c:\inetpub\wwwroot\KRMWbSr
c:\inetpub\wwwroot\KRMWbSr
I am not sure of the signatures ?
regards
janar
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);
[DllImport("webkrmdl.dll")
static extern int FRACalculateOav(ref SecurityDetails secData, IntPtr data);
ASKER
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:\\newca ll.txt");
// try getting data from the cache
SqlDataReader RdrCache = (SqlDataReader)Context.Cac he.Get("Kr mAuthors") ;
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("Sele ct au_lname,au_fname from authors");
sr.WriteLine ("trying to connect to datalayer");
// add the data to the cache
Context.Cache.Add("KrmAuth ors", RdrCache, null, Cache.NoAbsoluteExpiration ,Cache.NoS lidingExpi ration, CacheItemPriority.High,nul l);
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.GetS tring(0);
md.LastName=RdrCache.GetSt ring(1);
buf=Marshal.AllocHGlobal(M arshal.Siz eOf(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.PrincipalAmoun t = 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;
}
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:\\newca
// try getting data from the cache
SqlDataReader RdrCache = (SqlDataReader)Context.Cac
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("Sele
sr.WriteLine ("trying to connect to datalayer");
// add the data to the cache
Context.Cache.Add("KrmAuth
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.GetS
md.LastName=RdrCache.GetSt
buf=Marshal.AllocHGlobal(M
Marshal.StructureToPtr(md,
}
// temporarily set the new security details
sr.WriteLine ("instantiating security data structure");
SecurityDetails SecurityDet = new SecurityDetails() ;
SecurityDet.Cusip = "CMO1";
SecurityDet.PrincipalAmoun
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;
}
ASKER
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.Si zeOf(md)); // allocate unmanaged memory
buf2=Marshal.AllocHGlobal( Marshal.Si zeOf(md)); // allocate unmanaged memory
md.FirstName=RdrCache.GetS tring(0);
md.LastName=RdrCache.GetSt ring(1);
sr.Write(md.FirstName.ToSt ring());
sr.WriteLine(md.LastName.T oString()) ;
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.Si zeOf(md)); // allocate unmanaged memory
md.FirstName=RdrCache.GetS tring(0);
md.LastName=RdrCache.GetSt ring(1);
sr.Write(md.FirstName.ToSt ring());
sr.WriteLine(md.LastName.T oString()) ;
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
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(
buf2=Marshal.AllocHGlobal(
md.FirstName=RdrCache.GetS
md.LastName=RdrCache.GetSt
sr.Write(md.FirstName.ToSt
sr.WriteLine(md.LastName.T
md.Next = buf2;
Marshal.StructureToPtr(md,
MarketData_Head = buf1;
buf1 = buf2;
}
while (RdrCache.Read())
{
buf2=Marshal.AllocHGlobal(
md.FirstName=RdrCache.GetS
md.LastName=RdrCache.GetSt
sr.Write(md.FirstName.ToSt
sr.WriteLine(md.LastName.T
md.Next = buf2;
Marshal.StructureToPtr(md,
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,
regards
janar
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.Si zeOf(md)); // allocate unmanaged memory
IntPtr next=head; // next buffer is the same as the head
do {
md.FirstName=RdrCache.GetS tring(0); // fill in data bafore Read() is called
md.LastName=RdrCache.GetSt ring(1);
IntPtr current=next; // store current pointer for marshaling call
next=RdrCache.Read() ? Marshal.AllocHGlobal(Marsh al.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!
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(
IntPtr next=head; // next buffer is the same as the head
do {
md.FirstName=RdrCache.GetS
md.LastName=RdrCache.GetSt
IntPtr current=next; // store current pointer for marshaling call
next=RdrCache.Read() ? Marshal.AllocHGlobal(Marsh
md.Next=next; // set next member in structure (will be null id Read() returned false)
Marshal.StructureToPtr(md,
} 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!
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
} 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
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
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
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.
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.
Declare the structure like this:
[StructLayoutLayoutKind.Se
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.PtrToS
}
I think that this should get you going...