Qmake Command to copy directory and files - windows

I want to copy the contents in directory "temp" to "dd"
BINARY_DESTINATION_PATH = $$PWD$$SEPARATOR/dd/
RESOURCE_SOURCE_PATH = $$PWD$$SEPARATOR/temp
EXPORTED_DESTINATION_PATH = $${BINARY_DESTINATION_PATH}
EXPORTED_DESTINATION_PATH ~= s,/,\\,g
EXPORTED_SOURCE_PATH = $${RESOURCE_SOURCE_PATH}
EXPORTED_SOURCE_PATH ~= s,/,\\,g
QT += core
QT -= gui
CONFIG += c++11
TARGET = sample
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
win32 {
QMAKE_POST_LINK += $$quote(cmd /c xcopy /S /I /Y $${EXPORTED_HEADERS}\\copy_to_output $${EXPORTED_HEADERS_WIN})
}
but this is not adding the files into dd and showing error on build.
Error i son line "QMAKE_POST_LINK"

quote(string)
Converts a whole string into a single entity and returns the result. This is just a fancy way of enclosing the string into double quotes.
What you're probably looking for is system(command).

Related

qmake "system" command always return success code

I am writing the auto-build system for the one complicated Qt5 program.
Some of it's C++ sources contains special markers and need to be processed by the external utility.
So i decide to write a custom prebuild step in qmake project like this:
!build_pass {
win32 {
for( src, SOURCES ) {
system("findstr \"MY_CUSTOM_MARKER\" $$src"):system( MyExternalUtil $$src )
}
}
}
But first system command always return success, i.e. all of my sources will be processed!
However, official Qt documentation say they should not.
So my question is: why such system command always returns 0?
P.S. I tried it in the Windows command prompt: findstr properly set %errorlevel% to the 1 when specified text was not found.
P.P.S Here is the workaround(just use other version of system):
!build_pass {
win32 {
for( SRC_PATH, SOURCES ) {
FULL_SRC_PATH = \"$$PWD/$$SRC_PATH\"
FULL_SRC_PATH = $$replace( FULL_SRC_PATH, /, \ )
FIND_RES = $$system( findstr \"MY_CUSTOM_MARKER\" $$FULL_SRC_PATH )
!isEmpty( FIND_RES ) {
message( Processing $$SRC_PATH... )
system( MyExternalUtil $$FULL_SRC_PATH )
}
}
}
}
defineTest(systemWrapper) {
ret = $$system($${ARGS} 2> /dev/null; echo $?)
equals(ret, 0):return(true)
return(false)
}

calling gccxml from my code (Windows)

I'm writing a tool that makes use of gccxml. Basically I'm parsing the output xml file that has been created by gccxml. This works great on my windows machine in visual studio except for a couple of drawbacks. Here's the current state of my project:
cmake_gui gave me a visual studio solution that compiles perfectly (x64 Release). It's set up to create three executables in E:\cmake_builds\GCCXML\bin\Release.
My own C++ tool is located in a different VS solution file. When it's supposed to make use of gccxml the following code is used:
bool Parser::ParseFile( const std::string& _szFileName, std::string& _gccxmlPath,
const std::string& _tempFileLocation,
std::string& _errorStr)
{
bool retVal = true;
printf("Parsing file %s...\n\n", _szFileName.c_str());
/* format _gccxmlPath, adding a final forward slash to the path if required */
char lastChar = _gccxmlPath.at(_gccxmlPath.length()-1);
if(lastChar != '/' && lastChar != '\\')
_gccxmlPath += "/";
/* set up a temporary environment path variable so that the gccxml exe files may locate each other */
char envPath[500];
sprintf_s(envPath, "PATH=%s", _gccxmlPath.c_str());
const char* gccxml_env[] =
{
/* set path to gccxml directory where all exe files from gccxml are located */
envPath,
0
};
/* path & filename of gccxml.exe */
char gccxml_exe[500];
sprintf_s(gccxml_exe, "%sgccxml.exe", _gccxmlPath.c_str());
/* parameter string used to set gccxml output filename */
char fxmlParam[500];
sprintf_s(fxmlParam, "-fxml=\"%s\"", _tempFileLocation.c_str());
/* synthesize argument list for gccxml*/
/* see: http://gccxml.github.io/HTML/Running.html */
/* and: https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Invoking-GCC.html */
const char* gccxml_args[GCCXML_PARAM_LEN];
unsigned int curPos = 0;
/* 1st argument: exe name */
gccxml_args[curPos++] = "gccxml.exe";
/* the source code to be compiled */
gccxml_args[curPos++] = _szFileName.c_str();
/* try to find out which msvc compiler to use */
gccxml_args[curPos++] = "--gccxml-compiler cl";
/* the output xml file */
gccxml_args[curPos++] = fxmlParam;
/* last argument: zero termination */
gccxml_args[curPos++] = 0;
/* call gccxml & compile the source code file */
if(0 != _spawnvpe(P_WAIT, gccxml_exe, gccxml_args, gccxml_env))
{
_errorStr += "GCCXML Compiler Error";
return false;
}
/* now parse the gccxml output file from tempfile ... */
...
...
return retVal;
}
as you can see I have to set up a local environment PATH variable to make sure the three executables are able to find each other.
This works great for what I want to do.
Unfortunately I can't use this method to call gccxml.exe when I move the three executables to a different directory. Of course I provide the new _gccxmlPath string but gccxml returns
"Support item Vc10/Include is not available..."
telling me that it looked in the folder into which I moved the executables. All my local copies of Vc10/Include however are located somewhere totally different and I don't understand how it had been able find one of these before I moved the executables.
It seems like this problem can be fixed by calling gccxml_vcconfig.exe using the parameters "patch_dir" and providing the directory "gccxml/Source/GCC_XML/VcInstall" from my gccxml source files. I'm, however, not able to solve my issue this way using any of the spawn* commands.
If I do the gccxml_vcconfig.exe runs just fine but after that I'm trying to call gccxml.exe and it turns out that it still looks in the same directory as before.
So gccxml_vcconfig.exe was probably not what I was looking for?
I'm trying to find a way to provide my tool to users who don't want to recompile gccxml on their machine so I'd like to distribute the thre binaries (and what else is needed).
just to let you know. I found a way of doing what I wanted to do. The trick is as follows:
right before vpe-spawning gccxml using its own location as environment (as shown above) vp-spawn the gccxml_vcconfig.exe without providing any environment path variables. This may look like this
std::string VcInstallDir = resolveRelativePath(_gccxmlPath + "../share/gccxml-0.9/VcInstall");
std::string GCCXML09Dir = resolveRelativePath(_gccxmlPath + "../share/gccxml-0.9");
std::vector<const char*> gccxml_config_args;
gccxml_config_args.push_back("gccxml_vcconfig.exe");
gccxml_config_args.push_back(VcInstallDir.c_str());
gccxml_config_args.push_back(GCCXML09Dir.c_str());
gccxml_config_args.push_back(0);
if(0 != _spawnvp(_P_WAIT, gccxml_vcconfig_exe.c_str(), gccxml_config_args.data()))
{
_errorStr += "GCCXML Configuration Error";
return false;
}
note that resolveRelativePath is a self written function for string manipulation that produces a valid absolute path; gccxml_vcconfig_exe contains the absolute path to my exe file
and I somewhat changed my coding style from arrays to std::vectors as you can see

Windows command-line disk cataloging - save to csv?

I'm working on a Windows batch script that creates a directory/file listing of a complete hard disk for archival/cataloging purposes, using only command line-tools (and open-source/free tools). For each of the entries in the listing I wanted to list the filename, directory where it resides in, the filesize, date a,nd time of the file, and the md5 sum. I have been able to create somewhat a working starting point, but I'm hitting a wall since I'm not sure if it is even possible using the command-line tools in Windows. The command "dir /s /a:-d /o:-d /t:c" gives me a nice overview, but I would like this overview displayed (or saved to) a comma-delimited format. So my questions are:
Can I create a csv file with all the fields I mentioned above, with the standard command-line tools (and a m5 freeware tool for the md5 sums)
Do you know of a better way, or is there a dead simple disk cataloging command-line tool I missed?
Thanks in advance for any tips!
You can use dir /s /a:-d /o:-d /t:c > slam.txt
Then the content of this slam.txt, can be processed by WScript in windows, making a CSV file ...
If you need a WScript ex, I can provide one ?
I know this not an CSV example - but it should be complex enough for pattern inspiration :)
and remember this fil is saved as .js
var what2lookfor = '<rect ';
var forReading = 1, forWriting = 2, forAppending = 8, jx = 0, ix = 0;
var triStateUseDefault = -2, triStateTrue = -1, triStateFalse = 0;
var thisRecord="", validFileTypes="js,xml,txt,php,xsl,css,htm,html" , akkum = "";
var fileArray = [];
var FSO = new ActiveXObject("Scripting.FileSystemObject");
var objFiles = FSO.GetFolder("F:\\xps1710\\jscript\\");
var objFileControl = new Enumerator(objFiles.files);
for (; !objFileControl.atEnd(); objFileControl.moveNext()) {
objFile = FSO.GetFile(objFileControl.item());
var ext = objFile.Name.split(".");
if (validFileTypes.indexOf(ext[1]) > 1) {
fileArray[ix] = "F:\\xps1710\\jscript\\" + objFile.Name;
ix++;
}
}
for (zx = 0 ; zx < ix ; zx++ ) {
var file2Traverse = FSO.OpenTextFile(fileArray[zx], forReading, triStateUseDefault );
while (!file2Traverse.AtEndOfStream) {
thisRecord = file2Traverse.ReadLine();
if (thisRecord.indexOf(what2lookfor) > 1 ) {
akkum = akkum + fileArray[zx] + '::' + thisRecord + '\n';
break;
}
}
}
WScript.Echo(akkum);

