AIR Pollution - Part II
Disclaimer again: No swfs, libraries, Air runtime, tools were harmed during the making of this application.
Aperture employs a Win32 user level hooking technique called export forwarding. In the current version the msi.dll component is used that contains functions exported for Microsoft Installer packages.
The proxy dll creates function stubs that do nothing but jump to the exported addresses in the original dll, so all calls are delegated to the original msi.dll.
When the AIR application starts the NT DLL Loader sets up the dll initialization list from the static imports found in the PE header. If the proxy msi.dll exists in the application’s folder it will be loaded from this location. The proxy dll then will load the original dll (from the Windows system folder) and forward all calls to the original component.
Once a specific call is intercepted the proxy dll starts the Aperture system, preparing the LocalConnection communication with the current AIR application. And when your AIR ActionScript code starts running Aperture is already listening for commands.
To put it simply: the above stuff means that if the proxy dll is in the AIR application folder and you are on Windows then you have access to the Aperture system. The AIR application will work just fine on any other OS but without native library access.
COM, IDispatch anywhere
The Aperture system dispatches messages from AIR to objects that support OLE Automation (IDispatch). This is how COM “reflection” works. Please note that the provided libraries (apsystem, apimaging, apoutlook) are also ATL COM libraries but Aperture has the ability to work with them without registering their type library.
This is to avoid the necessity of a special installation to register the COM components so an AIR application using the framework will not need any special installation step.
All the libraries provided are statically linked and there are no external dependencies that must be satisfied.
And what about .NET?
Calling .NET assemblies is also possible via COM Interop but currently there is no way to distribute these assemblies without proper registration (you have to go through registration with the RegAsm tool).
FluorineFx Aperture Framework
You can find the framework here
headegg:
Hi. This is great, thanks. I want to open files in their associated application e.g. README.doc in Word, Test.xls in Excel, README.pdf in Acrobat etc. I have modified the When I try:
lc.Execute(”open”, “C:\\tmp\\README.doc”, null);
The air application hangs for 30 seconds (interestingly seems to consistently be 30 to the nearest second) before the document eventually opens. I have tried various file types with similar results. I’ve also tried various other combinations e.g.:
lc.Execute(”open”, “file://C:\\tmp\\README.doc”, null);
lc.Execute(”open”, “file://C:\\tmp\\README.doc”, “”);
lc.Execute(null, “file://C:\\tmp\\README.doc”, null);
and I get the same results. If I put the name of a program e.g. “winword.exe” in place of the document path, it opens instantly. If I put “C:\tmp\README.doc” into c:\tmp\test.bat and use “file://C:\\tmp\\test.bat” and use this as the 2nd parameter, it also opens up instantly (an intermediate command window flashes up before the doc opens).
I have read the help file that is installed with FluorineFX Aperture and also looked at documentation for ShellExecuteEX on msdn. How does the third String parameter, “Parameters”, map to the native ShellExecuteAPI. Is there something that I should pass into this third parameter to get this to work?
Can anybody explain the behaviour I am seeing and offer a possible solution?
I am using Windows XP.
Many thanks,
June 18, 2008, 10:26 amAlex
Zoltan:
ShellExecuteEx uses a SHELLEXECUTEINFO structure, parameters are mapped to the lpParameters member (”The address of a null-terminated string that contains the application parameters. The parameters must be separated by spaces”)
For example: lc.Execute(”open”, “winword”, “C:\\tmp\\README.doc”);
On some operating systems ShellExecute/Ex has this odd behavor, opening to slowly, can you check your application on another machine?
June 18, 2008, 12:47 pmheadegg:
Thanks Zoltan. I will try it on another machine when I get a chance. I’m not familiar with Windows/COM etc programming. It appears, from some further googling, that this issue may well be related to ShellExecuteEX and DDE messages. Do you think this posting is relevant (”fMask = SEE_MASK_FLAG_DDEWAIT”)?:
http://blogs.msdn.com/oldnewthing/archive/2004/11/26/270710.aspx
The example you suggest, above, does work. Obvious really! I tried something like that before, but must have made a mistake. Sorry.
June 18, 2008, 3:26 pmclarkke8:
Is it possible to use the Aperture libraries with straight ActionScript (no Flex)? If so, can you give an ActionScript example of calling the Execute function in the System library? I tried instantiating a LocalObject and setting the “source” property, but I’m kind of stuck.
August 15, 2008, 11:44 pmZoltan:
Do you mean to use AS code only (but from an AIR application)?
August 15, 2008, 11:53 pmAs the Aperture framework is loaded when an AIR application is launched otherwise it would be not possible.
clarkke8:
Yes, that’s exactly what I mean. I have an AIR application that I want to have call an external application, but I’m not planning on using Flex.
August 16, 2008, 12:03 amZoltan:
That would be something like this:
August 19, 2008, 2:19 pmvar lcc:LocalObject = new LocalObject();
lcc.source = “apsystem:26338E77-36A6-46FF-91CA-79E91079A81C”;
var token:AsyncToken = lcc.GetVersion();
token.addResponder(new mx.rpc.Responder(onGetVersion, onFault));
elffikk:
I need to build a wrapper for mplayer (slave mode).
):
September 15, 2008, 2:06 pmSo I have several options (and questions
- how is it possible to control output and input for a console app that is executed (in .NET through processes)?
- is there a place where I can download sources for aperture framework ?
- how can I register .net dlls on installing my application?
Zoltan:
If .NET is your best option then
September 16, 2008, 1:48 am-register the dll with regasm
Your installer will have to install .NET first in the case it is not installed on the target machine
-for controlling a console app maybe these can help: here or here
-the framework is not open source so you cannot donload the sources
elffikk:
Thanks!!!
Regasm was the solution. Thank you for giving such a great solution (aperture not regasm ;))..
Actualy I was asking about an API which can allow me to use console applications and to get their output without using other dlls.
Another question (get it as a feature request
):
September 16, 2008, 10:10 am- possibility to subscribe to system events like new device connection or cd/dvd in/ejected
marcos:
Hi Zoltan
first of all, excuse me for my poor english
I’m using you wonderful Aperture for access at the system level.
So, my aim is gather more and more information of the system is running under my Air application (like the SET in DOS).
My solutions are in order:
1) Open a .bat file with Aperture and redirect the output, but the DOS BlackScreen flashing for a while and is a little bit ugly.
2) Use a process hider (http://www.commandline.co.uk/chp/ to avoid the BlackScreen flash) and launch a cmd.exe with /c option and always redirect the output into a temporary file that i read and return its content to the application and then process the infos.
my code is:
public function ShellExecute(command:String):void {
var fileTmp:File = File.createTempFile();
var tokenExecute:AsyncToken = aperture.Execute(’open’,'chp.exe’,'cmd.exe /d /c ‘+command+’ > “‘+fileTmp.nativePath+’”‘);
tokenExecute.addResponder(new Responder(CaptureOutput,onFault));
tokenExecute.caller = ‘ShellExecute’;
tokenExecute.fileTmp = fileTmp;
}
private function CaptureOutput(evt:ResultEvent):void {
if (evt.token.fileTmp is File) {
var fstream:FileStream = new FileStream;
fstream.open(evt.token.fileTmp,FileMode.READ);
var ret:String = fstream.readUTFBytes(fstream.bytesAvailable);
fstream.close();
evt.token.fileTmp.deleteFileAsync();
return ret;
}
}
All the code works very well, the Result function (CaptureOutput) is called straight after the command is Execute, but the System doesn’t yet redirect the output of the command in the Temp file and the CaptureOutput function returns a empty string.
After all…
my question is: there’s a way to known when the system finish to execute my command, with an event, with some tricks or the only way is develop a time-interval that controls the file dimensions?
Many many thanks for all your works and for pay me some attention.
December 1, 2008, 11:54 amMarco
Zoltan:
Hi Marco,
December 2, 2008, 12:07 amthe current Execute version is asynchronous so you will not be able to get notified when the executable has finished (the system extension dll itself does not wait until the executable has finished).
The easiest solution would be to create your own extension dll to control this.
I will add a synchronous execute but the next release was not scheduled yet. If you do want to get a different version of the apsystem library contact me at support at thesilentgroup.
marcos:
Thanks Zoltan for the explanation
unfotunately i have no time to develop a new dll… i’m not a c++ dev and i spent more time to learn about it than create a workaround
this is my final solution
public function ShellCapture(command:String):void {
var fileTemp:File = File.createTempFile();
var fileStatus:File = File.createTempFile();
var strCommand:String = ‘cmd.exe /d /c ‘+command+’ > “‘+fileTemp.nativePath+’” && echo 0 > “‘+fileStatus.nativePath+’” || echo 1 > “‘+fileStatus.nativePath+’”‘;
var tokenExecute:AsyncToken = aperture.Execute(’open’,'chp.exe’,strCommand);
tokenExecute.addResponder(new Responder(traceShellStatus,onTmpExecuteFault));
tokenExecute.command = strCommand;
tokenExecute.fileTemp = fileTemp;
tokenExecute.fileStatus = fileStatus;
}
private function traceShellStatus(evt:ResultEvent):void {
var fstream:FileStream = new FileStream;
fstream.open(evt.token.fileStatus,FileMode.READ);
var bytes:uint = fstream.bytesAvailable;
fstream.close();
if (bytes == 0) setTimeout(traceShellStatus,100,evt);
else CaptureShellOutput(evt);
}
private function CaptureShellOutput(evt:ResultEvent):void {
if (evt.token.fileTemp is File) {
var sTemp:FileStream = new FileStream;
var sStatus:FileStream = new FileStream;
sTemp.open(evt.token.fileTemp,FileMode.READ);
sStatus.open(evt.token.fileStatus,FileMode.READ);
lastShellOutput = sTemp.readUTFBytes(sTemp.bytesAvailable).replace(/\r\n/g,’\n’);
lastErrorLevel = parseInt(sStatus.readUTFBytes(sStatus.bytesAvailable));
var eventresult:Object = {’output’:lastShellOutput,’errorlevel’:lastErrorLevel};
dispatchEvent(new ResultEvent(ResultEvent.RESULT,false,true,eventresult));
sTemp.close();
sStatus.close();
evt.token.fileTemp.deleteFileAsync();
evt.token.fileStatus.deleteFileAsync();
}
}
the strCommand have a particular dos syntax the code after the && is executed only if the first command is executed with success and the code after the || if there’s a fault
so i can catch even something similar to an “errorlevel” to return at my application
i hope to be useful to other that want to develop a similar solution
December 2, 2008, 5:28 pmcheers
Marco
nileshbafna:
Thanks for this great library.
I wanted to communicate from DLL to actionscript 3 / Flex application. How is this possbile?
Cause it uses LocalConnection I think this should be possbile? Please show me some pointers as to how I can achieve this using FlourineFx Aperture. I am using VC++ and Flex. Any snippet in this would be appreciated
December 23, 2008, 9:45 amZoltan:
The framework is designed to work only with AIR applications as described in the docs
December 23, 2008, 2:54 pmhaianh:
Hi Zoltan!
January 13, 2009, 5:03 amThank for your great work. It is very useful for my project. I want to ask you some question
1. In my AIR app, I want to open MS Word document but not visible, then save content to text file, so apsystem.dll can do it?
2. If I need to write a extension dll, can you give me the way to do it? I’m not expert in C++ but I know a little bit about ActiveXObject, and I will try to learn.
Thank in advance!!!
Hai Anh
layola:
I have a question about byte array from .net to flex.
here is my c# code.
int size = 0;
byte[] _swfdata=new byte[20];
public byte[] swfData
{
get
{
return _swfdata;
}
set
{
_swfdata = value;
}
}
public byte[] getSWF(string name)
{
Stream swfStream = this.GetType().Assembly.GetManifestResourceStream(name);
swfData = new BinaryReader(swfStream).ReadBytes((int)swfStream.Length);
MessageBox.Show(”"+swfData.Length);
size = 0;
byte[] buff = new byte[4000];
Array.Copy(swfData, buff, 4000);
size += 4000;
return buff;
}
public byte[] getChunk()
{
byte[] buff = new byte[1];
MessageBox.Show(”data.Length”);
MessageBox.Show(”data.Length”+swfData.Length);
/* if (data.Length - size > 4000)
{
Array.Copy(data, buff, 4000);
size += 4000;
return buff;
// MessageBox.Show(”size” + size);
}
else
{
if (size == data.Length)
{
size = 0;
return null;
}
Array.Copy(data, buff, data.Length - size);
size += data.Length - size;
return buff;
}*/
return buff;
}
after I invoke getSWF, and I invoke getChunk(),
April 28, 2009, 4:15 ambut the result was not I want .
messagebox show swfdata length always 20.
is that every time I invoke a method, c# create new instance?
thank you.
Zoltan:
In the case of the Aperture framework in the latest version (http://aperture.fluorinefx.com/) you do not have to use chunking (there is no size limitation)
April 29, 2009, 1:33 amAnd yes, for every call a new instance of the object is used (you could use static members if for some reason you need to preserve data across calls.
layola:
but my version is last version and the size limit is 4440?
April 29, 2009, 8:41 amanother qustion .
when I use realse verison dll intead of deubug version dll,it can’t work.alway throw a error:
fault: [RPC Fault faultString=”Invalid UUID apdotnet.Sample” faultCode=”Aperture.Error.InvalidUUID” faultDetail=”"
why?
not registration yet?or?
thank you ,waiting for your answer.
Zoltan:
No, in the last version there is no size limit (check that you are using the latest actionscript files too, taken from the sample projects). And the previous limit was around 40kb not 4440.
April 29, 2009, 9:17 pm“Aperture.Error.InvalidUUID” means that your class is not accessible as a COM object (see the “And what about .NET?” section of this blog post)
ofllm:
hi,zoltan!
July 5, 2009, 2:48 amfirst,please forgive my poor english.
Thank for your great work,and it really very good.
i want to ask you a questions.i write a dll use VC6(ATL DLL),and it run ok in my machine with my air project,but when i release my air project and run in other machine ,the dll seems doesn’t work.when regsvr32 the dll in the other machine,it run ok .but in your example ,the dll seems not to need register,could you help me,why? what should i do in my DLL,thank you very much.
ajithklm:
Hi,I created an AIR application for invoking executables(similar to SystemDemo example you provided).The application worked fine when i installed and run it in a WindowsXP machine.
July 6, 2009, 11:27 amBut when i tried to run this in a VISTA machine it gives an error:
Fault: [RPC Fault faultString=”Invoke failed”
faultCode=”Aperture.Error.Invoke”
faultDetail=”Unspecified error”]
Please help me….
Thanks in advance..
aneeshps:
Hi Everybody,
July 8, 2009, 7:23 amI am using ‘CaptureDemo’ Flex project to capture the desktop.This application successfully capture the whole desktop area,but i want to capture some selected area of desktop,for that i want to pass the x co-ordinate,y co-ordinate,height,width of the selected area.
How this can perform?
thank you,waiting for the answer.
Zoltan:
To pass additional parameters you will have to write your own extension library (as described in the extdev.doc in the installation folder)
July 8, 2009, 11:19 amUsing the latest version you can send back the (whole) image bytes wrapped in a SAFEARRAY**
Zoltan:
ofllm: if your dll requires registration then probably the type library is not accessible. Please consult page 10 in the extdev.doc
July 8, 2009, 11:22 amZoltan:
ajithklm: can you provide details about what/how are you invoking? Please contact me at support at thesilentgroup com
July 8, 2009, 11:24 amaneeshps:
Thank you Zoltan,
July 9, 2009, 12:28 pmIf u provided the source code for the whole desktop capturing in Flex,then only i can perform the selected desktop area capturing functionality.So please provide the source code for me.
waiting for your reply.
sobelito:
Hello.
I am enjoying the features of Aperture and am having success with Execute and CreateProcess.
The following 2 scenarios are not working as expected, and I was wondering if anyone had seen the same behavior, and perhaps might have some insight:
1) executesync does not appear to halt execution. Though the operation is performed, it seems to be operating as execite does, with one exception: the “onExecuteSync” event is never fired. Has anyone successfully used “ExecuteSync” as one would expect it to work?
2) When running multiple Execute commands in succession, the result event for the last called fires X times, where X = the number of commands fired. Thus if I create 3 localObjects, and I use the execute method one right after the other, the onResult event for the last of the 3 local objects fires, not 3 distinct onResult events, as expected.
Any insight would be greatly appreciated!
August 14, 2009, 2:32 amzaphod:
Hi, how do you specify the working directory when you launch an external app?
August 18, 2009, 4:21 pmZoltan:
Currently changing the working directory is not supported (however you could write your own Execute or CreateProcess and pass a directory as these are just wrapping the Win ShellExecuteEx and CreateProcess functions)
September 1, 2009, 1:24 pmZoltan:
sobelito:
September 1, 2009, 2:11 pm1)ExecuteSync does not halt the AIR application, but it will not raise an event until the running application is closed
2) try to change in LocalObject.as the _methodsLookup, _methodResultLookup, _methodResponderLookup to non-static members