-1

I do have a simple test-case here (C++) which does LoadLibrary, GetProcAddress & calls the function (ProcAdd). The signature is "char* ProcAdd(char*)".

Now I do get the string correctly in the executable, but once I do "FreeLibrary", it's gone... (obviously because I just did a return "hello").

The thing is, that I have another dll (.NET, C#) where the signature is "[return: MarshalAs LPSTR]string ProcAdd([MarshalAs LPSTR] string)". Now this function ALSO returns a string, but when I do "FreeLibrary", the string is still accessible within my executable?!

How does that come, and how could I mimic the same behaviour? (and yes, I know I can store it in another variable, but I would like to understand what is happening and how I can reproduce this .NET behaviour).

Thanks a lot!

As requested the code:

C++ exe:

int main( void ) 
{ 
    HINSTANCE hinstLib; 
    MYPROC ProcAdd; 
    BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; 
    void * val = NULL;

    // Get a handle to the DLL module.

   //  hinstLib = LoadLibrary(TEXT("C:\\Users\\steven\\temp\\myMyMy.orig.dll")); 
    hinstLib = LoadLibrary(TEXT("C:\\Users\\steven\\temp\\myMyMy.proxy.dll")); 

    // If the handle is valid, try to get the function address.

    if (hinstLib != NULL) 
    { 
        ProcAdd = (MYPROC) GetProcAddress(hinstLib, "ProcAdd"); 

        // If the function address is valid, call the function.

        if (NULL != ProcAdd) 
        {
            fRunTimeLinkSuccess = TRUE;
            val = (ProcAdd) ("0987654321"); 
        }
        // Free the DLL module.

        fFreeResult = FreeLibrary(hinstLib); 
    } 

    // If unable to call the DLL function, use an alternative.
    if (! fRunTimeLinkSuccess) 
        printf("Message printed from executable\n"); 

    return 0;

}

C++ dll:

    #include <Windows.h>
    #include <iostream>
    #include <fstream>

    static const char tmp[] = "hello";

    extern "C" const char * __stdcall ProcAdd(const char * param1) {
        FILE * fp = fopen("C:\\tmp\\ProcAdd.txt", "a");
        if ( fp ) {
            fprintf(fp, "param1: '%s'\r\n", param1);
            fclose(fp);
        }

//      return strdup("hello");
//      return "hello";
        return tmp;
    }

C# dll:

[return: MarshalAs(UnmanagedType.LPStr)]
public static string ProcAdd([MarshalAs(UnmanagedType.LPStr)] string param1)
{
    string str;
    try
    {
        str = new WebClient().DownloadString("http://www.salvania.be/test.php?param1=" + param1);
    }
    catch (Exception exception1)
    {
        str = "Error-DLL";
    }
    return str;
}

Working return:

  // http://stackoverflow.com/questions/14406818/heapcreate-and-heapalloc-confuse
  HANDLE winHandle = HeapCreate( 0, sizeof(tmp), sizeof(tmp) );
  char* s = (char*)HeapAlloc( winHandle, 0, sizeof(tmp) + 1 ); 
  strcpy((char*)s, tmp);
  s[sizeof(tmp)] = 0;
Steven Van Ingelgem
  • 872
  • 2
  • 9
  • 25
  • CODE! We need moar CODE! BTW, strings are not stored in variables. They are stored in memory. Where exactly? We don't know, we have no code. – Ivan Aksamentov - Drop Sep 27 '14 at 17:41
  • It is not the kind of function signature that ever makes anybody happy. You are probably returning a string literal, a const char*, it's gonzo after the DLL gets unloaded. Your C# declaration is not valid, the pinvoke marshaller will try to release the string buffer. Silent failure on XP, crash on later Windows versions. But sure, it survived because the marshaller copied the string. Do consider improving the function, int ProcAdd(char* input, char* output, size_t outputLength). Now it is copied and free of memory management hassles. – Hans Passant Sep 27 '14 at 17:42
  • @HansPassant: I can't improve on the C# dll, but I can improve/extend it by proxying functions. That's what I'm trying to do. Anyway I'm trying to understand first what is happening, and why it disappears when my own C++ dll is unloaded, but not when the .NET assembly is unloaded. – Steven Van Ingelgem Sep 27 '14 at 17:59

1 Answers1

1

If you just return "hello", that hello string may come from the data segment of the shared library, and may be unloaded after the life-time of the library.

To ensure the returned string live after the life-time of the library, you may store it on heap or otherwise provide a buffer from the caller.

Non-maskable Interrupt
  • 3,841
  • 1
  • 19
  • 26
  • It works fine (Edited the initial post with my result). But that memory will never be freed then right? – Steven Van Ingelgem Sep 27 '14 at 18:10
  • @StevenVanIngelgem - If you are going down the road of having the DLL provide the buffer, then you need to have a more sophisticated approach. Just returning `strdup` isn't a good idea, since someone has to call `free`. C# has no idea what `free` is. – PaulMcKenzie Sep 27 '14 at 18:12
  • @StevenVanIngelgem - You can document that the string *is* part of the DLL. The DLL unloads, the string is gone. No different than a local variable losing scope. So the string should be copied once retrieved to another buffer if the user wants to retain the value. – PaulMcKenzie Sep 27 '14 at 18:18