Jump to content
Sign in to follow this  
Aipion

Need help with: dll struct, Display Unicode String, Convert dll to x64

Recommended Posts

Aipion

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 by Aipion

Share this post


Link to post
Share on other sites
Valik

Never return a structure from a function. There's your clue.

Share this post


Link to post
Share on other sites
wraithdu

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.

Share this post


Link to post
Share on other sites
Aipion

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 by Aipion

Share this post


Link to post
Share on other sites
Shaggi

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

Share this post


Link to post
Share on other sites
Aipion

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 by Aipion

Share this post


Link to post
Share on other sites
Shaggi

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:

struct 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

Share this post


Link to post
Share on other sites
wraithdu

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.

Share this post


Link to post
Share on other sites
Shaggi

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

Share this post


Link to post
Share on other sites
wraithdu

Also, this function needs to return 2 values

Meh, just have it return the larger of the two lengths and make both buffers that size.

Share this post


Link to post
Share on other sites
Aipion

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 by Aipion

Share this post


Link to post
Share on other sites
Shaggi

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 :oops: 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 :bye:

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 by Shaggi

Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG

Share this post


Link to post
Share on other sites
Aipion

Yeah youre right, didnt test it obviously :oops: 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 :bye:

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.

Share this post


Link to post
Share on other sites
Aipion

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.

Share this post


Link to post
Share on other sites
Richard Robertson

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.

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×