Guest Posted March 19, 2012 Share Posted March 19, 2012 (edited) What i am trying to do is return a struct containing the following from a C++ Dll, if you know any other way please let me know. struct AttributeS { const char* x; const char* y; }; struct AttributeS DLL_EXPORT XmlGetFirstAttribute(int $XmlDocID, const char* XPathExp, struct AttributeS fff) { { TiXmlNode* node = 0; TiXmlElement* todoElement = 0; TiXmlElement* itemElement = 0; node = TinyXPath::XNp_xpath_node(AllXmlDocsHandles[$XmlDocID].RootElement(), XPathExp); if ( !node ) { // return 0; //Error } else { assert( node ); itemElement = node->ToElement(); assert( itemElement ); TiXmlAttribute* doors = itemElement->FirstAttribute(); fff.x = doors->Name(); fff.y = doors->Value(); struct AttributeS retval = { doors->Name() , doors->Value() }; return retval; } } } AutoiT code to call the dll and then get the data returned: Local $XmlAttributeInfo = DllStructCreate('const char* x; const char* y;') Local $aRet = DllCall($hDLL_ATinyXml, "struct:cdecl", "XmlGetFirstAttribute", "int", $XmlDocID, "str", $XPath, "ptr", DllStructGetPtr($XmlAttributeInfo)) MsgBox(0,DllStructGetData($aRet, 1),DllStructGetData($aRet, 2)) Edited April 20, 2012 by Guest Link to comment Share on other sites More sharing options...
Valik Posted March 19, 2012 Share Posted March 19, 2012 Never return a structure from a function. There's your clue. Link to comment Share on other sites More sharing options...
wraithdu Posted March 19, 2012 Share Posted March 19, 2012 There are things wrong with both ends of this. AutoIt: - What gave you the idea that your DllStructCreate pattern is in any way valid? - I can see where the documentation might not be clear on this, but struct cannot be a return type for DllCall. There is no way for AutoIt to know what the structure is supposed to be. C: - fff needs to be a pointer, which you've done in the AutoIt side. However in your DLL you're first assigning it values as a regular structure, then you are completely disregarding it, and creating a new structure in retval and trying to return that, which as I've mentioned (and Valik has also), you cannot do. Link to comment Share on other sites More sharing options...
Guest Posted March 20, 2012 Share Posted March 20, 2012 (edited) There are things wrong with both ends of this. AutoIt: - What gave you the idea that your DllStructCreate pattern is in any way valid? - I can see where the documentation might not be clear on this, but struct cannot be a return type for DllCall. There is no way for AutoIt to know what the structure is supposed to be. C: - fff needs to be a pointer, which you've done in the AutoIt side. However in your DLL you're first assigning it values as a regular structure, then you are completely disregarding it, and creating a new structure in retval and trying to return that, which as I've mentioned (and Valik has also), you cannot do. Never return a structure from a function. There's your clue. You are both right, got the the code to work with int datatype but i want to get strings from the struct with the Char * but when i create the struct in autoit it keeps on erroring. The Updated C++ code: struct AttributeS { char* x; char* y; }; int DLL_EXPORT XmlGetFirstAttribute(int $XmlDocID, const char* XPathExp, struct AttributeS * Somename) { { TiXmlNode* node = 0; TiXmlElement* todoElement = 0; TiXmlElement* itemElement = 0; node = TinyXPath::XNp_xpath_node(AllXmlDocsHandles[$XmlDocID].RootElement(), XPathExp); if ( !node ) { return 0; //Error } else { assert( node ); itemElement = node->ToElement(); assert( itemElement ); TiXmlAttribute* doors = itemElement->FirstAttribute(); Somename->x = (char*)doors->Name(); Somename->y = (char*)doors->Value(); return 1; } } } Edited March 20, 2012 by Guest Link to comment Share on other sites More sharing options...
Shaggi Posted March 20, 2012 Share Posted March 20, 2012 You are both right, got the the code to work with int datatype but i want to get strings from the struct with the Char * but when i create the struct in autoit it keeps on erroring. The Updated C++ code: struct AttributeS { char* x; char* y; }; int DLL_EXPORT XmlGetFirstAttribute(int $XmlDocID, const char* XPathExp, struct AttributeS * Somename) { { TiXmlNode* node = 0; TiXmlElement* todoElement = 0; TiXmlElement* itemElement = 0; node = TinyXPath::XNp_xpath_node(AllXmlDocsHandles[$XmlDocID].RootElement(), XPathExp); if ( !node ) { return 0; //Error } else { assert( node ); itemElement = node->ToElement(); assert( itemElement ); TiXmlAttribute* doors = itemElement->FirstAttribute(); Somename->x = (char*)doors->Name(); Somename->y = (char*)doors->Value(); return 1; } } } You probably need to post autoit code and more stuff too. What is doors? Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG Link to comment Share on other sites More sharing options...
Guest Posted March 21, 2012 Share Posted March 21, 2012 (edited) You probably need to post autoit code and more stuff too. What is doors? The doors is a variable with the TiXmlAttribute* datatype it was from the test file that came with TinyXml and just left it like that in my dll file. Anyways got the code to return the string I wanted but how am I supposed to go with the size of the string, say for E.G. the user has more than 5 char in his string and declare 5 in my struct and in autoit struct and in my dll how I am supposed to make the buffer larger without re compiling the dll or the autoit exe. If the above is not possible can i least get a array from "return char * array" from C++ dll then in autoit which will be returned by $aRet[0]. THe MORE Updated C++ Code: typedef struct { char x[5]; char y[5]; } AttributeS; extern "C" int DLL_EXPORT XmlGetFirstAttribute(int $XmlDocID, const char* XPathExp,AttributeS * ddddd) { { TiXmlNode* node = 0; TiXmlElement* todoElement = 0; TiXmlElement* itemElement = 0; node = TinyXPath::XNp_xpath_node(AllXmlDocsHandles[$XmlDocID].RootElement(), XPathExp); if ( !node ) { return 0; //Error } else { assert( node ); itemElement = node->ToElement(); assert( itemElement ); TiXmlAttribute* doors = itemElement->FirstAttribute(); if (ddddd) { strcpy(ddddd->x,doors->Name()); strcat(ddddd->y,doors->Value()); } return strlen(doors->Value()); } } } Autoit Code: Local $XmlAttributeInfo = DllStructCreate('char x[5]; char y[5];') Local $aRet = DllCall($hDLL_ATinyXml, "int:cdecl", "XmlGetFirstAttribute", "int", $XmlDocID, "str", $XPath, "ptr", DllStructGetPtr($XmlAttributeInfo)) MsgBox(0,DllStructGetData($XmlAttributeInfo, 1),DllStructGetData($XmlAttributeInfo, 2)) Edited March 21, 2012 by Guest Link to comment Share on other sites More sharing options...
Shaggi Posted March 21, 2012 Share Posted March 21, 2012 The doors is a variable with the TiXmlAttribute* datatype it was from the test file that came with TinyXml and just left it like that in my dll file. Anyways got the code to return the string I wanted but how am I supposed to go with the size of the string, say for E.G. the user has more than 5 char in his string and declare 5 in my struct and in autoit struct and in my dll how I am supposed to make the buffer larger without re compiling the dll or the autoit exe. If the above is not possible can i least get a array from "return char * array" from C++ dll then in autoit which will be returned by $aRet[0]. THe MORE Updated C++ Code: typedef struct { char x[5]; char y[5]; } AttributeS; extern "C" int DLL_EXPORT XmlGetFirstAttribute(int $XmlDocID, const char* XPathExp,AttributeS * ddddd) { { TiXmlNode* node = 0; TiXmlElement* todoElement = 0; TiXmlElement* itemElement = 0; node = TinyXPath::XNp_xpath_node(AllXmlDocsHandles[$XmlDocID].RootElement(), XPathExp); if ( !node ) { return 0; //Error } else { assert( node ); itemElement = node->ToElement(); assert( itemElement ); TiXmlAttribute* doors = itemElement->FirstAttribute(); if (ddddd) { strcpy(ddddd->x,doors->Name()); strcat(ddddd->y,doors->Value()); } return strlen(doors->Value()); } } } Autoit Code: Local $XmlAttributeInfo = DllStructCreate('char x[5]; char y[5];') Local $aRet = DllCall($hDLL_ATinyXml, "int:cdecl", "XmlGetFirstAttribute", "int", $XmlDocID, "str", $XPath, "ptr", DllStructGetPtr($XmlAttributeInfo)) MsgBox(0,DllStructGetData($XmlAttributeInfo, 1),DllStructGetData($XmlAttributeInfo, 2)) What you want is dynamic memory. But its truly going to be a bitch, since you gotta decide ownership, who's cleaning up etc, and you're doing it inbetween two languages. Normally, the user of the library should allocate the memory to the library function (xmlgetfirstattribute). Since you dont know how much you're going to need, you either have to a. supply a large enough buffer for all cases b. request the size from a helper function. b is the usual way, because a often results in buffer overruns. Here's how you could implement it in the dll: expandcollapse popupstruct AttributeHelper { size_t x, y; }; struct AttributeS { char * x, * y; }; extern "C" int DLL_EXPORT XmlGetFirstAttributeLen(int $XmlDocID, const char* XPathExp, AttributeHelper * s) { TiXmlNode* node = 0; TiXmlElement* todoElement = 0; TiXmlElement* itemElement = 0; node = TinyXPath::XNp_xpath_node(AllXmlDocsHandles[$XmlDocID].RootElement(), XPathExp); if ( !node ) { return 0; //Error itemElement = node->ToElement(); if(!itemElement) return 0; TiXmlAttribute* doors = itemElement->FirstAttribute(); if (s) { s.x = strlen(doors->Name()) + 1; s.y = strlen(doors->Value()) + 1; } return 1; //success } extern "C" int DLL_EXPORT XmlGetFirstAttribute(int $XmlDocID, const char* XPathExp, AttributeS * s) { TiXmlNode* node = 0; TiXmlElement* todoElement = 0; TiXmlElement* itemElement = 0; node = TinyXPath::XNp_xpath_node(AllXmlDocsHandles[$XmlDocID].RootElement(), XPathExp); if ( !node ) { return 0; //Error itemElement = node->ToElement(); if(!itemElement) return 0; TiXmlAttribute* doors = itemElement->FirstAttribute(); if (s) { ::strcpy(s.x, doors->Name()); ::strcpy(s.y, doors->Value()); } return 1; //success } and then, in autoit: $tagATTRIBUTEHELPER = "int x; int y;" ;sizeof the buffers $tagATTRIBUTES = "ptr x; ptr y;" ;pointers to char buffers $ah = DllStructCreate($tagATTRIBUTEHELPER) $res = DllCall($hDLL_ATinyXml, "int:cdecl", "XmlGetFirstAttributeLen", "int", $XmlDocID, "str", $XPath, "ptr", DllStructGetPtr($ah)) ;get sizes of buffers first If @Error or NOT $res[0] Then ConsoleWrite("!Error..." & @lF) EndIf ;Now AH is filled with the sizes that the buffer we pass must be $XmlAttributeInfo = DllStructCreate($tagATTRIBUTES) #cs Set the buffer pointer x to a newly allocated buffer of chars of ah.x size, which was returned from xmlgetfirstattributelen... #ce DllStructSetData($XmlAttributeInfo,"x",DllStructGetPtr(DllStructCreate("char[" & DllStructGetData($ah,1) & "]"))) #cs Do the same again, but for y #ce DllStructSetData($XmlAttributeInfo,"y",DllStructGetPtr(DllStructCreate("char[" & DllStructGetData($ah,2) & "]"))) #cs And now we can make the actual call with a buffer thats big enough in any case! #ce Local $aRet = DllCall($hDLL_ATinyXml, "int:cdecl", "XmlGetFirstAttribute", "int", $XmlDocID, "str", $XPath, "ptr", DllStructGetPtr($XmlAttributeInfo)) If @Error or NOT $res[0] Then ConsoleWrite("!Error..." & @lF) Else ConsoleWrite("Success: x = " & DllStructGetData($XmlAttributeInfo,"x") & ", y = " & DllStructGetData($XmlAttributeInfo,"y") & @lf) EndIf It may look at bit messy, but thats the usual way to do it. Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG Link to comment Share on other sites More sharing options...
wraithdu Posted March 21, 2012 Share Posted March 21, 2012 The other option is to keep it as one function but use the windows API method of passing a special value for the structure pointer, like NULL, which will simply return the required size of the buffer. Then you call the same function again with the pointer to your actual structure. Link to comment Share on other sites More sharing options...
Shaggi Posted March 22, 2012 Share Posted March 22, 2012 The other option is to keep it as one function but use the windows API method of passing a special value for the structure pointer, like NULL, which will simply return the required size of the buffer. Then you call the same function again with the pointer to your actual structure.You are right, but i've always felt it was awkward to have one function do two different things. Also, this function needs to return 2 values, which you can make a workaround for, but it wont be much prettier than 2 functions, i guess. Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG Link to comment Share on other sites More sharing options...
wraithdu Posted March 22, 2012 Share Posted March 22, 2012 Also, this function needs to return 2 valuesMeh, just have it return the larger of the two lengths and make both buffers that size. Link to comment Share on other sites More sharing options...
Guest Posted March 22, 2012 Share Posted March 22, 2012 (edited) Thank you Shaggi i will use this, but you made a mistake in the autoit code but i fixed it, where it shows DllStructGetData($XmlAttributeInfo,"x") it returns the pointer which is something like the following 0x332544. in order to show the text i moved the buffers and asigned them a variable, look below. $XmlAttributeInfo = DllStructCreate($tagATTRIBUTES) $hBuffers = DllStructCreate("char["& DllStructGetData($ah,1)&"]; char["& DllStructGetData($ah,2)&"]") DllStructSetData($XmlAttributeInfo,"x",DllStructGetPtr($hBuffers,1)) DllStructSetData($XmlAttributeInfo,"y",DllStructGetPtr($hBuffers,2)) Local $aRet = DllCall($hDLL_ATinyXml, "int:cdecl", "XmlGetFirstAttribute", "int", $XmlDocID, "str", $XPath, "ptr", DllStructGetPtr($XmlAttributeInfo)) MsgBox(0,DllStructGetData($hBuffers, 1),DllStructGetData($hBuffers, 2)) The other option is to keep it as one function but use the windows API method of passing a special value for the structure pointer, like NULL, which will simply return the required size of the buffer. Then you call the same function again with the pointer to your actual structure. wraithdu Thanks for the suggestion but I think Shaggi solutions is more efficient on the memory. Edited March 22, 2012 by Guest Link to comment Share on other sites More sharing options...
Shaggi Posted March 22, 2012 Share Posted March 22, 2012 (edited) Thank you Shaggi i will use this, but you made a mistake in the autoit code but i fixed it, where it shows DllStructGetData($XmlAttributeInfo,"x") it returns the pointer which is something like the following 0x332544. in order to show the text i moved the buffers and asigned them a variable, look below. $XmlAttributeInfo = DllStructCreate($tagATTRIBUTES) $hBuffers = DllStructCreate("char["& DllStructGetData($ah,1)&"]; char["& DllStructGetData($ah,2)&"]") DllStructSetData($XmlAttributeInfo,"x",DllStructGetPtr($hBuffers,1)) DllStructSetData($XmlAttributeInfo,"y",DllStructGetPtr($hBuffers,2)) Local $aRet = DllCall($hDLL_ATinyXml, "int:cdecl", "XmlGetFirstAttribute", "int", $XmlDocID, "str", $XPath, "ptr", DllStructGetPtr($XmlAttributeInfo)) MsgBox(0,DllStructGetData($hBuffers, 1),DllStructGetData($hBuffers, 2)) wraithdu Thanks for the suggestion but I think Shaggi solutions is more efficient on the memory. Yeah youre right, didnt test it obviously anyway, this seems to be wrong also: ::strcpy(s.x, doors->Name()); ::strcpy(s.y, doors->Value()); s.x = strlen(doors->Name()) + 1; s.y = strlen(doors->Value()) + 1; Should be: s->member; // not s.member in all four cases E: Another thing, my code example assumes: TiXmlAttribute* doors = itemElement->FirstAttribute(); is the same when you call both functions, i dont know the rest of your code but if this could change you might want to consider adding that pointer to the struct also, so you wont experience buffer overflows. Edited March 22, 2012 by Shaggi Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG Link to comment Share on other sites More sharing options...
Guest Posted March 22, 2012 Share Posted March 22, 2012 Yeah youre right, didnt test it obviously anyway, this seems to be wrong also: ::strcpy(s.x, doors->Name()); ::strcpy(s.y, doors->Value()); s.x = strlen(doors->Name()) + 1; s.y = strlen(doors->Value()) + 1; Should be: s->member; // not s.member in all four cases I did that whan i saw the error from the compiler, anyways thanks. E: Another thing, my code example assumes: TiXmlAttribute* doors = itemElement->FirstAttribute(); is the same when you call both functions, i dont know the rest of your code but if this could change you might want to consider adding that pointer to the struct also, so you wont experience buffer overflows. I will do that. Link to comment Share on other sites More sharing options...
Guest Posted April 20, 2012 Share Posted April 20, 2012 Renamed Then Main POST to "Need help with: dll struct, Display Unicode String, Convert dll to x64" from "Need help with dll struct" to avoid starting a new topic.Now that I have finished creating the 32 bit DLL but has one issue with Unicode, the thing is that i don't know how to go with it.When the file has been read by the TinyXml with Unicode encoding it returns the text by const char* which I then use the function "UTF8_to_WChar" mentioned on "http://www.cplusplus.com/forum/general/7142/" which will give me wchar_t . Is this the right way to go with it?Another thing is that when I want to make the 32 bit DLL to 64 bit, do I need to change any code from the DLL in order to make the DLL work or just change the complier options of the 64 bit compiler. Link to comment Share on other sites More sharing options...
Richard Robertson Posted April 20, 2012 Share Posted April 20, 2012 As long as you aren't treating pointers like 32 bit integers, recompiling is enough to get basic code from 32 bit to 64 bit. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now