scan & save as PDF in c# - c#

IM using the TWAIN Scanner & gdiplus.dll.
i scanned the file , and can save as image format like *.jpg, *.bmp
but it is not allow to save as in PDF format. Showing an error unknown format picture.
this is the code,
public static bool SaveDIBAs(string picname, IntPtr bminfo, IntPtr pixdat)
{
SaveFileDialog sd = new SaveFileDialog();
sd.FileName = picname;
sd.Title = "Save bitmap as...";
sd.Filter = "PDF (*.pdf)|*.pdf";
sd.Filter = "Bitmap file (*.bmp)|*.bmp|TIFF file (*.tif)|*.tif|JPEG file (*.jpg)|*.jpg|PNG file (*.png)|*.png|PDF file (*.pdf)|*.pdf|All files (*.*)|*.*";
sd.FilterIndex = 1;
if (sd.ShowDialog() == DialogResult.OK)
return false;
Guid clsid;
if (!GetCodecClsid(sd.FileName, out clsid))
{
//MessageBox.Show("Unknown picture format for extension " + Path.GetExtension(sd.FileName),
"Image Codec", MessageBoxButtons.OK, MessageBoxIcon.Information);
return false;
}
IntPtr img = IntPtr.Zero;
int st = GdipCreateBitmapFromGdiDib( bminfo, pixdat, ref img );
if( (st != 0) || (img == IntPtr.Zero) )
return false;
st = GdipSaveImageToFile(img, sd.FileName, ref clsid, IntPtr.Zero);
GdipDisposeImage(img);
return st == 0;
}
[DllImport("gdiplus.dll", ExactSpelling=true)]
internal static extern int GdipCreateBitmapFromGdiDib( IntPtr bminfo, IntPtr pixdat, ref IntPtr image );
[DllImport("gdiplus.dll", ExactSpelling=true, CharSet=CharSet.Unicode)]
internal static extern int GdipSaveImageToFile( IntPtr image, string filename, [In] ref Guid clsid, IntPtr encparams );
[DllImport("gdiplus.dll", ExactSpelling=true)]
internal static extern int GdipDisposeImage( IntPtr image );
}
****The above code doesnt allow to save as in PDF format.****

first you'd need to acquire the image using either TWAIN or WIA and then once you've captured that image you need to convert it to PDF using something like abcPDF

Not entirely sure what you are talking about, but this library: http://sourceforge.net/projects/pdflibrary/ is excellent for saving a PDF.
For scanning (from a TWAIN Scanner?) check out http://www.codeproject.com/KB/dotnet/twaindotnet.aspx Ive done that before, and it seems to work pretty well.

It will not save as PDF at all, because GDI library of Microsoft doesnt have PDF facility, the best way to do is save your file as JPEG first in temporary file. And then use iTextSharp library or PDFSharp library to create PDF out of JPEG, you can embed your JPG/Bitmap any sort of file inside PDF using these two libraries.

if you're using GDI. Print directly to a PDF printer (I use bullzip pdf myself because it's free and has a silent print feature)

Related

SHGetFileInfo for lnk file without lnk overlay arrow

I'm trying to get the icon out of a .lnk file without the .lnk overlay appearing. The documentation has information about a flag SHGFI_ADDOVERLAYS which can be set to add an overlay, but I am looking to remove the overlay.
I have read this question, as well as the links inside of it, but I am still unable to get it working in c#.
Here is the code I have tried:
SHFILEINFO shFileInfo = new SHFILEINFO();
SHGetFileInfo(
pathToLnk,
0,
ref shFileInfo,
(uint)Marshal.SizeOf(shFileInfo),
SHGFI_ICON & ~SHGFI_LINKOVERLAY);
As well as some other configurations of the flags.
Thanks in advance for the help.
The solution is as follows:
[DllImport("Comctl32.dll")]
public static extern IntPtr ImageList_GetIcon(IntPtr himl, int i, uint flags);
SHFILEINFO fileInfo = new SHFILEINFO();
IntPtr list = SHGetFileInfo(
pathToLnk,
FileAttributes,
ref fileInfo,
(uint)Marshal.SizeOf(fileInfo),
SHGFI_SYSICONINDEX);
var iconHandle = ImageList_GetIcon(list, fileInfo.iIcon.ToInt32(), FileFlags);
Icon icn = Icon.FromHandle(iconHandle);

