C++ Calling Dynamicaly loaded function from DLL, destruction error?

Hi, I have two projects (Projects A and B). Project A is a dll project, defining a function called "regex".

Project B dynamically loads this DLL, and calls Project A's "regex" function via LoadLibrary/GetProcAddress.

Regex takes a pointer to an std::vector (std::vector<std::cmatch>).

When debugging ProjectB, I can see that, within the code from ProjectA (in the "regex" call), a loop that loops through the elements of the vector outputs all the elements in the vector to console as expected. But the loop in ProjectB ( which executes after ProjectA), which also loops through the vector, and, is supposed to output the elements of the vector, outputs empty strings, not, as I would expect, the same strings (which contain results), as in the loop in Project A.

How is this happening. Does this have anything to do with it being a DLL, and, maybe, somehow values/memory addresses (or something similar) of the vector/its elements being destructed across the Projects/Dlls? Does anyone have any idea how to get rid of this?

Thanks! :)

C:)

Output and Code See Below:

Output Loop in A:

1
2
Un
Un


Output Loop in B: (empty) (i.e. none)


Project A DLL Header (interface.h):

1
2
3
4
5
6
7
#include "stdafx.h"
#include <vector>
#include <regex>
extern "C" {__declspec(dllexport) int __cdecl regex(std::string target,std::string rgx, std::vector<std::cmatch*>* matches);}






Project A DLL cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// regex.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "interface.h"
#include "windows.h"
#include <iostream>

extern "C" {
	__declspec(dllexport) int __cdecl regex(std::string target,std::string rgx, std::vector<std::cmatch*>* matches)
{
		// this can be done using raw string literals:
// const char *reg_esp = R"([ ,.\t\n;:])";
	 std::regex rgxa = std::regex(rgx);
	 const char *targetA = target.c_str();
	 for(auto it = std::cregex_iterator(targetA, targetA + std::strlen(targetA), rgxa);
             it != std::cregex_iterator();
           ++it)
    {
        std::cmatch match = *it;
		matches->push_back(&match); .
        std::cout << match.str() << '\n';
    }
//Output of loop:
// Un
// Un
//output End
	return EXIT_SUCCESS;

}
}






Project B calling code:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HINSTANCE hGetProcIDDLL = LoadLibrary(L"C:\\Users\\a\\Documents\\Visual Studio 2012\\Projects\\regex\\Debug\\regex.dll"); //<--- load DLL (works)

	FARPROC lpfnGetProcessID = GetProcAddress(HMODULE (hGetProcIDDLL),"regex");//<--- Get proc id, works

	typedef int (__cdecl * pICFUNCRegex)(std::string, std::string, std::vector<std::cmatch*>*);

	pICFUNCRegex regexP; 
    regexP = pICFUNCRegex(lpfnGetProcessID);

	std::vector<std::cmatch*>* matches = new std::vector<std::cmatch*>;
	intMyReturnVal = regexP("Unseen University", "Un", matches);
for(int i = 0; i < matches->size(); i++)
	{
		std::cout << matches->at(i)->str() << std::endl;
	}
//output of loop: empty


Last edited on
At least in some point each DLL and the binary each had their own heaps. That did mean that memory allocated by A.dll can only be released by A.dll and memory allocated by B.exe can only be released by B.exe.

Enter vector. Allocated by B and referred to via pointer in A. That seems ok.

matches->push_back(). vector allocates memory internally, and re-allocates when need increases. Who has each piece? This I would suspect first.

I sort of got around some of that by defining Allocators that hopefully kept all of container in B.exe.


PS. "explicitly loaded library" describes partly what you do. "Normal programs" have the runtime linker to "implicitly" load necessary libraries and map functions at startup. They fail if the libraries are missing. "Explicit" loading is postponed and can handle lack of library dynamically, but also has to map each function, explicitly.
Last edited on
Can you clarify what you mean with "who has each piece"? Thanks :)

btw, I have tried the whole thing with std::vector<int*>* (i.e. vector pointer of int pointers, instead of std::vector<std::cmatch*>*), to try if the ints are still available in Project B if they are populated in Project A, and that works :s


