views:

252

answers:

3

I'm trying to access the Wine implementation of some user32 functions on Kubuntu Linux. I have the Wine 1.1.31 package installed. When try running this simple test program in MonoDevelop, I get a System.EntryPointNotFoundException.

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace PinvokeTesting
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(GetKeyState((int)Keys.A));
        }

        [DllImport("user32")]
        private static extern short GetKeyState(int vKey);
    }
}

This is the output:

Unhandled Exception: System.EntryPointNotFoundException: GetKeyState at (wrapper managed-to-native) PinvokeTesting.MainClass:GetKeyState (int) at PinvokeTesting.MainClass.Main (System.String[] args) [0x00000] in .../Main.cs:11

The function should be there, but it's not finding it. Any ideas? I've done a lot of searching, haven't found anything helpful. The documentation seems to be rather sparse on these issues (either that or I'm searching for the wrong things).

Edit: I'm not trying to use P/Invoke in combination with Winforms, there are some other functions in Wine I need to P/Invoke to. I'm just trying to get Mono P/Invoke to Wine working.

+3  A: 

If you are trying to do this in combination with the managed System.Windows.Forms implementation in Mono on Linux, then I'm fairly certain that pinvoking Wine is not going to help you. SWF is implemented entirely differently/separately from Wine, and the two do not "mix" or in any way interact.

I suggest you find another way to achieve what you're trying to do.

Pete
+1  A: 

There is a simple moral to the story here as you have discovered...if there's pinvokes used, do not assume the code to be cross-platform portable and wine compatible! The only thing that you could work around on this would be something like this:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace PinvokeTesting
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(GetKeyState((int)Keys.A));
        }

#ifdef WIN32API_NT_5
        [DllImport("user32")]
        private static extern short GetKeyState(int vKey);
#else
        private static extern short GetKeyState(int vKey);
#endif
    }
}

And create some kind of a wrapper to substitute for the Win32API pinvoke signature. Just because it references System.Windows.Forms does not mean that WIN32API pinvokes will work under Wine, as the various underlying interfaces in terms of GUI is different and not guaranteed to be portable.

Then define the switch 'WIN32API_NT_5' or whatever you wish to choose of your own accord if you want to make this cross-platform friendly.

Hope this helps, Best regards, Tom.

tommieb75
+2  A: 

The wine libs are completely incompatible with mono. If you need to use wine libs on Linux, you need to get the windows version of mono and run it under wine.

This has nothing to do with Winforms specifically, it holds true for any wine library.

As for the actual solution to your problem:

  • Don't use the #ifdef WIN32API_NT_5 trick that has been suggested, instead use runtime detection and invoke one method or the other depending if you're running under windows or under different operating systems: having a single binary is worth the 1-cycle runtime penalty (moreoer if you store the operating system flag in a static readnly field mono will optimize the check away for you).
  • You need to cope with different operating system models if you want your code to be portable, because it's not always possible to implement ant call from one system in another one in a simple or fully compatible way. For example in the GetKeyState() case you might need to hookup keyboard events and record the press/release state yourself.
  • Consider different ways to do the same thing, for example, are your sure that the standard Console class in mscorlib doesn't provide the functuonality you need in your program?
lupus
Thanks for the explanation. The only reason I was trying to invoke `GetKeyState` is because I was trying to get *any* function in Wine actually P/Invoked from Mono, not because I actually wanted to use it. The funny thing is, apparently Mono was able to load `libuser32.so` but wasn't able to load the function. IIRC, about two years ago I was able to get P/Invoke to the Wine libraries working. (Perhaps the problem is something with my current setup now?) Anyway, I think I'll pursue an alternate route to Wine. (Besides, then Wine won't be a dependency for my program).
Zach Johnson
Mono has the ability to redirect P/Invoke calls and specifically it automatically redirects user32.dll calls to an helper shared library, which right now contains few if any working functions. That is why the dll appears to be loaded but the symbol is not found.For some cases you might be able to load a symbol from a wine dll, it's just that using it may not work or it may crash the runtime or make other things not work. It's not supported and likely will never be, so don't assume that path can be followed for any program that is not a late night experiment.
lupus