Get DOS path instead of Windows path

In a Windows CMD.exe command line, how can I get the full DOS name/short name (a.k.a. 8.3 format) of the directory I am in?
For example, if I am in the directory C:\Program Files\Java\jdk1.6.0_22, I want to display it's short name C:\PROGRA~1\Java\JDK16~1.0_2.
I know running dir /x will give me the short names of files/directories in the current directory but I haven't been able to find a way to display the full path of the current directory in short name format. I'm having to work my way through the path from the root, directory by directory, running dir /x in each.
I'm sure there is an easier way to do this?
for %I in (.) do echo %~sI
Any simpler way?
You could also enter the following into a CMD window:
dir <ParentDirectory> /X
Where <ParentDirectory> is replaced with the full path of the directory containing the item you would like the name for.
While the output is not a simple as Timbo's answer, it will list all the items in the specified directory with the actual name and (if different) the short name.
If you do use for %I in (.) do echo %~sI you can replace the . with the full path of the file/folder to get the short name of that file/folder (otherwise the short name of the current folder is returned).
Tested on Windows 7 x64.
In windows batch scripts, %~s1 expands path parameters to short names. Create this batch file:
#ECHO OFF
echo %~s1
I called mine shortNamePath.cmd and call it like this:
c:\>shortNamePath "c:\Program Files (x86)\Android\android-sdk"
c:\PROGRA~2\Android\ANDROI~1
Edit: here's a version that uses the current directory if no parameter was supplied:
#ECHO OFF
if '%1'=='' (%0 .) else echo %~s1
Called without parameters:
C:\Program Files (x86)\Android\android-sdk>shortNamePath
C:\PROGRA~2\Android\ANDROI~1
Being a programmer made this 10-minute Winform project. It's been useful for me. Making this app to a context menu for file explorer would save more clicks.
Form1.cs:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace ToShortPath
{
public partial class Form1 : Form
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetShortPathName(
[MarshalAs(UnmanagedType.LPTStr)]
string path,
[MarshalAs(UnmanagedType.LPTStr)]
StringBuilder shortPath,
int shortPathLength
);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Show the dialog and get result.
var openFileDialog1 = new OpenFileDialog();
DialogResult result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK) // Test result.
{
textBox1.Text = openFileDialog1.FileName;
}
}
private void button2_Click(object sender, EventArgs e)
{
var openFileDialog1 = new FolderBrowserDialog();
DialogResult result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK) // Test result.
{
textBox1.Text = openFileDialog1.SelectedPath;
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
StringBuilder shortPath = new StringBuilder(65000);
GetShortPathName(textBox1.Text, shortPath, shortPath.Capacity);
textBox2.Text = shortPath.ToString();
}
}
}
Form1.Designer.cs:
namespace ToShortPath
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(69, 13);
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(516, 53);
this.textBox1.TabIndex = 0;
this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(69, 72);
this.textBox2.Multiline = true;
this.textBox2.Name = "textBox2";
this.textBox2.ReadOnly = true;
this.textBox2.Size = new System.Drawing.Size(516, 53);
this.textBox2.TabIndex = 1;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(7, 35);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(56, 13);
this.label1.TabIndex = 2;
this.label1.Text = "Long Path";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(7, 95);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(57, 13);
this.label2.TabIndex = 3;
this.label2.Text = "Short Path";
//
// button1
//
this.button1.AutoSize = true;
this.button1.Location = new System.Drawing.Point(591, 13);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(40, 53);
this.button1.TabIndex = 4;
this.button1.Text = "File";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.AutoSize = true;
this.button2.Location = new System.Drawing.Point(637, 12);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(46, 53);
this.button2.TabIndex = 5;
this.button2.Text = "Folder";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(687, 135);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.textBox2);
this.Controls.Add(this.textBox1);
this.Name = "Form1";
this.Text = "Short Path";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
}
}
run cmd.exe and do the following:
> cd "long path name"
> command
Then command.com will come up and display only short paths.
source
Kimbo's answer is perfect for normal files.
for %I in (.) do echo %~sI
For MsDos file names on HardLinks
The hard links created with mklink /H <link> <target> will not have an MsDos short file name.
In case you dir /X and you discover that missing short name you should expect the followings:
d:\personal\photos-tofix\2013-proposed1-bad>dir /X
Volume in drive D has no label.
Volume Serial Number is 7C7E-04BA
Directory of d:\personal\photos-tofix\2013-proposed1-bad
03/02/2015 15:15 <DIR> .
03/02/2015 15:15 <DIR> ..
22/12/2013 12:10 1,948,654 2013-1~1.JPG 2013-12-22--12-10-42------Bulevardul-Petrochimiștilor.jpg
22/12/2013 12:10 1,899,739 2013-12-22--12-10-52------Bulevardul Petrochimiștilor.jpg
Normal file
In this case
> for %I in ("2013-12-22--12-10-42------Bulevardul-Petrochimiștilor.jpg") do echo %~sI
I've got what I expected
d:\personal\PH124E~1\2013-P~3\2013-1~1.JPG
Hard link file
In this case
> for %I in ("2013-12-22--12-10-52------Bulevardul-Petrochimiștilor.jpg") do echo %~sI
I've got the normal MsDos path but the normal filename.
d:\personal\PH124E~1\2013-P~3\2013-12-22--12-10-52------Bulevardul-Petrochimiștilor.jpg`
A someone more direct answer is to fix the bug.
%SPARK_HOME%\bin\spark-class2.cmd; Line 54
Broken: set RUNNER="%JAVA_HOME%\bin\java"
Windows Style: set "RUNNER=%JAVA_HOME%\bin\java"
Otherwise, the RUNNER ends up with quotes, and the command
"%RUNNER%" -Xmx128m ...
ends up with double-quotes. The result is that the Program and File are treated as separate parameters.
similar to this answer but uses a sub-routine
#echo off
CLS
:: my code goes here
set "my_variable=C:\Program Files (x86)\Microsoft Office"
echo %my_variable%
call :_sub_Short_Path "%my_variable%"
set "my_variable=%_s_Short_Path%"
echo %my_variable%
:: rest of my code goes here
goto EOF
:_sub_Short_Path
set _s_Short_Path=%~s1
EXIT /b
:EOF
$fso = New-Object -com scripting.filesystemobject
$fso.GetFolder('c:\Program Files (x86)').ShortName()
PROGRA~2
Inspired by Dr. Scripto's answer
if via a batch file use:
set SHORT_DIR=%~dsp0%
you can use the echo command to check:
echo %SHORT_DIR%
Place this script somewhere in the windows path. I called mine getshort.bat and placed it in the System32 folder.
To use this you must pass a single path parameter after calling the scrip in cmd.exe window.
So open cmd.exe and type something like getshort.bat "C:\folder\file name with spaces.ext" You must double-quote paths with spaces otherwise not necessary.
The script will take the path you supplied and store the shortname in a temporary text file with two versions, version 1 has quotes around the short path, and the other version does not.
I use notepad++ to open txt files so if you do not use that program you need to change the line START "" /MAX NOTEPAD++ "%TMP%\Test.txt" and replace notepad++ with your editor's name.
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
COLOR 0A
:----------------------------------------------------------------------------------
REM SET THE CD VARIABLE EQUAL TO THE FIRST PARAMATER YOU PASSED THE SCRIPT
:: ..WHICH WAS A FILE PATH OF YOUR CHOOSING...
CD=%1
:----------------------------------------------------------------------------------
:: DELETE ANY LEFTOVERS FROM PRIOR RUNS
IF EXIST "%TMP%\Test.txt" DEL /F /Q "%TMP%\Test.txt"
:----------------------------------------------------------------------------------
:: USE THE FOR COMMAND TO CALL A SUBROUTINE TO STORE THE SHORT NAMES WITH AND WITHOUT QUOTES
FOR %%1 IN ("%CD%") DO (
SET ARG1="%%~s1"
SET ARG2=%%~s1
CALL :CREATE_TXTFILE ARG1 ARG2
START "" /MAX NOTEPAD++ "%TMP%\Test.txt"
GOTO :EOF
)
:----------------------------------------------------------------------------------
REM USE THIS SUB-ROUTINE TO STORE THE SHORTNAMES INSIDE THE A TXT FILE
:CREATE_TXTFILE
(
ECHO %ARG1%
ECHO %ARG2%
)>"%TMP%\Test.txt"
:: THE NEXT LINE WILL RETURN THE SCRIPT TO THE LINE BELOW THE CALL COMMAND ABOVE AND CONTINUE EXECUUTION AS NORMAL
:: NOTEPAD++ WILL ATTEMPT TO FIND AND OPEN THE NEWLY CREATED TXT FILE WITH THE SHORTNAMES INSIDE
:: CHANGE TO WHATEVER TEXT EDITOR YOU HAVE TO OPEN TXT FILES IF YOU DONT HAVE NOTEPAD++
EXIT /B
use this link, it will automatically convert any path you give to any format
https://pathconverter-pp.azurewebsites.net

How can I move files to the Recycle Bin in a Windows batch script or Perl?

I've got a Windows XP batch script which cleans some directories, but I would like to move the deleted files to trash instead of using plain del. How is this done?
It looks like the only languages I can use for this is plain batch or Perl.
use Win32::FileOp qw(Recycle);
Recycle(#ARGV);
Write a VBS script (Original Link) then call it with MyDelScript.vbs
function main()
{
if (WScript.Arguments.length != 1)
{
WScript.Echo("<Insert informative error message here>");
return;
}
var Path = WScript.Arguments(0);
var Shell = WScript.CreateObject("Shell.Application");
var Item = Shell.Namespace(0).ParseName(Path);
Item.InvokeVerb("delete");
}
The Win32::FileOp module has a Recycle function. From the docs:
Recycle #filenames
Send the files into the recycle bin. You will not get any confirmation dialogs.
Returns true if successful.
It can be done like this with plain batch and embedded VBScript. Put the following code into a file called recycle.cmd:
<!-- : Begin batch script
#echo off
if "%1"=="" (
echo Usage: %~nx0 FILE_TO_RECYCLE[...]
echo This script puts files into the recycle bin
exit /b 1
)
cscript //nologo "%~f0?.wsf" %*
exit /b %errorlevel%
----- Begin embedded wsf script --->
<job><script language="VBScript">
Set app = WScript.CreateObject("Shell.Application")
Set fso = CreateObject("Scripting.FileSystemObject")
For Each arg In WScript.Arguments
If fso.FileExists(arg) Then
Set file = fso.GetFile(arg)
Set folderItem = app.Namespace(0).ParseName(file.Path)
folderItem.InvokeVerb("delete")
Else
WScript.Echo "File not found: " & arg
End If
Next
</script></job>
Example:
echo This file is dirt.> dirt.txt
echo This file is trash.> trash.txt
recycle dirt.txt trash.txt
As you can see the script allows recycling multiple files with one command.
It does not suppport the wildcards * and ? though.
The idea of embedding VBScript inside a batch file is taken from dbenham's answer to Is it possible to embed and execute VBScript within a batch file without using a temporary file? (scroll down to UPDATE 2014-04-27).
You could use the "recycle" utility which is part of CmdUtils from MaDdoG Software. From the page listing -
Recycle, a safe replacement for the DEL command, that sends files to the recycle bin instead of deleting them. Recycle is also more flexible than DEL; you can specify multiple files at once (or use wildcards), and you can recycle whole directories at once (be careful!)
I would suggest you try its various switches before you incorporate it into your script - there is quite a bit of deviation from the default behaviour of the "del" command.
UPDATE: Contrary to my original claim that the following code does not work, it indeed seems to work. I just forgot that the file I wanted to delete was not in $ENV{TEMP} but a subdirectory of $ENV{TEMP}. The problem is, the file does not go to the Recycle Bin.
The right solution is to use Win32::FileOp but I am going to leave this script here as an example of how to use Win32::API and Win32::API::Struct. I would appreciate it if anyone can point out what I am doing wrong. For your reference:
SHFileOperation: http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx
LPSHFILEOPSTRUCT: http://msdn.microsoft.com/en-us/library/bb759795(VS.85).aspx
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec::Functions qw( catfile );
use Win32::API;
Win32::API::Struct->typedef(
SHFILEOPSTRUCT => qw(
HWND hwnd;
UINT wFunc;
LPCTSTR pFrom;
LPCTSTR pTo;
FILEOP_FLAGS fFlags;
BOOL fAnyOperationsAborted;
LPVOID hNameMappings;
LPCTSTR lpszProgressTitle;
)
);
Win32::API->Import(
shell32 => q{ int SHFileOperation( LPSHFILEOPSTRUCT lpFileOp ) }
);
my $op = Win32::API::Struct->new( 'SHFILEOPSTRUCT' );
$op->{wFunc} = 0x0003; # FO_DELETE from ShellAPI.h
$op->{fFlags} = 0x0040; # FOF_ALLOWUNDO from ShellAPI.h
my $to_delete = catfile( $ENV{TEMP}, "test.file" );
$op->{pFrom} = $to_delete . "\0\0";
my $result = SHFileOperation( $op );
if ( $result ) {
warn sprintf "The operation failed: %4.4X\n", $result;
}
else {
if ( $op->{fAnyOperationsAborted} ) {
warn "Operation was aborted\n";
}
else {
warn "The operation succeeded\n";
}
}
__END__

Resources