I wrote this code to perform a shell style file drag operation from my wpf application to windows explorer:
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
DependencyObject src = (DependencyObject)(e.OriginalSource);
while (!(src is Control))
{
src = VisualTreeHelper.GetParent(src);
}
string clicked_element_name = src.GetType().Name;
if (clicked_element_name != "ListViewItem")
{
this.ExListView.UnselectAll();
this.ExListView.Focus();
}
else
{
ListViewItem item = src as ListViewItem;
FileExplorerItem explorerItem = item.Content as FileExplorerItem;
CDataObject dataObject = new CDataObject();
if (dataObject != null)
{
List<string> filePaths = GetSelectedListViewItemsPaths();
String fileList = "";
foreach (string filePath in filePaths)
{
fileList += filePath + char.MinValue;
}
fileList += char.MinValue;
// Allocate memory for the DROPFILES structure
int size = Marshal.SizeOf(typeof(DROPFILES)) + (fileList.Length + 2) *2; // Length of the string plus one for the double null terminator
IntPtr hGlobal = Marshal.AllocHGlobal(size);
// Initialize the DROPFILES structure
DROPFILES dropFiles = new DROPFILES();
unsafe
{
dropFiles.pFiles = sizeof(DROPFILES); // Offset to the file list
}
dropFiles.fWide = true; // Indicates that the file list is in Unicode format
byte[] dropFilesBuffer = new byte[size];
IntPtr dropFilesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(dropFiles));
Marshal.StructureToPtr(dropFiles, dropFilesPtr, true);
Marshal.Copy(dropFilesPtr, dropFilesBuffer, 0, Marshal.SizeOf(dropFiles));
Marshal.FreeHGlobal(dropFilesPtr);
// Copy the file list to the global memory
byte[] fileListBuffer = Encoding.Unicode.GetBytes(fileList.ToString());
Marshal.Copy(fileListBuffer, 0, hGlobal + Marshal.SizeOf(typeof(DROPFILES)), fileListBuffer.Length);
// Copy the DROPFILES structure to the global memory
Marshal.Copy(dropFilesBuffer, 0, hGlobal, dropFilesBuffer.Length);
System.Runtime.InteropServices.ComTypes.FORMATETC formatec = new System.Runtime.InteropServices.ComTypes.FORMATETC();
formatec.dwAspect = System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT;
formatec.lindex = -1;
formatec.tymed = System.Runtime.InteropServices.ComTypes.TYMED.TYMED_HGLOBAL;
formatec.cfFormat = (short)DataFormats.GetDataFormat(DataFormats.FileDrop).Id;
System.Runtime.InteropServices.ComTypes.STGMEDIUM stgmedium = new System.Runtime.InteropServices.ComTypes.STGMEDIUM();
stgmedium.tymed = System.Runtime.InteropServices.ComTypes.TYMED.TYMED_HGLOBAL;
stgmedium.unionmember = hGlobal;
IntPtr hwnd = new WindowInteropHelper(Application.Current.MainWindow).Handle;
dataObject.SetData(ref formatec, ref stgmedium, true);
try
{
int hr = DragHelper.SHDoDragDrop(hwnd, dataObject, null, DragDropEffects.Copy, out var result);
if (hr < 0)
{
throw Marshal.GetExceptionForHR(hr);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Marshal.FreeHGlobal(hGlobal);
}
else
{
// handle the case when dataObject is null
}
}
}
A successfull drop is performed on Windows Exlporer, hResult returns 262400, but nothing happens the file doesnt get copied, it is as no file drop ever happend.
My guess is that there is something wrong with the way i provide the filepaths to the DROPFILES Struct, but i cant figure it out.
I tried differnt ways of providing the filepaths to the struct but nothing helps.
Maybe somebody can lead me on the right path?
Related
I'm making a simple webserver to serve html, css, js & images (done in c#). I am using HttpListener and I can get the html, javascript and css files to work properly. I am just having trouble with the images. This is what I'm using currently:
if (request.RawUrl.ToLower().Contains(".png") || request.RawUrl.Contains(".ico") || request.RawUrl.ToLower().Contains(".jpg") || request.RawUrl.ToLower().Contains(".jpeg"))
{
string dir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string[] img = request.RawUrl.Split('/');
string path = dir + #"\public\imgs\" + img[img.Length - 1];
FileInfo fileInfo = new FileInfo(path);
long numBytes = fileInfo.Length;
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryReader binaryReader = new BinaryReader(fileStream);
byte[] output = binaryReader.ReadBytes((int)numBytes);
binaryReader.Close();
fileStream.Close();
var temp = System.Text.Encoding.UTF8.GetString(output);
return temp;
}
I am converting the image into a string to return them (it's the way my boss suggested). This is the method where I am handling these requests.
private static string SendResponse(HttpListenerRequest request)
This is my WebServer classes Run() method. The call to SetContentType just goes through the request.RawUrl and determines the content type.
public void Run()
{
ThreadPool.QueueUserWorkItem((o) =>
{
Console.WriteLine("StackLight Web Server is running...");
try
{
while (_listener.IsListening)
{
ThreadPool.QueueUserWorkItem((c) =>
{
var ctx = c as HttpListenerContext;
try
{
// store html content in a byte array
string responderString = _responderMethod(ctx.Request);
// set the content type
ctx.Response.Headers[HttpResponseHeader.ContentType] = SetContentType(ctx.Request.RawUrl);
byte[] buffer = buffer = Encoding.UTF8.GetBytes(responderString);
// this writes the html out from the byte array
ctx.Response.ContentLength64 = buffer.Length;
using(Stream stream = ctx.Response.OutputStream)
{
stream.Write(buffer, 0, buffer.Length);
}
}
catch (Exception ex)
{
ConfigLogger.Instance.LogCritical(LogCategory, ex);
}
}, _listener.GetContext());
}
}
catch (Exception ex)
{
ConfigLogger.Instance.LogCritical(LogCategory, ex);
}
});
}
My html page needs to display an image to the screen, it displays a broken image so far. I know the images directory is correct, I tested that.
This is where I got my code for the webserver: here
I was thinking that maybe I have to change the SendResponse method to not return a string
I figured it out. I created a class to hold the data, content type and the request.RawUrl. Then, where I was passing a string, I changed it to pass the object I created.
So, for my WebServer class, my Run method looks like this:
public void Run()
{
ThreadPool.QueueUserWorkItem((o) =>
{
Console.WriteLine("StackLight Web Server is running...");
try
{
while (_listener.IsListening)
{
ThreadPool.QueueUserWorkItem((c) =>
{
var ctx = c as HttpListenerContext;
try
{
// set the content type
ctx.Response.Headers[HttpResponseHeader.ContentType] = SetContentType(ctx.Request.RawUrl);
WebServerRequestData data = new WebServerRequestData();
// store html content in a byte array
data = _responderMethod(ctx.Request);
string res = "";
if(data.ContentType.Contains("text"))
{
char[] chars = new char[data.Content.Length/sizeof(char)];
System.Buffer.BlockCopy(data.Content, 0, chars, 0, data.Content.Length);
res = new string(chars);
data.Content = Encoding.UTF8.GetBytes(res);
}
// this writes the html out from the byte array
ctx.Response.ContentLength64 = data.Content.Length;
ctx.Response.OutputStream.Write(data.Content, 0, data.Content.Length);
}
catch (Exception ex)
{
ConfigLogger.Instance.LogCritical(LogCategory, ex);
}
finally
{
ctx.Response.OutputStream.Close();
}
}, _listener.GetContext());
}
}
catch (Exception ex)
{
ConfigLogger.Instance.LogCritical(LogCategory, ex);
}
});
}
And my SendResponse method looks like this:
private static WebServerRequestData SendResponse(HttpListenerRequest request)
{
string dir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string[] fileUrl = request.RawUrl.Split('/');
// routes
if (request.RawUrl.Contains("/"))
{
// this is the main page ('/'), all other routes can be accessed from here (including css, js, & images)
if (request.RawUrl.ToLower().Contains(".png") || request.RawUrl.ToLower().Contains(".ico") || request.RawUrl.ToLower().Contains(".jpg") || request.RawUrl.ToLower().Contains(".jpeg"))
{
try
{
string path = dir + Properties.Settings.Default.ImagesPath + fileUrl[fileUrl.Length - 1];
FileInfo fileInfo = new FileInfo(path);
path = dir + #"\public\imgs\" + fileInfo.Name;
byte[] output = File.ReadAllBytes(path);
_data = new WebServerRequestData() {Content = output, ContentType = "image/png", RawUrl = request.RawUrl};
//var temp = System.Text.Encoding.UTF8.GetString(output);
//return Convert.ToBase64String(output);
return _data;
}
catch(Exception ex)
{
ConfigLogger.Instance.LogError(LogCategory, "File could not be read.");
ConfigLogger.Instance.LogCritical(LogCategory, ex);
_errorString = string.Format("<html><head><title>Test</title></head><body>There was an error processing your request:<br />{0}</body></html>", ex.Message);
_byteData = new byte[_errorString.Length * sizeof(char)];
System.Buffer.BlockCopy(_errorString.ToCharArray(), 0, _byteData, 0, _byteData.Length);
_data = new WebServerRequestData() { Content = _byteData, ContentType = "text/html", RawUrl = request.RawUrl };
return _data;
}
}
I'm still cleaning up the code a bit but it now serves the images!
Oh... And here is the object I'm using:
public class WebServerRequestData
{
public string RawUrl { get; set; }
public string ContentType { get; set; }
public byte[] Content { get; set; }
public string RawData { get; set; }
}
Some really bad stuff here:
Empty catch. You'll never find out about many bugs.
Stuffing binary data into a string. Why? There's no encoding that is able to roundtrip binary data.
You're not disposing of ctx. I don't see why you need a manual finally block. Use using.
Untrusted callers can inject arbitrary paths into path. I could request your web.config file by navigating to /img/..\..\web.config (something like that).
Consider factoring out some common expressions into variables. You've got a Copy&Paste error with ToLower. Don't do dirty stuff and you'll have less bugs.
I want to write some data to a USB port and receive an answer from it.
I actually used the same
string cmdLine = "#00WD3000C82B*\r";
and sent it to the same machine with the SerialPort object and by using rs235 port.
its do well.. and I got the right answer
using these methods:
omronAX.WriteAction("3000", 200.ToString("0000"));
public void WriteAction(string DM_address, string Data)
{
write_action(DM_address, Data);
}
private void write_action(string DM_address, string Data)
{
GotData = "";
Puredata = "";
EndCode = "";
try
{
int ErCd = Write_2_port("WD", DM_address, Data);
if (ErCd != 0) { return; }
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private int Write_2_port(string cod_cmd, string Addr, string Data2write)
{
DReady = false;
out_data = "";
end_c = "";
char cr = Convert.ToChar(13);
string cmd = "", Dat1 = "", Dat2 = "";
Mess = "";
Dat2 = Data2write;
if (Addr.Trim().Length > 0)
{
try
{
Dat1 = String.Format("{0:0000}", Convert.ToInt16(Addr));
}
catch (FormatException ex)
{
Mess = ex.Message;
return 1;
}
catch (OverflowException ex1)
{
Mess = ex1.Message;
return 3;
}
}
int.TryParse(Dat2, out int hex);
string hexValue = hex.ToString("X");
cmd = "#" + BakN + cod_cmd + Dat1 + hexValue ;
string send2port = cmd + Checksm(cmd) + "*" + cr;
SentCommand = send2port;
try
{
// if (Sport.IsOpen == false) { Sport.Open(); }
locking = true;
Sport.WriteTimeout = 5000;
Sport.WriteLine(send2port);
int i = 0;
while (locking)
{
if (i++ == 500)
{
throw new TimeoutException("יתכן שיש בעיות תקשורת עם המערכת.");
}
Thread.Sleep(10);
}
// T:System.ArgumentNullException:
// The str parameter is null.
//
// T:System.InvalidOperationException:
// The specified port is not open.
//
// T:System.TimeoutException:
// The System.IO.Ports.SerialPort.WriteLine(System.String) method could not write
// to the stream.
}
catch (TimeoutException ex)
{
Mess = ex.Message;
throw ex;
}
catch (Exception ex)
{
Mess = ex.Message;
return 2;
}
return 0;
}
for the next code, I try using USB port and write to it the same line...
But I got nothing when I read the answer back and I got (bytesRead = 0)
in my bytesWritten, I got 15...
using System;
using System.Text;
using System.Text.RegularExpressions;
using LibUsbDotNet;
using LibUsbDotNet.Main;
namespace Examples
{
internal class ReadWrite
{
public static UsbDevice MyUsbDevice;
#region SET YOUR USB Vendor and Product ID!
public static UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(0x0590,0x005B);
#endregion
public static void Main(string[] args)
{
ErrorCode ec = ErrorCode.None;
try
{
// Find and open the USB device.
MyUsbDevice = UsbDevice.OpenUsbDevice(MyUsbFinder);
// If the device is open and ready
if (MyUsbDevice == null) throw new Exception("Device Not Found.");
// If this is a "whole" usb device (libusb-win32, linux libusb)
// it will have an IUsbDevice interface. If not (WinUSB) the
// variable will be null indicating this is an interface of a
// device.
IUsbDevice wholeUsbDevice = MyUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// This is a "whole" USB device. Before it can be used,
// the desired configuration and interface must be selected.
// Select config #1
wholeUsbDevice.SetConfiguration(1);
// Claim interface #0.
wholeUsbDevice.ClaimInterface(0);
}
// open read endpoint 1.
UsbEndpointReader reader = MyUsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);
// open write endpoint 1.
UsbEndpointWriter writer = MyUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
// Remove the exepath/startup filename text from the begining of the CommandLine.
//string cmdLine = Regex.Replace(Environment.CommandLine, "^\".+?\"^.*? |^.*? ", "", RegexOptions.Singleline);
string cmdLine = "#00WD3000A11*\r";
if (!String.IsNullOrEmpty(cmdLine))
{
int bytesWritten;
ec = writer.Write(Encoding.Default.GetBytes(cmdLine), 20000000, out bytesWritten);
if (ec != ErrorCode.None) throw new Exception(UsbDevice.LastErrorString);
byte[] readBuffer = new byte[1024];
while (ec == ErrorCode.None)
{
int bytesRead;
// If the device hasn't sent data in the last 100 milliseconds,
// a timeout error (ec = IoTimedOut) will occur.
ec = reader.Read(readBuffer, 100, out bytesRead);
if (bytesRead == 0) throw new Exception("No more bytes!");
// Write that output to the console.
Console.Write(Encoding.Default.GetString(readBuffer, 0, bytesRead));
}
Console.WriteLine("\r\nDone!\r\n");
}
else
throw new Exception("Nothing to do.");
}
catch (Exception ex)
{
Console.WriteLine();
Console.WriteLine((ec != ErrorCode.None ? ec + ":" : String.Empty) + ex.Message);
}
finally
{
if (MyUsbDevice != null)
{
if (MyUsbDevice.IsOpen)
{
// If this is a "whole" usb device (libusb-win32, linux libusb-1.0)
// it exposes an IUsbDevice interface. If not (WinUSB) the
// 'wholeUsbDevice' variable will be null indicating this is
// an interface of a device; it does not require or support
// configuration and interface selection.
IUsbDevice wholeUsbDevice = MyUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// Release interface #0.
wholeUsbDevice.ReleaseInterface(0);
}
MyUsbDevice.Close();
}
MyUsbDevice = null;
// Free usb resources
UsbDevice.Exit();
}
// Wait for user input.
Console.ReadKey();
}
}
}
}
I have no idea and I am doing wrong, thanks.
I recommend using Usb.Net (https://github.com/MelbourneDeveloper/Device.Net) instead of LibUsb. The problem with LibUsb is that is just wraps WinUSB calls. So, you are deploying an extra C dll (LibUsb) that just points to an existing Windows C DLL. LibUsb is good if you want to keep the code cross platform with Linux, but otherwise, there's not much point.
Here is sample WinUsb code (https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net/Windows/WindowsUsbDevice.cs)
public override Task InitializeAsync()
{
Dispose();
int errorCode;
if (string.IsNullOrEmpty(DeviceId))
{
throw new WindowsException($"{nameof(DeviceDefinition)} must be specified before {nameof(InitializeAsync)} can be called.");
}
_DeviceHandle = APICalls.CreateFile(DeviceId, (APICalls.GenericWrite | APICalls.GenericRead), APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, APICalls.FileAttributeNormal | APICalls.FileFlagOverlapped, IntPtr.Zero);
if (_DeviceHandle.IsInvalid)
{
//TODO: is error code useful here?
errorCode = Marshal.GetLastWin32Error();
if (errorCode > 0) throw new Exception($"Device handle no good. Error code: {errorCode}");
}
var isSuccess = WinUsbApiCalls.WinUsb_Initialize(_DeviceHandle, out var defaultInterfaceHandle);
HandleError(isSuccess, "Couldn't initialize device");
var bufferLength = (uint)Marshal.SizeOf(typeof(USB_DEVICE_DESCRIPTOR));
isSuccess = WinUsbApiCalls.WinUsb_GetDescriptor(defaultInterfaceHandle, WinUsbApiCalls.DEFAULT_DESCRIPTOR_TYPE, 0, 0, out _UsbDeviceDescriptor, bufferLength, out var lengthTransferred);
HandleError(isSuccess, "Couldn't get device descriptor");
byte i = 0;
//Get the first (default) interface
var defaultInterface = GetInterface(defaultInterfaceHandle);
_UsbInterfaces.Add(defaultInterface);
while (true)
{
isSuccess = WinUsbApiCalls.WinUsb_GetAssociatedInterface(defaultInterfaceHandle, i, out var interfacePointer);
if (!isSuccess)
{
errorCode = Marshal.GetLastWin32Error();
if (errorCode == APICalls.ERROR_NO_MORE_ITEMS) break;
throw new Exception($"Could not enumerate interfaces for device {DeviceId}. Error code: { errorCode}");
}
var associatedInterface = GetInterface(interfacePointer);
_UsbInterfaces.Add(associatedInterface);
i++;
}
IsInitialized = true;
RaiseConnected();
return Task.CompletedTask;
}
However, I did submit this sample to LibUsbDotNet and it's now the accepted Read/Write sample there (https://github.com/LibUsbDotNet/LibUsbDotNet/blob/master/src/Examples/Read.Write/ReadWrite.cs):
public static void Main(string[] args)
{
using (var context = new UsbContext())
{
context.SetDebugLevel(LogLevel.Info);
//Get a list of all connected devices
var usbDeviceCollection = context.List();
//Narrow down the device by vendor and pid
var selectedDevice = usbDeviceCollection.FirstOrDefault(d => d.ProductId == ProductId && d.VendorId == VendorId);
//Open the device
selectedDevice.Open();
//Get the first config number of the interface
selectedDevice.ClaimInterface(selectedDevice.Configs[0].Interfaces[0].Number);
//Open up the endpoints
var writeEndpoint = selectedDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
var readEnpoint = selectedDevice.OpenEndpointReader(ReadEndpointID.Ep01);
//Create a buffer with some data in it
var buffer = new byte[64];
buffer[0] = 0x3f;
buffer[1] = 0x23;
buffer[2] = 0x23;
//Write three bytes
writeEndpoint.Write(buffer, 3000, out var bytesWritten);
var readBuffer = new byte[64];
//Read some data
readEnpoint.Read(readBuffer, 3000, out var readBytes);
}
}
I had an issue getting my device to connect with another library because I was using upper case for alphabetic characters in the VID/PID. Have you tried using lower case ("0x005b" instead of "0x005B")?
In my project i want list out all files under a specific file path.Do to some user permission issue am using master file table to access all files.
Using this thread I can able to read all files under a specific file location
This one will list the file name and parent FRN,is there any way to list the file attributes also????
I want to show these details also.
Created Date
Modified Data
File Size
USN_RECORD contains these details.
You'd probably be better off using something more high-level to enumerate the contents of a path (as suggested by #HarryJohnston), but if you're determined to go down the route you've chosen, then...
Using the FileReferenceNumber you got from your USN call, you can call OpenFileByID (specifying a 0 for the dwDesiredAccess parameter - no read, no write), and you can then subsequently call GetFileInformationByHandle to get the details you need.
You don't need read or write permissions on the file to call GetFileInformationByHandle. The file attributes you're getting from the USN call and/or the GetFileInformationByHandle call can be cast to a .Net System.IO.FileAttributes.
public IEnumerable<FileDetails> EnumerateFiles(string szDriveLetter)
{
List<FileDetails> fdList = new List<FileDetails>();
try
{
var usnRecord = default(USN_RECORD);
var mft = default(MFT_ENUM_DATA);
var dwRetBytes = 0;
int cb;
var dicFrnLookup = new Dictionary<long, FSNode>();
bool bIsFile;
// This shouldn't be called more than once.
if (m_Buffer.ToInt32() != 0)
{
throw new Exception("invalid buffer");
}
// Assign buffer size
m_BufferSize = 65536;
//64KB
// Allocate a buffer to use for reading records.
m_Buffer = Marshal.AllocHGlobal(m_BufferSize);
// correct path
szDriveLetter = szDriveLetter.TrimEnd('\\');
// Open the volume handle
m_hCJ = OpenVolume(szDriveLetter);
uint iny = NativeMethods.GetLastError();
// Check if the volume handle is valid.
if (m_hCJ == INVALID_HANDLE_VALUE)
{
throw new Exception("Couldn't open handle to the volume.");
}
mft.StartFileReferenceNumber = 0;
mft.LowUsn = 0;
mft.HighUsn = long.MaxValue;
do
{
if (DeviceIoControl(m_hCJ, FSCTL_ENUM_USN_DATA, ref mft, Marshal.SizeOf(mft), m_Buffer, m_BufferSize, ref dwRetBytes, IntPtr.Zero))
{
cb = dwRetBytes;
// Pointer to the first record
IntPtr pUsnRecord = new IntPtr(m_Buffer.ToInt32() + 8);
while ((dwRetBytes > 8))
{
// Copy pointer to USN_RECORD structure.
usnRecord = (USN_RECORD)Marshal.PtrToStructure(pUsnRecord, usnRecord.GetType());
// The filename within the USN_RECORD.
string fileName = Marshal.PtrToStringUni(new IntPtr(pUsnRecord.ToInt32() + usnRecord.FileNameOffset), usnRecord.FileNameLength / 2);
bIsFile = !usnRecord.FileAttribute.HasFlag(FileAttributes.Directory);
dicFrnLookup.Add(usnRecord.FileReferenceNumber, new FSNode(usnRecord.ParentFileReferenceNumber, fileName, bIsFile));
// Pointer to the next record in the buffer.
pUsnRecord = new IntPtr(pUsnRecord.ToInt32() + usnRecord.RecordLength);
dwRetBytes -= usnRecord.RecordLength;
}
// The first 8 bytes is always the start of the next USN.
mft.StartFileReferenceNumber = Marshal.ReadInt64(m_Buffer, 0);
}
else
{
break; // TODO: might not be correct. Was : Exit Do
}
} while (!(cb <= 8));
// Resolve all paths for Files
foreach (FSNode oFSNode in dicFrnLookup.Values.Where(o => o.IsFile))
{
FileDetails fd = new FileDetails();
string sFullPath = oFSNode.FileName;
FSNode oParentFSNode = oFSNode;
while (dicFrnLookup.TryGetValue(oParentFSNode.ParentFRN, out oParentFSNode))
{
sFullPath = string.Concat(oParentFSNode.FileName, "\\", sFullPath);
}
sFullPath = string.Concat(szDriveLetter, "\\", sFullPath);
//File Attribute details
WIN32_FILE_ATTRIBUTE_DATA data;
if (NativeMethods.GetFileAttributesEx(#sFullPath, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, out data))
{
fd.FileSize = Convert.ToDouble(data.fileSizeLow);
}
long highBits = data.creationTime.dwHighDateTime;
highBits = highBits << 32;
DateTime createdDate = DateTime.FromFileTimeUtc(highBits + (uint)data.creationTime.dwLowDateTime);
fd.CreatedDate = createdDate.ToString(CultureInfo.CurrentCulture);
fd.CreatedYear = createdDate.Year;
fd.FileType = data.filetype;
long highBitsModified = data.lastWriteTime.dwHighDateTime;
highBitsModified = highBitsModified << 32;
DateTime modifiedDate = DateTime.FromFileTimeUtc(highBitsModified + (uint)data.creationTime.dwLowDateTime);
fd.ModifiedYear = modifiedDate.Year;
fd.ModifiedDate = modifiedDate.ToString(CultureInfo.CurrentCulture);
fd.FilePath = sFullPath;
fd.MachineName = SystemInformation.ComputerName;
List<string> names = sFullPath.Split('.').ToList();
fd.FileType = "." + names.LastOrDefault();
List<string> folders = sFullPath.Split('\\').ToList();
//fd.FileName = folders.LastOrDefault();
fd.FileName = oFSNode.FileName;
fdList.Add(fd);
yield return fd;
}
}
finally
{
//// cleanup
Cleanup();
}
}
public class NativeMethods
{
[DllImport("KERNEL32.dll", CharSet = CharSet.None)]
public static extern bool GetFileAttributesEx(string path, GET_FILEEX_INFO_LEVELS level, out WIN32_FILE_ATTRIBUTE_DATA data);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
I'm building a .Net 4.0 application for remote control of a scanner device. I have tried both TWAIN and WIA libraries, but I have the same problem. Scanning images without scanner selection and scanning settings dialogs.
I found a useful article on WIA scripting in .Net, and modified it to this:
private Image Scan(string deviceName)
{
WiaClass wiaManager = null; // WIA manager COM object
CollectionClass wiaDevs = null; // WIA devices collection COM object
ItemClass wiaRoot = null; // WIA root device COM object
CollectionClass wiaPics = null; // WIA collection COM object
ItemClass wiaItem = null; // WIA image COM object
try
{
// create COM instance of WIA manager
wiaManager = new WiaClass();
// call Wia.Devices to get all devices
wiaDevs = wiaManager.Devices as CollectionClass;
if ((wiaDevs == null) || (wiaDevs.Count == 0))
{
throw new Exception("No WIA devices found!");
}
object device = null;
foreach (IWiaDeviceInfo currentDevice in wiaManager.Devices)
{
if (currentDevice.Name == deviceName)
{
device = currentDevice;
break;
}
}
if (device == null)
{
throw new Exception
(
"Device with name \"" +
deviceName +
"\" could not be found."
);
}
// select device
wiaRoot = (ItemClass)wiaManager.Create(ref device);
// something went wrong
if (wiaRoot == null)
{
throw new Exception
(
"Could not initialize device \"" +
deviceName + "\"."
);
}
wiaPics = wiaRoot.GetItemsFromUI
(
WiaFlag.SingleImage,
WiaIntent.ImageTypeColor
) as CollectionClass;
if (wiaPics == null || wiaPics.Count == 0)
{
throw new Exception("Could not scan image.");
}
Image image = null;
// enumerate all the pictures the user selected
foreach (object wiaObj in wiaPics)
{
if (image == null)
{
wiaItem = (ItemClass)Marshal.CreateWrapperOfType
(
wiaObj, typeof(ItemClass)
);
// create temporary file for image
string tempFile = Path.GetTempFileName();
// transfer picture to our temporary file
wiaItem.Transfer(tempFile, false);
// create Image instance from file
image = Image.FromFile(tempFile);
}
// release enumerated COM object
Marshal.ReleaseComObject(wiaObj);
}
if (image == null)
{
throw new Exception("Error reading scanned image.");
}
return image;
}
finally
{
// release WIA image COM object
if (wiaItem != null)
Marshal.ReleaseComObject(wiaItem);
// release WIA collection COM object
if (wiaPics != null)
Marshal.ReleaseComObject(wiaPics);
// release WIA root device COM object
if (wiaRoot != null)
Marshal.ReleaseComObject(wiaRoot);
// release WIA devices collection COM object
if (wiaDevs != null)
Marshal.ReleaseComObject(wiaDevs);
// release WIA manager COM object
if (wiaManager != null)
Marshal.ReleaseComObject(wiaManager);
}
}
With this I actually managed to select the device from configuration (input parameter of the Scan method) and retrieve the resulting image after scan.
But the problem with scanning options dialog (Scan using DEVICENAME). As this is a remote control application, dialog will not be visible to the user, so I need to either skip it using default settings, or use settings from a configuration if necessary.
Scanning options dialog:
In the end I did not use the code written in the question for scanning dialogs. I found a useful example of Scanning with Windows Image Acquisition 2.0 which by the way also had a blocking dialog, but this was easily modified and in moments I had a simple class with a Scan(string scannerId) function which would just scan with a selected device and nothing more, see code () below:
using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
namespace WIATest
{
class WIAScanner
{
const string wiaFormatBMP = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}";
class WIA_DPS_DOCUMENT_HANDLING_SELECT
{
public const uint FEEDER = 0x00000001;
public const uint FLATBED = 0x00000002;
}
class WIA_DPS_DOCUMENT_HANDLING_STATUS
{
public const uint FEED_READY = 0x00000001;
}
class WIA_PROPERTIES
{
public const uint WIA_RESERVED_FOR_NEW_PROPS = 1024;
public const uint WIA_DIP_FIRST = 2;
public const uint WIA_DPA_FIRST = WIA_DIP_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
public const uint WIA_DPC_FIRST = WIA_DPA_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
//
// Scanner only device properties (DPS)
//
public const uint WIA_DPS_FIRST = WIA_DPC_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
public const uint WIA_DPS_DOCUMENT_HANDLING_STATUS = WIA_DPS_FIRST + 13;
public const uint WIA_DPS_DOCUMENT_HANDLING_SELECT = WIA_DPS_FIRST + 14;
}
/// <summary>
/// Use scanner to scan an image (with user selecting the scanner from a dialog).
/// </summary>
/// <returns>Scanned images.</returns>
public static List<Image> Scan()
{
WIA.ICommonDialog dialog = new WIA.CommonDialog();
WIA.Device device = dialog.ShowSelectDevice(WIA.WiaDeviceType.UnspecifiedDeviceType, true, false);
if (device != null)
{
return Scan(device.DeviceID);
}
else
{
throw new Exception("You must select a device for scanning.");
}
}
/// <summary>
/// Use scanner to scan an image (scanner is selected by its unique id).
/// </summary>
/// <param name="scannerName"></param>
/// <returns>Scanned images.</returns>
public static List<Image> Scan(string scannerId)
{
List<Image> images = new List<Image>();
bool hasMorePages = true;
while (hasMorePages)
{
// select the correct scanner using the provided scannerId parameter
WIA.DeviceManager manager = new WIA.DeviceManager();
WIA.Device device = null;
foreach (WIA.DeviceInfo info in manager.DeviceInfos)
{
if (info.DeviceID == scannerId)
{
// connect to scanner
device = info.Connect();
break;
}
}
// device was not found
if (device == null)
{
// enumerate available devices
string availableDevices = "";
foreach (WIA.DeviceInfo info in manager.DeviceInfos)
{
availableDevices += info.DeviceID + "n";
}
// show error with available devices
throw new Exception("The device with provided ID could not be found. Available Devices:n" + availableDevices);
}
WIA.Item item = device.Items[1] as WIA.Item;
try
{
// scan image
WIA.ICommonDialog wiaCommonDialog = new WIA.CommonDialog();
WIA.ImageFile image = (WIA.ImageFile)wiaCommonDialog.ShowTransfer(item, wiaFormatBMP, false);
// save to temp file
string fileName = Path.GetTempFileName();
File.Delete(fileName);
image.SaveFile(fileName);
image = null;
// add file to output list
images.Add(Image.FromFile(fileName));
}
catch (Exception exc)
{
throw exc;
}
finally
{
item = null;
//determine if there are any more pages waiting
WIA.Property documentHandlingSelect = null;
WIA.Property documentHandlingStatus = null;
foreach (WIA.Property prop in device.Properties)
{
if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_SELECT)
documentHandlingSelect = prop;
if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_STATUS)
documentHandlingStatus = prop;
}
// assume there are no more pages
hasMorePages = false;
// may not exist on flatbed scanner but required for feeder
if (documentHandlingSelect != null)
{
// check for document feeder
if ((Convert.ToUInt32(documentHandlingSelect.get_Value()) &amp;amp; WIA_DPS_DOCUMENT_HANDLING_SELECT.FEEDER) != 0)
{
hasMorePages = ((Convert.ToUInt32(documentHandlingStatus.get_Value()) &amp;amp; WIA_DPS_DOCUMENT_HANDLING_STATUS.FEED_READY) != 0);
}
}
}
}
return images;
}
/// <summary>
/// Gets the list of available WIA devices.
/// </summary>
/// <returns></returns>
public static List<string> GetDevices()
{
List<string> devices = new List<string>();
WIA.DeviceManager manager = new WIA.DeviceManager();
foreach (WIA.DeviceInfo info in manager.DeviceInfos)
{
devices.Add(info.DeviceID);
}
return devices;
}
}
}
First off, many thanks to Miljenko Barbir for his above solution, it works great.
I would like to add that if you want zero dialogs, you can use (from Milijenko's demo code)
WIA.ImageFile image = item.Transfer(wiaFormatBMP);
instead of
WIA.ImageFile image = (WIA.ImageFile)wiaCommonDialog.ShowTransfer(item, wiaFormatBMP, false);
This basically removes the progress bar as well, so you get scanning without any dialogs.
// show scanner view
guif.ShowUI = 0;
guif.ModalUI = 0;
You can see in this code that's I've implemented.
I want to enable drag and drop from our windows forms based application to Windows Explorer. The big problem: The files are stored in a database, so I need to use delayed data rendering. There is an article on codeproject.com, but the author is using a H_GLOBAL object which leads to memory problems with files bigger than aprox. 20 MB. I haven't found a working solution for using an IStream Object instead. I think this must be possible to implement, because this isn't an unusual case. (A FTP program needs such a feature too, for example)
Edit: Is it possible to get an event when the user drops the file? So I could for example copy it to temp and the explorer gets it from there? Maybe there is an alternative approach for my problem...
AFAIK, there is not working article about this for .net. So you should write it by yourself, this is somewhat complicate, because .net DataObject class is limited. I have working example of the opposite task (accepting delayed rendering files from explorer), but it is easier, because I do not needed own IDataObject implementation.
So your task will be:
Find working IDataObject implementation in .net. I recommend you look here (Shell Style Drag and Drop in .NET (WPF and WinForms))
You also need an IStream wrapper for managed stream (it is relatively easy to implement)
Implement delayed rendering using information from MSDN (Shell Clipboard Formats)
This is the starting point, and in general enough information to implement such feature. With bit of patience and several unsuccessful attempts you will do it :)
Update: The following code lacks many necessary methods and functions, but the main logic is here.
// ...
private static IEnumerable<IVirtualItem> GetDataObjectContent(System.Windows.Forms.IDataObject dataObject)
{
if (dataObject == null)
return null;
List<IVirtualItem> Result = new List<IVirtualItem>();
bool WideDescriptor = dataObject.GetDataPresent(ShlObj.CFSTR_FILEDESCRIPTORW);
bool AnsiDescriptor = dataObject.GetDataPresent(ShlObj.CFSTR_FILEDESCRIPTORA);
if (WideDescriptor || AnsiDescriptor)
{
IDataObject NativeDataObject = dataObject as IDataObject;
if (NativeDataObject != null)
{
object Data = null;
if (WideDescriptor)
Data = dataObject.GetData(ShlObj.CFSTR_FILEDESCRIPTORW);
else
if (AnsiDescriptor)
Data = dataObject.GetData(ShlObj.CFSTR_FILEDESCRIPTORA);
Stream DataStream = Data as Stream;
if (DataStream != null)
{
Dictionary<string, VirtualClipboardFolder> FolderMap =
new Dictionary<string, VirtualClipboardFolder>(StringComparer.OrdinalIgnoreCase);
BinaryReader Reader = new BinaryReader(DataStream);
int Count = Reader.ReadInt32();
for (int I = 0; I < Count; I++)
{
VirtualClipboardItem ClipboardItem;
if (WideDescriptor)
{
FILEDESCRIPTORW Descriptor = ByteArrayHelper.ReadStructureFromStream<FILEDESCRIPTORW>(DataStream);
if (((Descriptor.dwFlags & FD.FD_ATTRIBUTES) > 0) && ((Descriptor.dwFileAttributes & FileAttributes.Directory) > 0))
ClipboardItem = new VirtualClipboardFolder(Descriptor);
else
ClipboardItem = new VirtualClipboardFile(Descriptor, NativeDataObject, I);
}
else
{
FILEDESCRIPTORA Descriptor = ByteArrayHelper.ReadStructureFromStream<FILEDESCRIPTORA>(DataStream);
if (((Descriptor.dwFlags & FD.FD_ATTRIBUTES) > 0) && ((Descriptor.dwFileAttributes & FileAttributes.Directory) > 0))
ClipboardItem = new VirtualClipboardFolder(Descriptor);
else
ClipboardItem = new VirtualClipboardFile(Descriptor, NativeDataObject, I);
}
string ParentFolder = Path.GetDirectoryName(ClipboardItem.FullName);
if (string.IsNullOrEmpty(ParentFolder))
Result.Add(ClipboardItem);
else
{
VirtualClipboardFolder Parent = FolderMap[ParentFolder];
ClipboardItem.Parent = Parent;
Parent.Content.Add(ClipboardItem);
}
VirtualClipboardFolder ClipboardFolder = ClipboardItem as VirtualClipboardFolder;
if (ClipboardFolder != null)
FolderMap.Add(PathHelper.ExcludeTrailingDirectorySeparator(ClipboardItem.FullName), ClipboardFolder);
}
}
}
}
return Result.Count > 0 ? Result : null;
}
// ...
public VirtualClipboardFile : VirtualClipboardItem, IVirtualFile
{
// ...
public Stream Open(FileMode mode, FileAccess access, FileShare share, FileOptions options, long startOffset)
{
if ((mode != FileMode.Open) || (access != FileAccess.Read))
throw new ArgumentException("Only open file mode and read file access supported.");
System.Windows.Forms.DataFormats.Format Format = System.Windows.Forms.DataFormats.GetFormat(ShlObj.CFSTR_FILECONTENTS);
if (Format == null)
return null;
FORMATETC FormatEtc = new FORMATETC();
FormatEtc.cfFormat = (short)Format.Id;
FormatEtc.dwAspect = DVASPECT.DVASPECT_CONTENT;
FormatEtc.lindex = FIndex;
FormatEtc.tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_HGLOBAL;
STGMEDIUM Medium;
FDataObject.GetData(ref FormatEtc, out Medium);
try
{
switch (Medium.tymed)
{
case TYMED.TYMED_ISTREAM:
IStream MediumStream = (IStream)Marshal.GetTypedObjectForIUnknown(Medium.unionmember, typeof(IStream));
ComStreamWrapper StreamWrapper = new ComStreamWrapper(MediumStream, FileAccess.Read, ComRelease.None);
// Seek from beginning
if (startOffset > 0)
if (StreamWrapper.CanSeek)
StreamWrapper.Seek(startOffset, SeekOrigin.Begin);
else
{
byte[] Null = new byte[256];
int Readed = 1;
while ((startOffset > 0) && (Readed > 0))
{
Readed = StreamWrapper.Read(Null, 0, (int)Math.Min(Null.Length, startOffset));
startOffset -= Readed;
}
}
StreamWrapper.Closed += delegate(object sender, EventArgs e)
{
ActiveX.ReleaseStgMedium(ref Medium);
Marshal.FinalReleaseComObject(MediumStream);
};
return StreamWrapper;
case TYMED.TYMED_HGLOBAL:
byte[] FileContent;
IntPtr MediumLock = Windows.GlobalLock(Medium.unionmember);
try
{
long Size = FSize.HasValue ? FSize.Value : Windows.GlobalSize(MediumLock).ToInt64();
FileContent = new byte[Size];
Marshal.Copy(MediumLock, FileContent, 0, (int)Size);
}
finally
{
Windows.GlobalUnlock(Medium.unionmember);
}
ActiveX.ReleaseStgMedium(ref Medium);
Stream ContentStream = new MemoryStream(FileContent, false);
ContentStream.Seek(startOffset, SeekOrigin.Begin);
return ContentStream;
default:
throw new ApplicationException(string.Format("Unsupported STGMEDIUM.tymed ({0})", Medium.tymed));
}
}
catch
{
ActiveX.ReleaseStgMedium(ref Medium);
throw;
}
}
// ...
Googlers may find this useful: download a file using windows IStream