简体   繁体   中英

C# DllImport trouble

My question is a little general, so i'm not looking for an exact answer, but possibly some directions to look into that will help me...

At my work place I program mostly in C#. We have this 3rd party company we work with, that gave us a Native C++ dll that we need to use. Since the C++ method I needed wasn't exposed in a manner that was easy to reference from C#, I wrapped the dll in another Native C++ Dll.

So now i have 2 Native C++ dlls, one wrapping the other.

I created a small C# console application that calls the method I created in C++. My method signature looks like this :

[DllImport("HashMethodWrapper.dll")]
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string CreateHash(
            string input,
            [MarshalAs(UnmanagedType.LPStr)]StringBuilder output);

In my console application, everything works fine, and i always receive the string im expecting in the result.

But when I move it to a web service or a Web Application i created (since this is where i really need it), I see that the string im receiving is garbage and not even consistent. It seems as if im getting just some reference to memory that is lost or something like that, but this is only a guess of mine...

I don't know why this happens, since in my console application everything works fine.

Does anyone have a direction that might help me ???...

Thanks in advance, gillyb

Edit : I thought it might have to do with some unpinned objects, so i tried calling the method in a fixed statement, something like :

unsafe public static string CreateHashWrap(string pass)
{
    String bb;
    StringBuilder outPass = new StringBuilder();
    fixed (char* resultStr = CreateHash(pass, outPass))
    {
        bb = new String(resultStr);
    }
    return bb;
}

...but this still didn't do it for me. Is this the right way to pin objects ?

2nd Edit : The method signature in C++ looks like this :

extern "C" __declspec(dllexport) char *CreateRsaHash(char *inputPass, char *hashPass);

3rd Edit : I changed the signature of the method to be

extern "C" __declspec(dllexport) bool CreateRsaHash(char *inputPass, char *hashPass);

and the return value im looking for is placed in the *hashPass parameter.

Now, I created a simple Console application to test it. When insert the DllImport in my main class, and directly call the method everything works great, but when I move the DllImport and wrap the method in a different class and call that class from the Console 'Main' method, I get a StackOverflow exception!

Anyone got any ideas why this is happening ??

在将StringBuilder传递给interop方法之前,请尝试指定它的容量

It's really hard to know from the sparse information but if I had to guess I would say you need to make sure you're pinning the output object. Also I would probably change the output parameter to some other type, it seems pretty strange that StringBuilder works at all frankly.

I do know that if you allocate an object, it will get a pointer but that doesn't mean that it won't move. So if you try to pass a pointer to a managed object into an unmanaged environment you need to make sure you tell the GC to "pin" the memory so it doesn't get moved out from under you.

Here is a really rough version of what I mean by pinning:

string input = "...";
StringBuilder output = new StringBuilder();
var handle = System.Runtime.InteropServices.GCHandle.Alloc(output, GCHandleType.Pinned);
try
{
    CreateHash(input, output);
}
finally
{
    handle.Free();
}

I would consider to warp inside a C# shared assembly/dll instead of a c++ dll and then try to get your console application to work with the dll. It is good practice to wrap external dependencies this way anyway.
Otherwise some traditional issues are 32 vs 64 bit, the load path to the shared library. Is it really only a string or something more complex?

I found the solution to my problem, and now i feel kinda (if not really!) stupid... :-|

I used the LoadLibrary() method in C++ to dynamically invoke a method from the other native dll. The problem was that I didn't give the method any path, and just the dll filename. In .net, it would've searched in the current folder, but seems like in native code this doesn't work this way.

The bigger problem in my programming practices is obviously the fact that i didn't fully cover error handling in my native C++ dll!

All the asnwers I received on this page weren't for nothing though...

Once I found out that i had problem with the directory path, I ran into different exceptions about trying to access corrupt memory, etc. And then I needed to create pinned objects, and declare a size for my StringBuilder object.

Thanks to everyone for your help!!

:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM