0

i have one dll that i build in C++ and one software that i built in C# - windows forms.

When i am calling to the dll via C# i got an fatal exception:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Here is the call:

    [DllImport("dlltest.dll")]
    [return: MarshalAs(UnmanagedType.SafeArray)]
    private extern static string[] getFiles(string directory, string fileFilter, bool recrusive);

Here is the code from my DLL source:

extern "C" __declspec(dllexport) LPSAFEARRAY getFiles(string directory, std::string fileFilter, bool recrusive);

LPSAFEARRAY getFiles(string directory, std::string fileFilter, bool recrusive)
{
       std::vector<std::string> filesArray;

if (recrusive)
    getFilesByDirs(directory, fileFilter, false);

directory += "\\";

WIN32_FIND_DATAA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE;

std::string filter = directory + (recrusive ? "*" : fileFilter);

hFind = FindFirstFileA(filter.c_str(), &FindFileData);

if (hFind == INVALID_HANDLE_VALUE)
{

}
else
{
    if (!recrusive)
    {
        if(isGoodForUs(directory + std::string(FindFileData.cFileName))) 
        {
            filesArray.push_back(directory + std::string(FindFileData.cFileName));
        }
    }

    while (FindNextFileA(hFind, &FindFileData) != 0)
    {
        if (!recrusive)
        {
            if(!isGoodForUs(directory + std::string(FindFileData.cFileName))) continue;
                filesArray.push_back(directory + std::string(FindFileData.cFileName));
        }
        else
        {
            if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)>0 && FindFileData.cFileName[0]!='.')
            {
                if(!isGoodForUs(directory + std::string(FindFileData.cFileName))) continue;
                    filesArray.push_back(directory + std::string(FindFileData.cFileName));
            }
        }
    }
}

CComSafeArray<BSTR> a(filesArray.size());

std::vector<std::string>::const_iterator it;
int i = 0;
for (it = filesArray.begin(); it != filesArray.end(); ++it, ++i)
{
    a.SetAt(i, A2BSTR_EX((*it).c_str()), FALSE);
}
return a.Detach();
}

Anyone know what is the problem?

anton.burger
  • 5,637
  • 32
  • 48
  • Did you run it under the debugger to find out which line in the code is triggering the error? – Timo Geusch Sep 25 '13 at 20:58
  • @TimoGeusch Yup, when i am using the getFiles method via C#. – Eric Toorner Sep 25 '13 at 21:00
  • 1
    You can´t Marshal C++ STL strings. Stick to LPCTSTR. For the return type to use a SafeArray is ok but I am not sure if the Marshaller will recognize it. You can try to declare as return type object and later cast it to string[] so you can inspect in the debugger what the Marshaller did do to your array. – Alois Kraus Sep 25 '13 at 21:20
  • http://stackoverflow.com/questions/17099611/custom-marshaler-for-pinvoke-with-stdstring – anton.burger Sep 25 '13 at 21:34
  • 1
    Yet another case of the missing CallingConvention, it is Cdecl. – Hans Passant Sep 26 '13 at 00:57
  • I've rolled back your question to how it was originally. If you have a *different* question, please [ask in a new post](http://stackoverflow.com/questions/ask) rather than changing the original. *Clarifying* the original question is fine, but replacing it with something completely different makes all the comments and answers to the original question meaningless for anyone who reads this page later. – anton.burger Sep 27 '13 at 08:41

1 Answers1

2

The real question is why are you not using the .Net method that is the equivalent of your GetFiles() call

Directory.GetFiles Method (String path, String searchPattern, SearchOption searchOption)

You avoid degugging C++ code, the extra deployment issues and the execution penalty for switch between managed and unmanaged code.

EDIT ADDED

When you want to revert to using your C++ DLL from C# the most painless method if to create a C++ wrapper class that makes this as simple as possible. Microsoft recommends this over "platform invoke" that your were attempting to use (and ran into problems because of the std::string classes).

First, see this MSDN article for an overview of C#/C++ Interopability. Second, this articlegive a quick example of C++ class wrapping. Microsoft refers to using a C++ wrapping class at the method that "just works".

Having done lots of cross-platform programming in C, and C++ I have to wish you best of luck, it requires lots of effort to get it right. Since you are using C++ apparently as your cross platform interop layer, you would be well advised to create across-platform library for your interop code (keeping it separate from your application code). And second, create extern C interfaces to your interop as much as possible. This is really the "universal " calling format, no problems with name mangling, etc. A second question is how much interop do you need? Basic Files and Directories are not too hard. Sockets are another set of issues. Multithreading are another. I've used ACE before, it is C++ all the way, been around for a long time -- but it is a very powerful C++ interop library (among other things).

Gary Walker
  • 8,831
  • 3
  • 19
  • 41
  • My idea is to use this C++ code in anoter platforms.. of course i need to change some functions for cross platform, but the real deal is because of the usage in other platforms. Besides, i have a lot of C++ code that connected to the Main function in the DLL.. so it's to much work right now to change to C# method. – Eric Toorner Sep 25 '13 at 21:12
  • Hi @GaryWalker because of the priority and the waste of time in this project, i will use temporary a C# method for this kind of action. But now i can't get any files, i got null in the result.. You can see the code in the edited post, above ^^ – Eric Toorner Sep 26 '13 at 10:20
  • You are not calling the right method. The method that I mentioned has 3 arguments, the third allows specifying recursive descent – Gary Walker Sep 26 '13 at 14:20
  • Added some interop recommendations to my initial answer – Gary Walker Sep 26 '13 at 14:46