uFFI ExternalAddress challenges

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

uFFI ExternalAddress challenges

eftomi
Dear all,

I'm struggling with passing an ExternalAddress to a c function call with uFFI. Firstly, I get the ExternalAddress from this method:

myFFI>>create: aString
    ^ self
        ffiCall: #(void * CreateObject (String aString) ) 

[in c:
void* CreateObject(char* szProgId)]

The method that uses the resulting ExternalAddress is defined as:

myFFI>>ask: anExternalAddress
    ^ self
        ffiCall: #(void CallMethod (void * anExternalAddress) )

[in c:
void CallMethod(void* myObj)]

In playground I have

| w |
w := myFFI create: 'Word.Application'.
myFFI ask: w .

w gets a "nice", properly looking external address, however the last line crushes Pharo, its window gets closed.

I went through the uFFI book, however I cannot find the answer.

I'm on Windows 10 x64, Pharo 7.0.4 32-bit.

Best wishes,
Tomaz
Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

Guillermo Polito
Hi Thomas, 

Can you share more about the definition of your CallMethod function in C?
From the uFFI point of view the binding looks ok…

How are you opening Pharo? From the command line or the pharo launcher?
Are you on cygwin/mingw? One other possibility (since you’re playing with C libraries) is to launch Pharo inside a gdb/lldb so you can check the reason behind the error when it crashes.

Keep us posted,
Guille

El 10 sept 2019, a las 19:53, Tomaž Turk <[hidden email]> escribió:

Dear all,

I'm struggling with passing an ExternalAddress to a c function call with uFFI. Firstly, I get the ExternalAddress from this method:

myFFI>>create: aString
    ^ self 
        ffiCall: #(void * CreateObject (String aString) ) 

[in c:
void* CreateObject(char* szProgId)]

The method that uses the resulting ExternalAddress is defined as:

myFFI>>ask: anExternalAddress
    ^ self 
        ffiCall: #(void CallMethod (void * anExternalAddress) )

[in c:
void CallMethod(void* myObj)]

In playground I have

| w |
w := myFFI create: 'Word.Application'.
myFFI ask: w .

w gets a "nice", properly looking external address, however the last line crushes Pharo, its window gets closed.

I went through the uFFI book, however I cannot find the answer.

I'm on Windows 10 x64, Pharo 7.0.4 32-bit.

Best wishes,
Tomaz

Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

eftomi
Hi, thanks for your quick reply. I found the culprint while writing this email :-)

Generally, what I'm trying to do is to make a very basic package to create and communicate with COM components on Windows, mostly as an exercise :-) I found this library: http://disphelper.sourceforge.net/ as a great start, so you don't have to fiddle with OLE/COM headaches.

The problematic function in C is defined as:

__declspec(dllexport) void CallMethod(void* myObj) {
    ...
}

It takes a pointer to a COM object, which was previously returned by 

__declspec(dllexport) void* CreateObject(char* szProgId) {
...
   IDispatch * myObj = NULL
...
   return &myObj;
}

So, CreateObject() creates COM object and returns a pointer to it, CallMethod() takes this pointer and tries to communicate with it. 

My error was in returning a pointer to a pointer ... since IDispatch * myObj = NULL was actually hidden in a macro and I missed its true definition, huh. 

I'm running Pharo from PharoLauncher directly on Win10, without cygwin/mingw. For DLL I use Visual Studio 2019. I was not sure whether the pointer survives from one FFI call to another, or how Pharo loads/unloads DLLs - but it works as expected.

Thanks again and best wishes,
Tomaz

------ Original Message ------
From: "Guillermo Polito" <[hidden email]>
To: "Tomaž Turk" <[hidden email]>; "Any question about pharo is welcome" <[hidden email]>
Sent: 11. 09. 2019 10:29:34
Subject: Re: [Pharo-users] uFFI ExternalAddress challenges

Hi Thomas, 

Can you share more about the definition of your CallMethod function in C?
From the uFFI point of view the binding looks ok…

How are you opening Pharo? From the command line or the pharo launcher?
Are you on cygwin/mingw? One other possibility (since you’re playing with C libraries) is to launch Pharo inside a gdb/lldb so you can check the reason behind the error when it crashes.

Keep us posted,
Guille

Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

Guillermo Polito


El 11 sept 2019, a las 11:07, Tomaž Turk <[hidden email]> escribió:

Hi, thanks for your quick reply.

no problem ;)

I found the culprint while writing this email :-)

Cool!


Generally, what I'm trying to do is to make a very basic package to create and communicate with COM components on Windows, mostly as an exercise :-) I found this library: http://disphelper.sourceforge.net/ as a great start, so you don't have to fiddle with OLE/COM headaches.

The problematic function in C is defined as:

__declspec(dllexport) void CallMethod(void* myObj) {
    ...
}

It takes a pointer to a COM object, which was previously returned by 

__declspec(dllexport) void* CreateObject(char* szProgId) {
...
   IDispatch * myObj = NULL
...
   return &myObj;
}

So, CreateObject() creates COM object and returns a pointer to it, CallMethod() takes this pointer and tries to communicate with it. 

My error was in returning a pointer to a pointer ... since IDispatch * myObj = NULL was actually hidden in a macro and I missed its true definition, huh. 

Yes, looking at the code above it seems strange returning the &myObj, being myObj a local variable.
Because that means that you would be returning a pointer to the stack, from a frame that already returned...


I was not sure whether the pointer survives from one FFI call to another

Well, the pointer survives, the area of memory pointed by that pointer will be recycled with subsequent calls in general.

, or how Pharo loads/unloads DLLs - but it works as expected.

In general Pharo will load the library on usage, so you don’t have to care about it.
I’m sure the library is not unloaded, unless it is done explicitly (you can check VirtualMachine >> unloadModule:)


