To start of lets create one flash file. The file has to be simple just carrying one select directory button and a movieclip with textbox inside it, which will display the directory name. You can also create dynamic movie clips to deal all these but here i have placed them in library.
This is the link to the fla file if you don't want to create it. You can download it from below link.
I am using a document class here named as "main.as". I am using external interface to communicate to the VB which reads
the directory and sends us the values. Lets check the document class below.
package {
import flash.display.Sprite;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.external.ExternalInterface;
import flash.text.TextField;
import flash.net.navigateToURL;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.display.Loader;
public class main extends Sprite
{
private var _directoryPath:String;
private var _directoryList:XML;
private var _sampleXML:XML;
private var _parserData:directoryParser;
private var _linkObjects:Array = new Array();
public function main()
{
_directoryPath = "C:\\Documents and Settings\\nitins\\Desktop\\Reading_Directory_Content\\data"
selectFolder.addEventListener(MouseEvent.MOUSE_DOWN, selectDirectoryInvoked);
ExternalInterface.addCallback("sendToActionScript",callFromVB);
}
private function callFromVB(_myParam)
{
_directoryList = XML(_myParam);
_parserData = new directoryParser(_directoryList);
generateTree(_parserData.returnXML());
}
public function selectDirectoryInvoked(event:MouseEvent)
{
ExternalInterface.call("selectFolder",_directoryPath);
}
public function generateTree(givenList:XML)
{
var loopLen:Number = givenList.filename.length();
var strtY:Number = 50;
for(var i=0;i<loopLen;i++)
{
var tempHold = givenList.filename[i].split(".")
var req:String = tempHold(tempHold.length-1)+".jpg"
var iconLoader:Loader = new Loader();
iconLoader.load(new URLRequest(req));
_linkObjects[i] = new ListBox();
_linkObjects[i].txt.text = givenList.filename[i];
_linkObjects[i].openFileName = givenList.filename[i];
_linkObjects[i].addEventListener(MouseEvent.MOUSE_DOWN, clikedLink);
_linkObjects[i].y = strtY
addChild(_linkObjects[i]);
strtY += _linkObjects[i].height+10;
}
}
public function clikedLink(e:MouseEvent):void
{
var _passPath:String = _directoryPath+"\\"+e.currentTarget.openFileName
ExternalInterface.call("openFile",_passPath);
}
}
}
As you can notice above - the method "selectDirectoryInvoked" is the one which gets called when the select directory button is clicked from the interface. Basically it just makes a call to VB function "selectFolder" and passes a parameter as the path of the directory. One more function you can notice above is the "callFromVB" it is the method that is registered to External Interface in the constructor of the "main" class.
This class is basically sorting the list given from VB to file types and showing the display. The public method within this class "returnXML()" is used to fetch the sorted XML which our document class "main" processes. It generated a list of files using "generateTree()" method.
In the above image the two red boxes are important, mySWF is the name of the shockwave object. AllowScriptAccess you need to make it "always" if you want your application to work.
/*------------------------------------------------------------*/
private function callFromVB(_myParam)
{
_directoryList = XML(_myParam);
_parserData = new directoryParser(_directoryList);
generateTree(_parserData.returnXML());
}
/*-------------------------------------------------------------*/
Above method is reading "_myParam" which is a dynamically generated XML from VB App.
Few very important things which we must keep in mind doing this communication is that the entire communication is XML oriented. From making the External interface call to reading it, all this happens in a perticular XML node format which is something like this :
<invoke name="functionName" returntype="xml">
<arguments>
... (individual argument values)
</arguments>
</invoke>
Communication between ActionScript and an application hosting the Shockwave Flash ActiveX
control uses a specific XML format to encode function calls and values. There are two parts to the
XML format used by the external API. One format is used to represent function calls. Another
format is used to represent individual values; this format is used for parameters in functions
as well as function return values. The XML format for function calls is used for calls to and
from ActionScript. For a function call from ActionScript, Flash Player passes the XML to the container;
for a call from the container, Flash Player expects the container application to pass it an XML string
in this format. - (From Adobe Docs)
This is the reason i have casted the parameter from VB to XML. One more thing to notice
in the above method is
_parserData = new directoryParser(_directoryList);
Here i also have created one class which parses the VB returned XML to flash. Lets take a look
at this class.
directoryParser.as
package {
import flash.display.Sprite;
public class directoryParser
{
private var _returnData:XML;
private var _maxLen:Number;
private var _typeArray:Array;
public function directoryParser(dataToParse:XML)
{
_maxLen = dataToParse.filename.length();
_typeArray = new Array();
var temp:Array;
for(var i=0;i<_maxLen;i++)
{
temp = dataToParse.filename[i].split(".")
dataToParse.filename[i].@type = temp[1];
_typeArray.pushUnique(temp[1]);
}
_typeArray.sort();
generateSortedXML(dataToParse);
}
private function generateSortedXML(inputXML:XML)
{
var myTempXML:XML = <rootNode></rootNode>;
for(var i=0;i<_typeArray.length;i++)
{
for(var k=0;k<inputXML.filename.length();k++)
{
if(inputXML.filename[k].@type == _typeArray[i])
{
myTempXML.appendChild(inputXML.filename[k].toXMLString());
}
}
}
_returnData = myTempXML
}
public function returnXML():XML
{
return _returnData;
}
Array.prototype.pushUnique = function( a )
{
var l:Number = this.length;
var exists:Boolean = false;
for( var i=0; i<l; i++ ) if( this[i] == a ) exists = true;
if( !exists ) this.push( a );
};
}
}
This class is basically sorting the list given from VB to file types and showing the display. The public method within this class "returnXML()" is used to fetch the sorted XML which our document class "main" processes. It generated a list of files using "generateTree()" method.
You can use the same method to do any application handling using ExternalInteface.
Link to this class below :
Now lets take a look @ the VB interface and its code, what things we should keep in mind. Look @ below image.
In the above image the two red boxes are important, mySWF is the name of the shockwave object. AllowScriptAccess you need to make it "always" if you want your application to work.
Lets take a look @ the VB Code and its methods.
Lets take a look at VB code.
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, _
ByVal lpFile As String, ByVal lpParameters As String, _
ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
Dim directoryPath As String
Private Sub mySWF_FlashCall(ByVal request As String)
Dim objXML As MSXML2.DOMDocument
Dim strXML As String
Set objXML = New MSXML2.DOMDocument
strXML = request
If Not objXML.loadXML(strXML) Then
Err.Raise objXML.parseError.errorCode, , objXML.parseError.reason
End If
Dim objElem As MSXML2.IXMLDOMElement
Set objElem = objXML.selectSingleNode("//invoke")
If objElem.getAttribute("name") = "selectFolder" Then
Dim xmlElem As IXMLDOMElement
Set xmlElem = objXML.selectSingleNode("invoke/arguments/string")
directoryPath = xmlElem.Text
Dim fso As FileSystemObject
Dim fso_folder As Folder
Dim txt As String
Dim fso_file As File
Dim i As Long
Dim file_names() As String
' Make a new File System object.
Set fso = New FileSystemObject
' Get the FSO Folder (directory) object.
MsgBox directoryPath + "\"
Set fso_folder = fso.GetFolder(directoryPath + "\")
' Make the list of names.
ReDim file_names(1 To fso_folder.Files.Count)
i = 1
For Each fso_file In fso_folder.Files
file_names(i) = fso_file.Name
i = i + 1
Next fso_file
'MsgBox file_names(1)
Dim postString As String
postString = "<rootNode>"
For k = 1 To UBound(file_names)
postString = postString + "<filename>" + file_names(k) + "</filename>"
Next k
postString = postString + "</rootNode>"
mySWF.CallFunction ("<invoke name='sendToActionScript' returntype='xml'><arguments><string>" + postString + "</string></arguments></invoke>")
End If
If objElem.getAttribute("name") = "openFile" Then
Dim xmlElem1 As IXMLDOMElement
Set xmlElem1 = objXML.selectSingleNode("invoke/arguments/string")
MsgBox xmlElem1.Text
temp1 = Split(xmlElem1.Text, "\")
For i = 1 To UBound(temp1) - 1
finalPath = finalPath + temp1(i) + "\"
Next i
Dim ret As Long
Dim lErr As Long
ret = ShellExecute(Me.hwnd, vbNullString, xmlElem1.Text, vbNullString, finalPath, 1)
If (ret < 0) Or (ret > 32) Then
'ShellEx = True
Else
Select Case ret
Case 0
lErr = 7: sErr = "Out of memory"
Case ERROR_FILE_NOT_FOUND
lErr = 53: sErr = "File not found"
Case ERROR_PATH_NOT_FOUND
lErr = 76: sErr = "Path not found"
Case ERROR_BAD_FORMAT
sErr = "The executable file is invalid or corrupt"
Case SE_ERR_ACCESSDENIED
lErr = 75: sErr = "Path/file access error"
Case SE_ERR_ASSOCINCOMPLETE
sErr = "This file type does not have a valid file association."
Case SE_ERR_DDEBUSY
lErr = 285: sErr = "The file could not be opened because the target application is busy. Please try again in a moment."
Case SE_ERR_DDEFAIL
lErr = 285: sErr = "The file could not be opened because the DDE transaction failed. Please try again in a moment."
Case SE_ERR_DDETIMEOUT
lErr = 286: sErr = "The file could not be opened due to time out. Please try again in a moment."
Case SE_ERR_DLLNOTFOUND
lErr = 48: sErr = "The specified dynamic-link library was not found."
Case SE_ERR_FNF
lErr = 53: sErr = "File not found"
Case SE_ERR_NOASSOC
sErr = "No application is associated with this file type."
Case SE_ERR_OOM
lErr = 7: sErr = "Out of memory"
Case SE_ERR_PNF
lErr = 76: sErr = "Path not found"
Case SE_ERR_SHARE
lErr = 75: sErr = "A sharing violation occurred."
Case Else
sErr = "File could not be opened due to invalid application"
End Select
'Err.Raise lErr, , App.EXEName & "", sErr
MsgBox sErr
End If
End If
End Sub
Lets take a look at VB code.
"Private Sub mySWF_FlashCall(ByVal request As String)" Its very important to keep the function name in same way like with the shockwave object and adding the "_FlashCall" to let your application work. "request" is the variable as in XML format with arguement as shown and explained above in the tag.
With this code
postString = "<rootNode>"
For k = 1 To UBound(file_names)
postString = postString + "<filename>" + file_names(k) + "</filename>"
Next k
postString = postString + "</rootNode>"
I am creating one Dynamic XML String which we are sending to flash function "callFromVB".
This is the line which is sending the data to AS3.0 method.
mySWF.CallFunction ("<invoke name='sendToActionScript' returntype='xml'><arguments><string>" + postString + "</string></arguments></invoke>")
The other code is for opening a perticular file with the associated application as in case of my requirement it was required. I have put in a check to see if the file is working or not corrupt and if its fine i am launching it.
You can download entire application @ below link :
A special thanks to my manager Manotosh Roy who helped me in doing this stuff :o)
I hope you find it useful.
-Nitin