hmm: I have just found out that, actually, using,

std::cout << match.str() << '\n';





, i.e. accessing a match individually and DIRECTLY and printing its string works, but, using




std::cout << matches->at(i)->str() << std::endl;


to print out the match from WITHIN a vector doesn't work, even if I'm within Project A (i.e. the project that generates the match and populates the vector). how can this be the case???
Last edited on
Each piece? Assuming vector<Foo> is like
1
2
3
4
class VFoo {
  size_t count;
  Foo * data;
};

The integer and the pointer obviously are allocated where the vector is instantiated (heap of B.exe for you).
The memory pointed to by 'data' is in some heap too, but one does not know when and where vector (re)allocates -- unless one uses resize() or reserve().

Having the 'Foo' to be a pointer type, like 'cmatch*', the memory (in unknown location) pointed by 'data' contains simple pointers. Those pointers, however, could (and should) point to some memory locations (somewhere).

Bits and pieces all over. :)

Or not:
1
2
3
4
{
  std::cmatch match = *it;
  matches->push_back(&match);
}

The memory location of 'match' is in the stack of that scope, and bound to vanish as you leave the scope. A classic:
1
2
3
4
int * foo() {
  int a = 42;
  return &a;
}

Therefore, you might not actually have any DLL-memory management issue.
Last edited on
yep, that seems as if it is true. Additionally, the fact that I (which I also described above) can not print the string of a match via

matches->at(i)->str()



outside of

1
2
3
4
5
6
7
8
9
10
11

	 for(auto it = std::cregex_iterator(targetA, targetA + std::strlen(targetA), rgxa);
             it != std::cregex_iterator();
           ++it)
    {
        std::cmatch match = *it;
		matches->push_back(&match); .
        std::cout << match.str() << '\n';
    }



also indicates this issue. I was able to solve this, by declaring

std::cmatch match

outside of the for-loop (as following):

1
2
3
4
5
6
7
8
9
10
11
12
13

	 std::regex_iterator<const char*> it;
	 

	 std::cmatch match;
	 for(it = std::cregex_iterator(targetA, targetA + std::strlen(targetA), rgxa);
             it != std::cregex_iterator();
           ++it)
    {
        match = *it;
		matches->push_back(&match); //it
		
    }


Now I can also access


matches->at(i)->str


outside that loop, BUT, only within project A. As soon as I'm in Project B, all the std::cmatch objects in my std::vector<std::cmatch*>* object pointer become "empty" (i.e. the std::cmatch objects are still there, but they become (quote from Visual Studio 2012 debugger)"empty"),... Is it a DLL memory issue in the end?


Last edited on
Not yet. You are still using a local (stack) variable within the scope of function regex().
1
2
3
4
 for ... {
  std::cmatch * match = new std::cmatch( *it );
  matches->push_back( match );
}

Now you definitely would have objects in the heap of A.dll. They exist, until deleted. Either B.exe cannot reference them, it will be error to delete by B.exe, or all will be fine.
Hm, ok, so, now, I have coded as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	 std::regex_iterator<const char*> it;

	 std::cmatch *match = NULL;
	 for(it = std::cregex_iterator(targetA, targetA + std::strlen(targetA), rgxa);
             it != std::cregex_iterator();
           ++it)
    {
        match = new std::cmatch(*it);
		matches->push_back(match); //it

		//std::cout << match->str() << '\n';
    }




And I can now see the std::cmatch objects in ProjectB, and they are NOT empty (i.e. they have a "size", etc.). BUT, printing the string (from within ProjectB, i.e. NOT the DLL) via

std::cout << matches->at(i)->str() << std::endl;

produced output:

1
2
3
4
▌▌                         
▌▌                                       
▌▌                          
▌▌                                      


is this some kind of encoding issue between the dll and exe, or so? :)

Thanks for all your explanations so far, by the way! Great job!! :)
Last edited on
The obvious test is to copy the regex() function code to B.exe. If it does print sensibly when local, ...
hmmm.... not expected: doesn't print locally either. let's see..... ^^
Topic archived. No new replies allowed.