How do you automatically close a specific excel file when it is already open (without killing excel)?

Is it possible to close an excel file when it is already open? I have written a code that can determine if a specific excel file is already open, however I cannot close the file once it has been determined to be open. I have tried the following method (see below) to close a workbook and excel application:
// The name of my workbook is "workbook", while the Excel application is named "excel."
workbook.Close(true);
excel.Quit();
Performing the latter code does not close the already open Excel window. It may also be of assistance to know the code I am using to determine if a file is open (it is provided below):
// The following checks to see if a file is open and returns truth1 as "true" if the file is open and "false" if the file is closed.
file = new FileInfo(file_name);
truth1 = IsFileinUse(file);
// ...
protected static bool IsFileinUse(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
return true;
}
finally
{
if (stream != null)
stream.Close();
}
return false;
}
Again, I cannot create a program in which I "kill Excel". I just need know how to close an already open Excel window if its path is the same as the one I am trying to read and write to.
Please have a look on below sample code :-
using Excel = Microsoft.Office.Interop.Excel;
Excel.Application exl = new Excel.Application();
# open a file
Excel.Workbook wbook = exl.Workbooks.Open("some_file.xlsx");
# To close the file
wbook.Close();
exl.Quit();
Edit 1:-
You can refer below link if above solution not works for you :-
Closing an Excel Workbook
You can use the Windows API, to close a certain Window by it's title.
This will invoke a Window-Close, so it will prompt the user if he really wants to close:
using System.Runtime.InteropServices;
...
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
const UInt32 WM_CLOSE = 0x0010;
And then
IntPtr windowPtr = FindWindowByCaption(IntPtr.Zero, "MyFile.xlsx - Excel");
if (windowPtr == IntPtr.Zero)
{
MessageBox.Show("Document not open");
return;
}
SendMessage(windowPtr, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
If you need to force a close, even if the file has been modified, you only can kill the related excel-process. But keep in mind that this will forcefully kill other excel-windows as well, if they are running within the same excel-process-instance.
using System.Diagnostics;
...
Process[] processes = Process.GetProcesses();
foreach (Process p in processes)
{
if (p.MainWindowTitle == "MyFile.xlsx - Excel")
{
p.Kill();
break;
}
}

C# open file dialog; specify name and extension?

In C#, you can specify a filter on an OpenFileDialog object.
var dlg = new OpenFileDialog();
dlg.DefaultExt = ".xml";
dlg.Filter = "XML Files|*.xml";
Is there a way to automatically select files by name? For example, if I navigated to a folder of xml files, is there any filtering option that would automatically target "myxml.xml"?
Yes, just set the FileName property of the OpenFileDialog like this:
dlg.FileName = "myxml.xml";
However, it would be more appropriate if you use the name in the filter. Just place it instead of the star which acts as a wildcard:
dlg.Filter = "XML Files|myxml.xml";
And always remember you can have multiple filters like this: (It may be useful in the future):
"Image Files (*.bmp, *.jpg)|*.bmp;*.jpg"
// -- OR --
"Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
More documentation on filters at MSDN.
Yes, you can actually set the filter to a complete filename:
dlg.Filter = "myxml Files|myxml.xml";
Note that when this filter is selected, you won't be able to select other XML files. If you simply want to default to that filename while showing and allowing selection of any XML file, go with Fᴀʀʜᴀɴ Aɴᴀᴍ's (original) answer. And now that he copied my answer into his, you can just go with his.
What you can do is either set the FileName property like this:
dlg.FileName = "myxml.xml";
or set the Filter property like this:
dlg.Filter = "XML files|file.xml";
(it's important to check that there's no space at the end like this "file.xml ", because if there is, your file won't show up, in other words OpenFileDialog doesn't trim the Filter property)
if you don't know what the file name is beforehand, you can use DirectoryInfo and FileInfo like this:
DirectoryInfo dir = new DirectoryInfo("PATHHERE");
FileInfo[] files = dir.GetFiles();
and loop through the files to find the one you are looking for
Step 1: Add this method to your code:
[DllImport("shell32.dll", SetLastError = true)]
public static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags);
[DllImport("shell32.dll", SetLastError = true)]
public static extern void SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, [Out] out IntPtr pidl, uint sfgaoIn, [Out] out uint psfgaoOut);
public static void OpenFolderAndSelectItem(string folderPath, string file)
{
IntPtr nativeFolder;
uint psfgaoOut;
SHParseDisplayName(folderPath, IntPtr.Zero, out nativeFolder, 0, out psfgaoOut);
if (nativeFolder == IntPtr.Zero)
return;
IntPtr nativeFile;
SHParseDisplayName(System.IO.Path.Combine(folderPath, file), IntPtr.Zero, out nativeFile, 0, out psfgaoOut);
IntPtr[] fileArray;
if (nativeFile == IntPtr.Zero)
{
fileArray = new IntPtr[0];
}
else
{
fileArray = new IntPtr[] { nativeFile };
}
SHOpenFolderAndSelectItems(nativeFolder, (uint)fileArray.Length, fileArray, 0);
Marshal.FreeCoTaskMem(nativeFolder);
if (nativeFile != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(nativeFile);
}
}
Step 2: Call the method OpenFolderAndSelectItem(string folderPath, string file) to use.
Step 3: Enjoy!

