AX 文件操作示例

来源:互联网 发布:电话线传网络 编辑:程序博客网 时间:2024/05/29 03:32


A very common scenario when doing interfaces with other applications, is to have a directory where one application places files, and the other one picks those up and processes them.
Let’s say some application want to interface with Dynamics AX, one way, from that application to AX. That application will drop files of a specific format, say *.txt, in a directory. You’ll want to have a batch job in AX that checks that directory every 5 minutes for those files. When your batch job finds those files, it will open them and do something with that data, then move them to an other directory, so you don’t process them twice.

Now the first thing you’ll probably think of (well, I did…), is to use the classWinAPI. This class has some useful static methods for checking if folders or files exist, finding files with a filename of a certain format. Just the kind of thing we’re looking for. Your code might look like this:

static void loopfilesWinApi(Args _args)
{
    str fileName;
    int handle;
    str format = "*.txt";
    ;

    if(WinApi::folderExists("C:\\Temp\\"))
    {
        [handle,fileName]= WinApi::findFirstFile("C:\\Temp\\"+ format);
        while(fileName)
        {
            //currentfileName = fileName;
            info(filename);
            filename = WinApi::findNextFile(handle);
        }
        WinApi::closeHandle(handle);
    }
}

That’s perfect. It checks a directory, in this cas C:\temp, for files with the extension txt…until you try to run this in batch. Your batch will have the status error, and there will be an entry in the event log saying:

RPC error: RPC exception 1702 occurred in session xxx

That error message is very misleading, but Florian Dittgen has a nice blog entry about this, that explains why this goes wrong: WinAPI methods a static and run on client. In batch, you can not use these client methods.
But hey! There is a class called WinAPIServer, surely this runs on server, right? Correct, but a lot of the methods available in WinAPI are missing in WinAPIServer. You can however extend the WinAPI server class by copying methods from the WinAPI class and modifying them a bit. Let’s try that.

This is what the WinAPI::FindFirstFile() method looks like:

client staticcontainer findFirstFile(str filename)
{
    Binary data = new Binary(592);// size of WIN32_FIND_DATA when sizeof(TCHAR)==2
    DLL _winApiDLL = new DLL(#KernelDLL);
    DLLFunction _findFirstFile = new DLLFunction(_winApiDLL,'FindFirstFileW');

    _findFirstFile.returns(ExtTypes::DWord);

    _findFirstFile.arg(ExtTypes::WString,ExtTypes::Pointer);

    return [_findFirstFile.call(filename, data),data.wString(#offset44)];
}

As you can see, this method uses the function FindFirstFileW from the Kernel32 dll located in the folder C:\WINDOWS\system32. Let’s copy this method to WinAPIServer and modify it to look like this:

server staticcontainer findFirstFile(str filename)
{
    #define.KernelDLL('KERNEL32')
    #define.offset44(44)

    InteropPermission interopPerm;
    Binary data;
    DLL _winApiDLL;
    DLLFunction _findFirstFile;
    ;

    interopPerm = new InteropPermission(InteropKind::DllInterop);
    interopPerm.assert();

    data = new Binary(592);// size of WIN32_FIND_DATA when sizeof(TCHAR)==2
    _winApiDLL = new DLL(#KernelDLL);
    _findFirstFile = new DLLFunction(_winApiDLL,'FindFirstFileW');

    _findFirstFile.returns(ExtTypes::DWord);

    _findFirstFile.arg(ExtTypes::WString,ExtTypes::Pointer);

    return [_findFirstFile.call(filename, data),data.wString(#offset44)];
}

Three things have changed here:

  1. “Client” has been change to “Server” so it can run on server;
  2. The code has been changed to use the InteropPermission class; this is required because the code runs on server;
  3. Initialization of the variables is moved to after assertion of the InteropPermission.

When you test this in batch (running on server), this will work, and you can do this for all WinAPI methods in the examples above…until you try this on a x64 architecture. You’ll receive a message saying that an error occurred when calling the FindFirstFileW function in the Kernel32 DLL (the exact message slipped my mind).
The problem is described on the Axapta Programming Newsgroup. You can code your way around this problem, but I think the message is clear:don’t use WinAPI.

Instead, use the .NET Framework System.IO namespace (watch out for lowercase/uppercase here), in our case in specific, System.IO.File and System.IO.Directory.
I get this nice code example from Greg Pierce’s blog (modified it a bit):

static void loopfilesSystemIO(Args _args)
{
    // loop all files that fit the required format
    str fileName;
    InteropPermission interopPerm;

    System.Array files;
    int i, j;
    container fList;
    ;

    interopPerm = new InteropPermission(InteropKind::ClrInterop);
    interopPerm.assert();

    files = System.IO.Directory::GetFiles(@"C:\temp","*.txt");
    for( i=0; i<ClrInterop::getAnyTypeForObject(files.get_Length()); i++ )
    {
        fList = conins(fList,conlen(fList)+1, ClrInterop::getAnyTypeForObject(files.GetValue(i)));
    }

    for(j=1;j&lt;=conlen(fList); j++)
    {
        fileName = conpeek(fList, j);
        info(fileName);
    }
}

This will run on client and on server without any problems. To have clean code, you could create your own class, just like WinAPI, but using the System.IO namespace. That way, you can reuse this code and save time.




Append text from one file to an other

This method will append all text from the original file to the destination file.
While this is a very easy task, the method shows many of the things you come across when you are working with files in AX, like:

– Using the #File macro
– Asserting FileIOPermission to be able to access files
– Asserting InteropPermission to be able to use .NET Interop
– Using a set to assert multiple permissions at once
– Using .NET Clr Interop in AX (better than winapi and winapiserver)
– Optional cleaning up after you’re done using reverAssert()

void AppendFileToFile(FileName original, FileName distination)
{
    #File
    FileIOPermission    FileIOPermissionA   = new FileIOPermission(distination, #io_append);
    FileIOPermission    FileIOPermissionR   = new FileIOPermission(original, #io_read);
    InteropPermission   InteropPermission   = new InteropPermission(InteropKind::ClrInterop);
    Set                 permissionset       = new set(types::Class);
    ;

    // create permissionset
    permissionset.add(FileIOPermissionA);
    permissionset.add(FileIOPermissionR);
    permissionset.add(InteropPermission);
    // assert permissions
    CodeAccessPermission::assertMultiple(permissionset);
    // append text from source file to destination file
    System.IO.File::AppendAllText(distination, System.IO.File::ReadAllText(original));

    // limit the scope of the assert
    CodeAccessPermission::revertAssert();
}

Because all permissions are taken care of, this code will run client side as well as server side.




Delete files in Folder using X++ Ax 2012

static void clearDirectory(Args _args)

{

    System.String[] files;

    int             filecount;

    int             i;

 

    if (WinAPI::folderExists('C:\\Myfolder'))

    {

        files = System.IO.Directory::GetFiles('C:\\Myfolder');

        filecount = files.get_Length();

 

        for(i=0; i < filecount; i++)

        {

            System.IO.File::Delete(files.get_Item(i));

        }

    }

}

原创粉丝点击