Page 1 of 3

P/Invoke

PostPosted: Fri Dec 31, 2010 9:22 pm
by torial
I can't remember if this has come up, couldn't find any references in any of the docs or forum threads.

Is P/Invoke supported w/ Cobra? If so, what is the syntax?

Thanks! (And Happy New Year!)

Re: P/Invoke

PostPosted: Sat Jan 01, 2011 9:49 am
by Charles
You can find the syntax for attributes in the test cases at:

http://cobra-language.com/trac/cobra/browser/cobra/trunk/Tests/320-misc-two/800-attributes

Re: P/Invoke

PostPosted: Sat Jan 01, 2011 11:07 am
by torial
I'm not seeing anything that relates to P/Invoke. Is there a particular attribute you are thinking of that allows for P/Invoke calls?

Re: P/Invoke

PostPosted: Sat Jan 01, 2011 12:13 pm
by Charles
Maybe I don't realize what you're trying to do. When I search for "C# P/Invoke" I hit this page which uses a DllImport attribute:

http://msdn.microsoft.com/en-us/magazine/cc164123.aspx

This page uses the same attribute:

http://www.codeproject.com/KB/cs/essentialpinvoke.aspx

Is there something else you want to do? If so, please provide an example or more specifics.

Re: P/Invoke

PostPosted: Sat Jan 01, 2011 4:20 pm
by torial
Yeah, the DLLImport attribute is part of it. I was thinking of the "extern" keyword used in C#. Is anything like that required for making the p/invoke call work?

Here's a sample P/Invoke (http://www.pinvoke.net/default.aspx/ker ... ndNextFile)

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA
lpFindFileData);

Would the conversion be:
def FindNextFile(hFindFile as IntPtr, lpFindFileData as out WIN32_FIND_DATA) as bool is shared
has DLLImport("kernel32.dll", charSet=CharSet.Auto)


I see a reference to an "extern" keyword, but it seems to be for a replacement of "fake" which doesn't seem for this purpose: http://cobra-language.com/trac/cobra/changeset/1654

Re: P/Invoke

PostPosted: Sun Jan 02, 2011 3:15 am
by Charles
The conversion looks good to me except that (a) I'm not familiar with WIN32_FIND_DATA and (b) I just realized these are bodiless methods in C# obviously since they are external--Cobra does not currently support that. Doh!

The "extern" is so that if you include a C# class like so:
Code: Select all
cobra -c -d Foo.cobra Bar.cs

You can tell the Cobra compiler what the C# class looks like. It's a rarely used feature since typically C# code would just get compiled to a DLL that you would use.

So you have 2 options today:

1)
Do your P/Invoke work in C# and compile to a DLL when your Cobra code @ref's.

2)
Do your P/Invoke work in C# and pass the file to the Cobra compiler, with a mirror "is extern" declaration somewhere in the Cobra source code.

I would lean towards (1).

Also, I've be able to avoid P/Invoke most of the time because I find someone has already made a wrapper, or provided some kind of library with equivalent functionality either in the .NET standard libs, or 3rd party [open source].

HTH

Re: P/Invoke

PostPosted: Sun Jan 02, 2011 2:50 pm
by torial
Is there a way I could trick it using the "sharp" keyword, like:

sharp"extern" def fxnName(a as B, c as D) ?

I've been doing a hobby project over New Year's vacation, and tried to keep as much in Cobra as possible (even ported a Boyer-Moore class from C# to Cobra).

Or perhaps even do the full <DLLImport....> static extern ... , within the sharp"..." syntax? I haven't played with it so don't know how flexible it is.

Re: P/Invoke

PostPosted: Sun Jan 02, 2011 6:01 pm
by Charles
Sorry, sharp'foo' only works where expressions and statements are expected.

Btw what native libs are you calling out to?

Re: P/Invoke

PostPosted: Sun Jan 02, 2011 7:18 pm
by torial
I've got two problems I wish to solve using the P/Invoke strategy:

a) Get a Windows Shell Context Menu options ( as described at http://www.codeproject.com/KB/cs/shellContextMenu.aspx ). In particular, I wish to right click on a file and show the same (or close to the same) context menu that someone using Windows Explorer would see.

b) Find a faster way to scan through directories (research on the internet indicates that P/Invoke is about 10x faster than Directory.GetFiles and the folder equivalent): http://stackoverflow.com/questions/7241 ... ely-in-net. For b) I also have a command switch option I'll experiment w/ for performance.