How to get the location of the first byte of the file on a disk?

I need to checksum every single file on a given USB disk in a C# application. I suspect the bottleneck here is the actual read off the disk so I'm looking to make this as fast as possible.
I suspect this would be much quicker if I could read the files on the disk sequentially, in the actual order they appear on the disk (assuming the drive is not fragmented).
How can I find this information for each file from it's standard path? i.e. given a file at "F:\MyFile.txt", how can I find the start location of this file on the disk?
I'm running a C# application in Windows.
Now... I don't really know if it will be useful for you:
[StructLayout(LayoutKind.Sequential)]
public struct StartingVcnInputBuffer
{
public long StartingVcn;
}
public static readonly int StartingVcnInputBufferSizeOf = Marshal.SizeOf(typeof(StartingVcnInputBuffer));
[StructLayout(LayoutKind.Sequential)]
public struct RetrievalPointersBuffer
{
public uint ExtentCount;
public long StartingVcn;
public long NextVcn;
public long Lcn;
}
public static readonly int RetrievalPointersBufferSizeOf = Marshal.SizeOf(typeof(RetrievalPointersBuffer));
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFileW(
[MarshalAs(UnmanagedType.LPWStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
ref StartingVcnInputBuffer lpInBuffer, int nInBufferSize,
out RetrievalPointersBuffer lpOutBuffer, int nOutBufferSize,
out int lpBytesReturned, IntPtr lpOverlapped);
// Returns a FileStream that can only Read
public static void GetStartLogicalClusterNumber(string fileName, out FileStream file, out long startLogicalClusterNumber)
{
SafeFileHandle handle = CreateFileW(fileName, FileAccess.Read | (FileAccess)0x80 /* FILE_READ_ATTRIBUTES */, FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (handle.IsInvalid)
{
throw new Win32Exception();
}
file = new FileStream(handle, FileAccess.Read);
var svib = new StartingVcnInputBuffer();
int error;
RetrievalPointersBuffer rpb;
int bytesReturned;
DeviceIoControl(handle.DangerousGetHandle(), (uint)589939 /* FSCTL_GET_RETRIEVAL_POINTERS */, ref svib, StartingVcnInputBufferSizeOf, out rpb, RetrievalPointersBufferSizeOf, out bytesReturned, IntPtr.Zero);
error = Marshal.GetLastWin32Error();
switch (error)
{
case 38: /* ERROR_HANDLE_EOF */
startLogicalClusterNumber = -1; // empty file. Choose how to handle
break;
case 0: /* NO:ERROR */
case 234: /* ERROR_MORE_DATA */
startLogicalClusterNumber = rpb.Lcn;
break;
default:
throw new Win32Exception();
}
}
Note that the method will return a FileStream that you can keep open and use to read the file, or you can easily modify it to not return it (and not create it) and then reopen the file when you want to hash it.
To use:
string[] fileNames = Directory.GetFiles(#"D:\");
foreach (string fileName in fileNames)
{
try
{
long startLogicalClusterNumber;
FileStream file;
GetStartLogicalClusterNumber(fileName, out file, out startLogicalClusterNumber);
}
catch (Exception e)
{
Console.WriteLine("Skipping: {0} for {1}", fileName, e.Message);
}
}
I'm using the API described here: https://web.archive.org/web/20160130161216/http://www.wd-3.com/archive/luserland.htm . The program is much easier because you only need the initial Logical Cluster Number (the first version of the code could extract all the LCN extents, but it would be useless, because you have to hash a file from first to last byte). Note that empty files (files with length 0) don't have any cluster allocated. The function returns -1 for the cluster (ERROR_HANDLE_EOF). You can choose how to handle it.
If your drives are SSD or based on memory stick technology - forget it.
Memory sticks and other similar devices are generally based on SSD (or similar) technology, where the problem of random read/write access is actually not a problem. So you can just enumerate files and run your checksum.
You can try running this in several threads, but I am not sure that could speed up the process, it's something you may need to test. It may also vary from device to device.
Bonus
#xanatos mentioned an interesting point: "I always noticed that copying thousand of files on a memory stick is much slower than copying a single big file"
It is indeed much faster to copy one big file, rather than a pile of small files. And the reason is (usually) not because the files are located close to each other, so it's easier for hardware to read them sequentially. The problem comes to the OS that needs to keep tracking of each file.
If you ever run a procmon on Windows, you would observe huge amount of FileCreates, FileReads and FileWrites. In order to copy 100 files, OS would open each file, read its content, write to another file, close both files + lots of update operations that are sent to the file system, such as update attributes for both files, update security descriptors for both files, update directory information etc. So one copy operation has many satellite operations.

Deserializing Metafile

I have an application that works with Enhanced Metafiles.
I am able to create them, save them to disk as .emf and load them again no problem.
I do this by using the gdi32.dll methods and the DLLImport attribute.
However, to enable Version Tolerant Serialization I want to save the metafile in an object along with other data.
This essentially means that I need to serialize the metafile data as a byte array and then deserialize it again in order to reconstruct the metafile.
The problem I have is that the deserialized data would appear to be corrupted in some way, since the method that I use to reconstruct the Metafile raises a "Parameter not valid exception".
At the very least the pixel format and resolutions have changed.
Code use is below.
[DllImport("gdi32.dll")]
public static extern uint GetEnhMetaFileBits(IntPtr hemf, uint cbBuffer, byte[] lpbBuffer);
[DllImport("gdi32.dll")]
public static extern IntPtr SetEnhMetaFileBits(uint cbBuffer, byte[] lpBuffer);
[DllImport("gdi32.dll")]
public static extern bool DeleteEnhMetaFile(IntPtr hemf);
The application creates a metafile image and passes it to the method below.
private byte[] ConvertMetaFileToByteArray(Image image)
{
byte[] dataArray = null;
Metafile mf = (Metafile)image;
IntPtr enhMetafileHandle = mf.GetHenhmetafile();
uint bufferSize = GetEnhMetaFileBits(enhMetafileHandle, 0, null);
if (enhMetafileHandle != IntPtr.Zero)
{
dataArray = new byte[bufferSize];
GetEnhMetaFileBits(enhMetafileHandle, bufferSize, dataArray);
}
DeleteEnhMetaFile(enhMetafileHandle);
return dataArray;
}
At this point the dataArray is inserted into an object and serialized using a BinaryFormatter.
The saved file is then deserialized again using a BinaryFormatter and the dataArray retrieved from the object.
The dataArray is then used to reconstruct the original Metafile using the following method.
public static Image ConvertByteArrayToMetafile(byte[] data)
{
Metafile mf = null;
try
{
IntPtr hemf = SetEnhMetaFileBits((uint)data.Length, data);
mf = new Metafile(hemf, true);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
return (Image)mf;
}
The reconstructed metafile is then saved saved to disk as a .emf (Model) at which point it can be accessed by the Presenter for display.
private static void SaveFile(Image image, String filepath)
{
try
{
byte[] buffer = ConvertMetafileToByteArray(image);
File.WriteAllBytes(filepath, buffer); //will overwrite file if it exists
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
The problem is that the save to disk fails. If this same method is used to save the original Metafile before it is serialized everything is OK. So something is happening to the data during serialization/deserializtion.
Indeed if I check the Metafile properties in the debugger I can see that the ImageFlags, PropertyID, resolution and pixelformats change.
Original Format32bppRgb changes to Format32bppArgb
Original Resolution 81 changes to 96
I've trawled though google and SO and this has helped me get this far but Im now stuck.
Does any one have enough experience with Metafiles / serialization to help..?
EDIT: If I serialize/deserialize the byte array directly (without embedding in another object) I get the same problem.

Categories