Thanks again and best wishes,
Tomaz

Cheers!

Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

eftomi
>  Well, the pointer survives, the area of memory pointed by that pointer will be recycled with subsequent calls in general.

I'll pay special attention to that, but it's probably a matter of properly creating and releasing COM objects.


I have another question - in FFI call, how would one send a wide string, as in 

void CallMethod(void * COMObj, wchar_t * MethodName)

I might have additional questions about UTF-8 and other code page nuances ...

Best wishes,
Tomaz
Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

Guillermo Polito


El 11 sept 2019, a las 11:43, Tomaž Turk <[hidden email]> escribió:

>  Well, the pointer survives, the area of memory pointed by that pointer will be recycled with subsequent calls in general.

I'll pay special attention to that, but it's probably a matter of properly creating and releasing COM objects.


I have another question - in FFI call, how would one send a wide string, as in 

void CallMethod(void * COMObj, wchar_t * MethodName)

I might have additional questions about UTF-8 and other code page nuances …

:D Windows is particularly different than other platforms.
In *nixes most APIs require a char* with the encoded bytes, which we can encode in Pharo doing something like

‘my string’ utf8Encoded => a byte array with the encoded string

But for windows, I think the best is to use their encoding APIs.
You’ll find the image already has some support for that, that we use to access environment variables and the working directory string.

Check the class Win32WideString and its class comment ;)


Best wishes,
Tomaz

Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

eftomi
Excellent, thanks!

Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

eftomi
In reply to this post by eftomi
> We are interested in it :)
> If I recall well Pablo got one version somewhere.

Me too :-)  Unfortunately, I only have time for 'weekend' projects, and any help is appreciated :-)

Regarding the uFFI calls, is it possible to pass Win32WideString in a similar fashion as a String - or where to look to implement that?

Best wishes,
Tomaz
Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

Torsten Bergmann
From your snippets it looks like you want to do COM Automation on Windows.

Maybe you should have a look at: https://github.com/tesonep/pharo-com

Bye
T.
 
Gesendet: Donnerstag, 12. September 2019 um 11:08 Uhr
Von: "Tomaž Turk" <[hidden email]>
An: "Any question about pharo is welcome" <[hidden email]>
Betreff: Re: [Pharo-users] uFFI ExternalAddress challenges
> We are interested in it :)
> If I recall well Pablo got one version somewhere.
 
Me too :-)  Unfortunately, I only have time for 'weekend' projects, and any help is appreciated :-)
 
Regarding the uFFI calls, is it possible to pass Win32WideString in a similar fashion as a String - or where to look to implement that?
 
Best wishes,
Tomaz
Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

Ben Coman


On Thu, 12 Sep 2019 at 18:08, Torsten Bergmann <[hidden email]> wrote:
From your snippets it looks like you want to do COM Automation on Windows.

Maybe you should have a look at: https://github.com/tesonep/pharo-com

Does that by chance include an implementation of "MS-CFB" Microsoft Compound File Binary 

I was wanting to write a utility to parse headers of MSG files saved from Outlook,
to process a for a project email archive by bulk renaming each MSG file with its transmit-date & subject.

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

eftomi
In reply to this post by Torsten Bergmann
> Maybe you should have a look at: https://github.com/tesonep/pharo-com

Thanks for the link, I'll check it out - that's probably the work Steph has mentioned.

Best wishes,
Tomaz
Reply | Threaded
Open this post in threaded view
|

Re: uFFI ExternalAddress challenges

eftomi
In reply to this post by Torsten Bergmann
OK, I'm proceeding with the library that I mentioned (http://disphelper.sourceforge.net/) and I got nice results. Pablo's package goes directly into the core of OLE/COM automation and it would be too hard to for me to study it and continue with it at this stage. 

An example below loads Word for Windows, makes it visible, creates a new document and writes some text:

| w |
COMEngineAlpha initializeCOM .
w := COMEngineAlpha createObjectByName: 'Word.Application'.
COMEngineAlpha callObject: w setProperty: '.Visible = %b' withInteger: 1.
COMEngineAlpha callObject: w method: '.Documents.Add'.
COMEngineAlpha callObject: w method: '.Selection.TypeText(%S)' withString: 'This is a message to appear in Word document.'.
COMEngineAlpha safeReleaseObject: w.
COMEngineAlpha uninitializeCOM.

However, I'd like to marshall a WideString through uFFI so that I won't loose UTF-8 support, of course the plain (String aString) signature doesn't work ("Cannot coerce arguments"). Is there anything I can do myself (i.e. without poking into Pharo virtual machine complexity) to solve this challenge?

Best wishes,
Tomaz

------ Original Message ------
From: "Torsten Bergmann" <[hidden email]>
Cc: "Any question about pharo is welcome" <[hidden email]>
Sent: 12. 09. 2019 12:07:34
Subject: Aw: Re: [Pharo-users] uFFI ExternalAddress challenges

From your snippets it looks like you want to do COM Automation on Windows.

Maybe you should have a look at: https://github.com/tesonep/pharo-com

Bye
T.
 
Gesendet: Donnerstag, 12. September 2019 um 11:08 Uhr
Von: "Tomaž Turk" <[hidden email]>
An: "Any question about pharo is welcome" <[hidden email]>
Betreff: Re: [Pharo-users] uFFI ExternalAddress challenges
> We are interested in it :)
> If I recall well Pablo got one version somewhere.
 
Me too :-)  Unfortunately, I only have time for 'weekend' projects, and any help is appreciated :-)
 
Regarding the uFFI calls, is it possible to pass Win32WideString in a similar fashion as a String - or where to look to implement that?
 
Best wishes,
Tomaz