Thread: Help returning string from dll

  1. #1
    Registered User
    Join Date
    Feb 2011
    Posts
    5

    Help returning string from dll

    I am trying to create a dll that returns a line from a text file given a search string. I have the code to open a file and search for the string which works fine but i can't get it to return the string.

    I am a novice at c++ so a bit lost. I don't even know if i should be "throwing" a return into the middle of the code like i am. The problem code is

    Code:
     return line;
    I get the error" cannot convert `std::string' to std::string*' in return " Any ideas?

    This is what i have,

    Code:
    #include <iostream>
    #include <windows.h>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    
    BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved){
        return TRUE;
    }
    
    
    extern "C" __declspec( dllexport ) __stdcall string* strFind(char *);
    
    extern "C" __declspec( dllexport ) __stdcall string* strFind(char *strString)
    
    {
        
        
        char* search = strString; // search pattern
        int offset;
        string line;
        ifstream Myfile;
        
        Myfile.open ("example.txt");
        
        if(Myfile.is_open())
        {
            while(!Myfile.eof())
            {
                getline(Myfile,line);
                if ((offset = line.find(search, 0)) != string::npos) {
                    cout << "found '" << line <<endl;
                    return line;
                }
            }
            Myfile.close();
        }
        else
        cout<<"Unable to open this file."<<endl;
        
        return 0;
    }

  2. #2
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    std::string* is a pointer to string, you need to return an address to a string object.

    std::string is a class and returning it in any way is not a good idea, since you will not be able to use this DLL in any other language or even different compiler version.

    I'd recommend passing a 'char*' parameter to the function, which would fill it with necessary data (plus an 'int' parameter for max length), or returning a const char* (worse idea in some cases).

  3. #3
    Registered User
    Join Date
    Feb 2011
    Posts
    5
    Quote Originally Posted by kmdv View Post
    std::string* is a pointer to string, you need to return an address to a string object.

    std::string is a class and returning it in any way is not a good idea, since you will not be able to use this DLL in any other language or even different compiler version.

    I'd recommend passing a 'char*' parameter to the function, which would fill it with necessary data (plus an 'int' parameter for max length), or returning a const char* (worse idea in some cases).
    "Passing a char" i assume you mean change,
    Code:
    string line;  to
    char line;
    I get all kinds of errors if i do that, starting in this line,

    Code:
    getline(Myfile,line);
    no matching function for call to `getline(std::ifstream&, char&)'
    I also tried converting the string output "line" to a char with line.c_str() and making the fucntion a const char like you said so,

    Code:
    extern "C" __declspec( dllexport ) __stdcall const char* Trimstr(char* strString)
    and although i did get a response form the dll(i'm using VB as a front end), bizzarly enough the data that was returned was the partial name of the VB project i am working in, how it got that i don't know, it's as if it's reading some random memory location and sending that back. It is almost there though i think.

    Any suggestions?
    Last edited by rm78; 02-05-2011 at 01:17 PM.

  4. #4
    Nasal Demon Xupicor's Avatar
    Join Date
    Sep 2010
    Location
    Poland
    Posts
    179
    Nope, kmdv ment this:
    Code:
    void foo(char* buffer, int length) {
        // fill buffer, mind it's length
    }
    You can use std::string internally if you like it (I do ), just copy it over to char* buffer after all is done.

    Returning const pointer would work more or less like string::c_str() does, so the library would have to allocate and deallocate it on its side.

    You could also take a pointer and allocate memory for it in foo, then return it, but that is error prone solution - memory leaks will occur if you forget to free memory or provide a pointer to already allocated memory. So, I'd say don't do it.

    edit: Are you by any chance doing something like this?
    Code:
    const char* foo() {
        std::string s = "Hello there!";
        return s.c_str();
    }
    Ever heard of "dangling pointer"? By the time you get the pointer back, the string object is destroyed. Even though the memory is probably unchanged at the moment and it may look like no big deal, it can be claimed and changed any moment now.

  5. #5
    Registered User
    Join Date
    Feb 2011
    Posts
    5
    Yes that's what i am trying to do turn a string into a char and return it back to the VB app.

    Code:
    const char* foo() {
        std::string s = "Hello there!";
        return s.c_str();
    }

    I tried this code in a console app and it works fine but not in a dll. The dll is going back to a VB app. If i tried this with the VB i would get something like "projec" back which to me is weird it makes non sense, where is this coming from, the name of the VB form i am using is "project" if you see what i mean.

    copy it over to char* buffer
    That's what i am trying to do but as i said my c++ knowledge is limited. I thought c_str() would work but it doesn't.


    How can i modify the search to change,

    Code:
    string line;
    to

    Code:
    char line;

    and yet have it work with
    Code:
      getline(Myfile,line);
                if ((offset = line.find(search, 0)) != string::npos) {

  6. #6
    Nasal Demon Xupicor's Avatar
    Join Date
    Sep 2010
    Location
    Poland
    Posts
    179
    The last part makes no sense. You can't fit more than one character in one character, right?

    Just to make sure you understand it, the following code is NOT working properly:
    Code:
    const char* foo() {
        std::string s = "Hello there!";
        return s.c_str(); // kaboom, the pointer won't be valid after return
    }
    what you want is something like this:
    Code:
    #include <iostream>
    #include <string>
    #include <cstring>
    
    int foo(char* buffer, int len) {
        std::string s = "Hello there!";
        // do something
        strncpy(buffer, s.c_str(), len-1); // copy things, but don't overrun buffer
        buffer[len - 1] = '\0'; // 0 terminates the c-string
        return 0; // return error state, e.g. 0 - all good, something else -  bad
        // or maybe return char*, but NULL (or nullptr) on error?
    }
    
    int main() {
        char buf[5] = { 0 }; // or dynamically allocated 
        foo(buf, 5); 
        std::cout << buf << '\n';
        
        return 0;
    }

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You cannot return a std::string without problems as has been mentioned. You also do not want to return a pointer to memory that was allocated inside of a DLL and then de-allocate or call delete on that pointer from outside of the DLL. This will corrupt the heap of the module that is attempting to the clean up because it's CRT does not know about the allocation. Your best bet is to do something like this:

    Code:
    unsigned int GetStringLength();
    void GetString(char *pBuffer,unsigned int length);
    The client calls GetStringLength() and then allocates a char array of that size. GetString() is then called by the client and the array it allocated is passed in as a param. The length is passed in as a simple buffer overrun check. Even though the DLL knows the length of the string this type of function could be used to execute a buffer overrun exploit so you will want to pass in the length. Do a memcpy for this length from the DLLs string buffer to the buffer passed to the function. Check to ensure that the length passed in matches the length of the string and only copy at max the length of the string into the buffer. This does not ensure that the array passed in is actually the correct size and the code could still crash if it is not. This will work and it is clunky but it ensures that the client allocates the memory for the resulting string and thus can correctly clean it up and it does not expose the actual string in the DLL to external clients who could then misuse this and wreak havoc. Keep in mind if you wish to return a null terminated string you must allocate an array of sufficient size to accomodate for that.

    Understand that the std::string approach will work as long as the client of the DLL and the DLL are guaranteed to use the same version of the STL. So if this is the case you may be wasting cycles solving a problem that may never happen in your program.
    Last edited by VirtualAce; 02-05-2011 at 11:00 PM.

  8. #8
    Registered User
    Join Date
    Feb 2011
    Posts
    5
    Quote Originally Posted by Bubba View Post
    You cannot return a std::string without problems as has been mentioned. You also do not want to return a pointer to memory that was allocated inside of a DLL and then de-allocate or call delete on that pointer from outside of the DLL. This will corrupt the heap of the module that is attempting to the clean up because it's CRT does not know about the allocation. Your best bet is to do something like this:

    Code:
    unsigned int GetStringLength();
    void GetString(char *pBuffer,unsigned int length);
    The client calls GetStringLength() and then allocates a char array of that size. GetString() is then called by the client and the array it allocated is passed in as a param. The length is passed in as a simple buffer overrun check. Even though the DLL knows the length of the string this type of function could be used to execute a buffer overrun exploit so you will want to pass in the length. Do a memcpy for this length from the DLLs string buffer to the buffer passed to the function. Check to ensure that the length passed in matches the length of the string and only copy at max the length of the string into the buffer. This does not ensure that the array passed in is actually the correct size and the code could still crash if it is not. This will work and it is clunky but it ensures that the client allocates the memory for the resulting string and thus can correctly clean it up and it does not expose the actual string in the DLL to external clients who could then misuse this and wreak havoc. Keep in mind if you wish to return a null terminated string you must allocate an array of sufficient size to accomodate for that.

    Understand that the std::string approach will work as long as the client of the DLL and the DLL are guaranteed to use the same version of the STL. So if this is the case you may be wasting cycles solving a problem that may never happen in your program.
    Thanks for your help but most of that is over my head, i did however find a function that converts a string to a char,

    Code:
    char *convertStringToChar(const string &str)
    {
        char *retPtr(new char[str.length() + 1]);
        copy(str.begin(), str.end(), retPtr);
        retPtr[str.length()] = '\0';
        return retPtr;
    }
    and this all seems to work, also if i add a MessageBox in the code(for debugging purposes) it does show the right "line" from the text file when calling it from VB, however in the next line when the dll actually tries to return the (now char) data back to VB, well VB crashes giving me the error, "The instruction at "xxxxxx" referenced memory at "xxxxxxx". The memory could not be read. I know i am declaring the function correct in VB as there is only one way to do so. Here is what i have, any more ideas?

    Code:
    using namespace std;
    
    
    BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved){
        return TRUE;
    }
    
    char *convertStringToChar(const string &str)
    {
        char *retPtr(new char[str.length() + 1]);
        copy(str.begin(), str.end(), retPtr);
        retPtr[str.length()] = '\0';
        return retPtr;
    }
    
    
    extern "C" __declspec( dllexport ) __stdcall  const char* sSearch(char *);
    
    extern "C" __declspec( dllexport ) __stdcall  const char* sSearch(char* strString)
    
    {
        
        char* search = strString; // search pattern
        int offset;
        string line;
        ifstream Myfile;
        Myfile.open ("c:\\example.txt");
        
        if(Myfile.is_open())
        {
            While (!Myfile.EOF())
            {
                getline(Myfile,line);
                if ((offset = line.find(search, 0)) != string::npos) {
                    //cout << "found '" << line <<endl;
                    char *dPtr(convertStringToChar(line));
                    MessageBox(NULL, dPtr, NULL, NULL);  // This (dPtr) give the correct result i have successfully converted a string to a char
                    return dPtr;   //  This crashes my VB app
                }
            }
            Myfile.close();
        }
    
        return 0;
    }
    Last edited by rm78; 02-06-2011 at 07:06 AM.

  9. #9
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    Returning a raw pointer should not cause memory access violation, something must be wrong in the main application. We have already told you not to return a pointer. Your function convertStringToChar() returns a POINTER to char, not char, I noticed you don't distinguish these two completely different things. Learn about pointers.
    This function creates a memory leak, because it allocates necessary amount of memory but does not free it (there should be an appropriate call to delete[]). The only way I see returning a pointer is creating a global buffer.

    While (!Myfile.EOF())
    What is this? What is 'While' and what is 'EOF'. It is 'while' and 'eof'. I don't even know how it can compile (with default compiler settings).

    And how the hell did you want to manage std::string with visual basic?

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    but most of that is over my head
    Then ditch the multi-DLL approach because you are not ready for it. Compile everything into the same exe via static libraries. DLLs are actually very simple to use but there are a few ground rules you must follow or should follow (depending on your usage) and this is a result of the way in which DLLs are treated in the Windows operating system.
    Last edited by VirtualAce; 02-06-2011 at 12:02 PM.

  11. #11
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,738
    Since we're talking about DLLs, can i ask something relevant?

    At a project i found on the net i discovered two small classes called Allocator and AllocatorFast. The former used virtual functions to allocate and free memory, while the latter did it directly. The docs say that the first can be used across dll boundaries while the second can't! Why's that?
    Devoted my life to programming...

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I would have to see the code to comment one way or another.

  13. #13
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,738
    Here's the file. It's from the Irrlicht SDK.
    Devoted my life to programming...

  14. #14
    Registered User
    Join Date
    Feb 2011
    Posts
    5
    After a lot of work i got it, i see what you mean about not to return a pointer now, and i changed it to a void and copied the data into a buffer using strncpy. I thought i had to specially return the data back to the app,


    Code:
    return a*b;
    kind of thing

    which isn't actually the case, or at least not for a void as a void isn't a function right, it's a routine.
    But by filling a buffer the app is actually passed the data back as long as you have set a buffer on the apps side to receive it, really quite simple.

    What i was really trying to do is comapre the speed of VB vs C++ to open a file and search for a string. Obvioulsy i am not using pure c++ but accessing it via a dll.

    C++ is hard, .NET should supercede it though. Who needs C++ anymore?? I have a theory that one day all proramming lanaguges will become extinct as soon you will just be able to speak into a microphone and some clever app will auto convert your voice into the code you wan't. Thanks again.

  15. #15
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,738
    Quote Originally Posted by rm78 View Post
    I have a theory that one day all proramming lanaguges will become extinct as soon you will just be able to speak into a microphone and some clever app will auto convert your voice into the code you wan't. Thanks again.
    Who's gonna make/debug/fix/maintain this clever app? I know!...
    Devoted my life to programming...

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C++ ini file reader problems
    By guitarist809 in forum C++ Programming
    Replies: 7
    Last Post: 09-04-2008, 06:02 AM
  2. Program using classes - keeps crashing
    By webren in forum C++ Programming
    Replies: 4
    Last Post: 09-16-2005, 03:58 PM
  3. can anyone see anything wrong with this code
    By occ0708 in forum C++ Programming
    Replies: 6
    Last Post: 12-07-2004, 12:47 PM
  4. returning pointer to string between exe and dll
    By evilpope in forum C Programming
    Replies: 2
    Last Post: 05-15-2003, 12:16 PM
  5. Something is wrong with this menu...
    By DarkViper in forum Windows Programming
    Replies: 2
    Last Post: 12-14-2002, 11:06 PM