I've attached a screesnhot of what I've got so far...

Re: P/Invoke

PostPosted: Sun Jan 02, 2011 10:25 pm
by hopscc
Torial
SFAICT theres not very much stopping this from working in cobra

Heres cobra code for a simple Pinvoke declaration and invocation.
You need specify both of shared and extern and have 'pass' as the body
use System.Runtime.InteropServices

class PInvokeTst

#PInvoke entry point
def messageBeep(beepType as uint32) as bool is shared, extern
has DllImport("User32.dll", setLastError = true)
pass

# PInvoke entry point
def beep(dur as uint32, freg as uint32) as bool is shared, extern
has DllImport("kernel32.dll", setLastError = true)
pass

def main is shared
print .messageBeep(0)
print .messageBeep(16)

print .beep(750, 300)


It doesnt compile because of a check for return types thats not cogniscent of extern on Members. ( i.e wot chuck said)
pinvoke.cobra(5): error: Missing return statement for method "messageBeep" which returns bool.
pinvoke.cobra(9): error: Missing return statement for method "beep" which returns bool.
Compilation failed - 2 errors, 0 warnings

If you get past that,
It fails in the C# compilation phase cos the cobra code gen is adding an empty block to the extern defn (basically same issue as previously expressed in a different place).
c:\home\hops\src\cobra\Tst\regexp\pinvoke.cobra(6): error: "PInvokeTst.MessageBeep(uint)" cannot be extern and declare a body (C#)
c:\home\hops\src\cobra\Tst\regexp\pinvoke.cobra(10): error: "PInvokeTst.Beep(uint, uint)" cannot be extern and declare a body (C#)
Compilation failed - 2 errors, 0 warnings

In spite of 'extern' not being implemented for this exact purpose it does find its way through to the generated C# code and does the desired thing.

Heres the patches to the compiler that allow this to compile.
Code: Select all

Index: Source/Members.cobra
===================================================================
--- Source/Members.cobra        (revision 2474)
+++ Source/Members.cobra        (working copy)
@@ -701,6 +701,9 @@

        get statements from _stmts

+       get isExtern as bool
+               return 'extern' in _isNames
+
        def findLocal(name as String) as AbstractLocalVar?
                # TODO: should this use a dictionary lookup?
                for local in _locals, if local.name==name, return local
@@ -1204,7 +1207,7 @@
                if _implementsTypeNode
                        _implementsType = _implementsTypeNode.realType
                        # TODO: make sure the type is among the interfaces of the box
-               if .compiler.errors.count==numErrors and _returnType not in [.compiler.voidType to IType, .compiler.passThroughType to IType] and not .hasReturnStmt and not .hasYieldStmt and not .hasThrowStmt and not .isAbstract and not .parentBox.isExtern and not .parentBox inherits Interface
+               if .compiler.errors.count==numErrors and _returnType not in [.compiler.voidType to IType, .compiler.passThroughType to IType] and not .hasReturnStmt and not .hasYieldStmt and not .hasThrowStmt and not .isAbstract and not .parentBox.isExtern and not .parentBox inherits Interface and not .isExtern
                        .throwError('Missing return statement for method "[_name]" which returns [_returnType.name].')
                # check for `return` and `yield` in the same method
                if .resultType is not .compiler.voidType
Index: Source/BackEndClr/SharpGenerator.cobra
===================================================================
--- Source/BackEndClr/SharpGenerator.cobra      (revision 2474)
+++ Source/BackEndClr/SharpGenerator.cobra      (working copy)
@@ -1572,6 +1572,9 @@
                pass

        def writeSharpImp(sw as CurlyWriter, skipFirst as bool)
+               if .isExtern
+                       sw.write(';\n')
+                       return
                .compiler.codeMemberStack.push(this)
                try
                        sw.writeAndIndent('{\n')

This does not cause any new failures in the existing tests

YMMV with this, Theres possibly additional tweaks needed for some entrypoints (e.g finding the WIN32_FIND_DATA value mentioned above, ...)
and I've not tried anything with explicit out parameters...

Now returning to your regularly scheduled program :|