2

have been working on this for hours, couldn't get it work :(

below code gives exception "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." this is fixed, see below update

but when i change the return type to int and return a int value, it works. how to make it to return a float value? thanks.

vs2012

c++ code

extern "C"  __declspec(dllexport)
float compute_similarity()
{       
    return 1.01;
}

c# code

[DllImport("demo.exe", EntryPoint = "compute_similarity", CallingConvention =  CallingConvention.Cdecl)]
public static extern float compute_similarity();

public void Get()
{
  float x = compute_similarity(); // here returns random value
}

=====================================

UPDATE

See comments from David below, the problem was the c# project is targeting x64 and c++ is targeting Win32. After changing the c# project to target to x86, the exception went away.

However the call from c# returns some random value instead of 1.01 expected

Xin
  • 575
  • 4
  • 20
  • 2
    Are you on a 64 bit machine? I'm just wondering whether the C code returns a 64 bit "float" (ie a double for the C# code) if it is a 64 bit environment. – David Arno Oct 08 '13 at 08:18
  • 2
    Does it work if you explicitly add `__cdecl` between `float` and `compute_similarity` in C++? – dtb Oct 08 '13 at 08:21
  • @DavidArno thanks a lot David, yes c# project is targeting x64 and c++ is targeting Win32, after changing c# project to target x86, it made the exception go away although it still returns some random value instead of 1.01 expected. Updated the question. – Xin Oct 08 '13 at 08:30
  • 2
    Shouldn't you be using `CallingConvention.StdCall`? – Matthew Watson Oct 08 '13 at 08:38
  • 2
    When you say random, is it near what you expected or something else? Example return values would help. – OMGtechy Oct 08 '13 at 08:39
  • That should work fine (but demo should be a .dll, not an .exe). Or post your project somewhere so we can dig more. – Simon Mourier Oct 08 '13 at 08:56
  • @JoshuaGerrard most of time returns 0.0, sometimes return values like -6.4284847310593848E-40. thx – Xin Oct 08 '13 at 09:15
  • 1
    Try making the return value const and adding f to the end like so: float compute_similarity() const { return 1.01f; } – OMGtechy Oct 08 '13 at 09:52
  • 1
    @MatthewWatson Default C++ calling convention is `cdecl`. So, not the C# should not be using `stdcall`. – David Heffernan Oct 08 '13 at 10:35
  • 1
    @DavidHeffernan True - but that's the *default*, and perhaps the C++ module is compiled with `/Gz`. Maybe the safest thing to do when exporting C/C++ methods is to explicitly mark them with `__cdecl` or `__stdcall`. – Matthew Watson Oct 08 '13 at 11:00
  • 1
    @Xin What happens if you change the `C` declaration to include `extern "C" __declspec(dllexport) __stdcall` and change the PInvoke to specify `CallingConvention.StdCall` ? – Matthew Watson Oct 08 '13 at 11:03
  • 1
    @MatthewWatson Odds are very high that the C++ module is not compiled with `/Gz`. Yes, explicitly stating calling convention is best. But suggesting `stdcall` is probably not helpful. Also, for a function with no parameters, `stdcall` and `cdecl` are the same. – David Heffernan Oct 08 '13 at 11:03
  • 1
    @DavidHeffernan Very true. It's an odd thing - the OP's code should work. I don't suppose there's any way that the C++ compiler is enregistering the return value? That would have an untoward effect. I'm pretty sure that's not default behaviour though (and is incompatible with DLLExport in any case). – Matthew Watson Oct 08 '13 at 11:13
  • 1
    @MatthewWatson The only error I can see is the use of an exe rather than a dll – David Heffernan Oct 08 '13 at 11:20

1 Answers1

3

I think your problem is that your function is declared in an executable rather than a DLL. Convert your native code into a library project and compile a DLL.

Your code will result in a call to LoadLibrary passing the file name demo.exe. The documentation for LoadLibrary says:

LoadLibrary can also be used to load other executable modules. For example, the function can specify an .exe file to get a handle that can be used in FindResource or LoadResource. However, do not use LoadLibrary to run an .exe file. Instead, use the CreateProcess function.

And so your code is doing exactly what you are told not to do. The call to LoadLibrary will succeed. The subsequent calls to GetProcAddress will succeed. But the module that you loaded is not fit to execute code.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • It seems plausible, but on the other hand Microsoft say that it's ok to P/Invoke into exes (see the response from a Microsoft employee [here](http://social.msdn.microsoft.com/Forums/vstudio/en-US/e5eab264-6ce8-4c60-ac6d-2b4cf37a5a8c/pinvoke-into-hosting-executable?forum=clr) - still, it seems worth a try! – Matthew Watson Oct 08 '13 at 11:23
  • 1
    @MatthewWatson It's fine if the module is already loaded. But the code in the question will call `LoadLibrary` on the executable. That is documented as not working: *LoadLibrary can also be used to load other executable modules. For example, the function can specify an .exe file to get a handle that can be used in FindResource or LoadResource. However, do not use LoadLibrary to run an .exe file. Instead, use the CreateProcess function.* – David Heffernan Oct 08 '13 at 11:25
  • @DavidHeffernan, MatthewWaston thanks for all your inputs. changing it to dll output did fix it. although not sure why this made a difference. – Xin Oct 08 '13 at 11:31
  • 1
    @Xin It makes a difference because calling `LoadLibrary` on an executable file does not return a module handle that is valid to execute code. That's documented by the italicised quote in my comment above that is extracted from the MSDN docs for `LoadLibrary`. – David Heffernan Oct 08 '13 at 11:32