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.
Related
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?
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")?
1) I am trying to make a simple BHO in C# like here already answered: https://stackoverflow.com/a/5740004/285594
2) But unfortunately they all tried less then IE11, where some made it work and some failed too
3) after following everything as mentioned in that answer, i also purchased official code sign but it simply does not working in IE11 Windows 7 64-bit.
You can download my prepared version of Visual studio 2013: which includes all the source code and details for IE11:
https://www.dropbox.com/s/60kg212vkjb7yud/ClassLibrary2.rar
Q. Can anyone please advise/suggest/help how can i make one hello world of this BHO?
I have also tried others sample from codeproject, but still none of them i was able to make work yet, trying since 4 weeks, i am lost, please advise what is wrong in my ClassLibrary2.rar which is not hilighting the text "browser"?
I am completely lost, please advise.
EDIT:
IEAddon.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.Win32;
using mshtml;
using SHDocVw;
namespace InternetExplorerExtension
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("D40C654D-7C51-4EB3-95B2-1E23905C2A2D")]
[ProgId("MyBHO.WordHighlighter")]
public class WordHighlighterBHO : IObjectWithSite, IOleCommandTarget
{
const string DefaultTextToHighlight = "browser";
IWebBrowser2 browser;
private object site;
#region Highlight Text
void OnDocumentComplete(object pDisp, ref object URL)
{
try
{
// This will prevent this method being executed more than once.
if (pDisp != this.site)
return;
var document2 = browser.Document as IHTMLDocument2;
var document3 = browser.Document as IHTMLDocument3;
var window = document2.parentWindow;
window.execScript(#"function FncAddedByAddon() { alert('Message added by addon.'); }");
Queue<IHTMLDOMNode> queue = new Queue<IHTMLDOMNode>();
foreach (IHTMLDOMNode eachChild in document3.childNodes)
queue.Enqueue(eachChild);
while (queue.Count > 0)
{
// replacing desired text with a highlighted version of it
var domNode = queue.Dequeue();
var textNode = domNode as IHTMLDOMTextNode;
if (textNode != null)
{
if (textNode.data.Contains(TextToHighlight))
{
var newText = textNode.data.Replace(TextToHighlight, "<span style='background-color: yellow; cursor: hand;' onclick='javascript:FncAddedByAddon()' title='Click to open script based alert window.'>" + TextToHighlight + "</span>");
var newNode = document2.createElement("span");
newNode.innerHTML = newText;
domNode.replaceNode((IHTMLDOMNode)newNode);
}
}
else
{
// adding children to collection
var x = (IHTMLDOMChildrenCollection)(domNode.childNodes);
foreach (IHTMLDOMNode eachChild in x)
{
if (eachChild is mshtml.IHTMLScriptElement)
continue;
if (eachChild is mshtml.IHTMLStyleElement)
continue;
queue.Enqueue(eachChild);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
#endregion
#region Load and Save Data
static string TextToHighlight = DefaultTextToHighlight;
public static string RegData = "Software\\MyIEExtension";
[DllImport("ieframe.dll")]
public static extern int IEGetWriteableHKCU(ref IntPtr phKey);
private static void SaveOptions()
{
// In IE 7,8,9,(desktop)10 tabs run in Protected Mode
// which prohibits writes to HKLM, HKCU.
// Must ask IE for "Writable" registry section pointer
// which will be something like HKU/S-1-7***/Software/AppDataLow/
// In "metro" IE 10 mode, tabs run in "Enhanced Protected Mode"
// where BHOs are not allowed to run, except in edge cases.
// see http://blogs.msdn.com/b/ieinternals/archive/2012/03/23/understanding-ie10-enhanced-protected-mode-network-security-addons-cookies-metro-desktop.aspx
IntPtr phKey = new IntPtr();
var answer = IEGetWriteableHKCU(ref phKey);
RegistryKey writeable_registry = RegistryKey.FromHandle(
new Microsoft.Win32.SafeHandles.SafeRegistryHandle(phKey, true)
);
RegistryKey registryKey = writeable_registry.OpenSubKey(RegData, true);
if (registryKey == null)
registryKey = writeable_registry.CreateSubKey(RegData);
registryKey.SetValue("Data", TextToHighlight);
writeable_registry.Close();
}
private static void LoadOptions()
{
// In IE 7,8,9,(desktop)10 tabs run in Protected Mode
// which prohibits writes to HKLM, HKCU.
// Must ask IE for "Writable" registry section pointer
// which will be something like HKU/S-1-7***/Software/AppDataLow/
// In "metro" IE 10 mode, tabs run in "Enhanced Protected Mode"
// where BHOs are not allowed to run, except in edge cases.
// see http://blogs.msdn.com/b/ieinternals/archive/2012/03/23/understanding-ie10-enhanced-protected-mode-network-security-addons-cookies-metro-desktop.aspx
IntPtr phKey = new IntPtr();
var answer = IEGetWriteableHKCU(ref phKey);
RegistryKey writeable_registry = RegistryKey.FromHandle(
new Microsoft.Win32.SafeHandles.SafeRegistryHandle(phKey, true)
);
RegistryKey registryKey = writeable_registry.OpenSubKey(RegData, true);
if (registryKey == null)
registryKey = writeable_registry.CreateSubKey(RegData);
registryKey.SetValue("Data", TextToHighlight);
if (registryKey == null)
{
TextToHighlight = DefaultTextToHighlight;
}
else
{
TextToHighlight = (string)registryKey.GetValue("Data");
}
writeable_registry.Close();
}
#endregion
[Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
[InterfaceType(1)]
public interface IServiceProvider
{
int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject);
}
#region Implementation of IObjectWithSite
int IObjectWithSite.SetSite(object site)
{
this.site = site;
if (site != null)
{
LoadOptions();
var serviceProv = (IServiceProvider)this.site;
var guidIWebBrowserApp = Marshal.GenerateGuidForType(typeof(IWebBrowserApp)); // new Guid("0002DF05-0000-0000-C000-000000000046");
var guidIWebBrowser2 = Marshal.GenerateGuidForType(typeof(IWebBrowser2)); // new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E");
IntPtr intPtr;
serviceProv.QueryService(ref guidIWebBrowserApp, ref guidIWebBrowser2, out intPtr);
browser = (IWebBrowser2)Marshal.GetObjectForIUnknown(intPtr);
((DWebBrowserEvents2_Event)browser).DocumentComplete +=
new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
}
else
{
((DWebBrowserEvents2_Event)browser).DocumentComplete -=
new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
browser = null;
}
return 0;
}
int IObjectWithSite.GetSite(ref Guid guid, out IntPtr ppvSite)
{
IntPtr punk = Marshal.GetIUnknownForObject(browser);
int hr = Marshal.QueryInterface(punk, ref guid, out ppvSite);
Marshal.Release(punk);
return hr;
}
#endregion
#region Implementation of IOleCommandTarget
int IOleCommandTarget.QueryStatus(IntPtr pguidCmdGroup, uint cCmds, ref OLECMD prgCmds, IntPtr pCmdText)
{
return 0;
}
int IOleCommandTarget.Exec(IntPtr pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
try
{
// Accessing the document from the command-bar.
var document = browser.Document as IHTMLDocument2;
var window = document.parentWindow;
var result = window.execScript(#"alert('You will now be allowed to configure the text to highlight...');");
var form = new HighlighterOptionsForm();
form.InputText = TextToHighlight;
if (form.ShowDialog() != DialogResult.Cancel)
{
TextToHighlight = form.InputText;
SaveOptions();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return 0;
}
#endregion
#region Registering with regasm
public static string RegBHO = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects";
public static string RegCmd = "Software\\Microsoft\\Internet Explorer\\Extensions";
[ComRegisterFunction]
public static void RegisterBHO(Type type)
{
string guid = type.GUID.ToString("B");
// BHO
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(RegBHO, true);
if (registryKey == null)
registryKey = Registry.LocalMachine.CreateSubKey(RegBHO);
RegistryKey key = registryKey.OpenSubKey(guid);
if (key == null)
key = registryKey.CreateSubKey(guid);
key.SetValue("Alright", 1);
registryKey.Close();
key.Close();
}
// Command
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(RegCmd, true);
if (registryKey == null)
registryKey = Registry.LocalMachine.CreateSubKey(RegCmd);
RegistryKey key = registryKey.OpenSubKey(guid);
if (key == null)
key = registryKey.CreateSubKey(guid);
key.SetValue("ButtonText", "Highlighter options");
key.SetValue("CLSID", "{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}");
key.SetValue("ClsidExtension", guid);
key.SetValue("Icon", "");
key.SetValue("HotIcon", "");
key.SetValue("Default Visible", "Yes");
key.SetValue("MenuText", "&Highlighter options");
key.SetValue("ToolTip", "Highlighter options");
//key.SetValue("KeyPath", "no");
registryKey.Close();
key.Close();
}
}
[ComUnregisterFunction]
public static void UnregisterBHO(Type type)
{
string guid = type.GUID.ToString("B");
// BHO
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(RegBHO, true);
if (registryKey != null)
registryKey.DeleteSubKey(guid, false);
}
// Command
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(RegCmd, true);
if (registryKey != null)
registryKey.DeleteSubKey(guid, false);
}
}
#endregion
}
}
i'm trying to do the same - and i've just noticed that in build log there was an error
Failure adding assembly to the cache: Attempt to install an assembly
without a strong name
so i've added *.snk and highlighting worked (using ie11 to, x64), but 'highlight options' menu item isn't working
IEExtension example
although your IE11 runs in a 64bit Windows, but the default IE instance is 32bit version. The enhanced protection mode need to be enabled so that IE11 will run in 64bit mode.
Another trick is for 32bit IE, you have to register 32bit extension, and vice versa for 64bit. My suggestion is as follows:
make sure your IE11 mode is 32bit or 64bit
register only 32bit or 64bit extension, if both registered, the extension can NOT work either. You have to double check your registry to delete the one un-necessary
I warmly suggest you this post of Pavel Zolnikov published in 2002!
http://www.codeproject.com/Articles/2219/Extending-Explorer-with-Band-Objects-using-NET-and
It is based on the use of Band objects and is compiled using .Net 2.0.
As you will read on the post comments, it works perfectly well for IE 11 and on Windows 7 and Windows 10.
Tutorial source code is provided and opens and compiles well with Visual Studio 2013.
Enjoy!
I'm new to the EDSDK 2.8
At the moment, my program can take pictures. However, when a picture is taken, that picture is temporarily stored in a buffer in the Canon camera. I would like to know how to save it directory to the PC?
Does anyone have any ideas? Or sample code in c# or vb.net?
I am doing the same in that I have no memory card in the camera and want to transfer the image to the host computer after the take picture command is sent. Here is what worked for me to get the ObjectEventHandler callback called when no memory card is installed in the Canon EOS50D.
EdsUInt32 setsaveto = kEdsSaveTo_Both;
err = EdsSetPropertyData(camera, kEdsPropID_SaveTo, 0, sizeof(setsaveto), &setsaveto);
Voila, the callback gets called and I can then proceed to do the getCapturedItem() function as Wayne has posted in the earlier post.
Here is what I have done:
First, you have to register for the callback event when an object is created (ie, a picture). I did this in a registerEvents method that I created:
// Register OBJECT events
edsObjectEventHandler = new EDSDK.EdsObjectEventHandler(objectEventHandler);
error = EDSDK.EdsSetObjectEventHandler(this.CameraDevice,
EDSDK.ObjectEvent_All, edsObjectEventHandler, IntPtr.Zero);
if (EDSDK.EDS_ERR_OK != error)
{
throw new CameraEventRegistrationException("Unable to
register object events with the camera!", error);
}
The objectEventHandler is the method that will be called when a picture is created.
The method needs to conform to the interface dictated by the API. Here's an example implementation of that method:
/// <summary>
/// Registered callback function for recieving object events
/// </summary>
/// <param name="inEvent">Indicate the event type supplemented.</param>
/// <param name="inRef">Returns a reference to objects created by the event.</param>
/// <param name="inContext">Passes inContext without modification</param>
/// <returns>Status 0 (OK)</returns>
private uint objectEventHandler(uint inEvent, IntPtr inRef, IntPtr inContext)
{
switch (inEvent)
{
case EDSDK.ObjectEvent_DirItemCreated:
this.invokeNewItemCreatedEvent(new NewItemCreatedEventArgs(getCapturedItem(inRef)));
Console.WriteLine("Directory Item Created");
break;
case EDSDK.ObjectEvent_DirItemRequestTransfer:
Console.WriteLine("Directory Item Requested Transfer");
break;
default:
Console.WriteLine(String.Format("ObjectEventHandler: event {0}, ref {1}", inEvent.ToString("X"), inRef.ToString()));
break;
}
return 0x0;
}
In this example I turn around and spawn my own event, which has the reference to the stream object. This is handled by the following code:
/// <summary>
/// Gets a photo or video clip from the camera
/// </summary>
/// <param name="directoryItem">Reference to the item that the camera captured.</param>
/// <returns></returns>
private CapturedItem getCapturedItem(IntPtr directoryItem)
{
uint err = EDSDK.EDS_ERR_OK;
IntPtr stream = IntPtr.Zero;
EDSDK.EdsDirectoryItemInfo dirItemInfo;
err = EDSDK.EdsGetDirectoryItemInfo(directoryItem, out dirItemInfo);
if (err != EDSDK.EDS_ERR_OK)
{
throw new CameraException("Unable to get captured item info!", err);
}
// Fill the stream with the resulting image
if (err == EDSDK.EDS_ERR_OK)
{
err = EDSDK.EdsCreateMemoryStream((uint)dirItemInfo.Size, out stream);
}
// Copy the stream to a byte[] and
if (err == EDSDK.EDS_ERR_OK)
{
err = EDSDK.EdsDownload(directoryItem, (uint)dirItemInfo.Size, stream);
}
// Create the returned item
CapturedItem item = new CapturedItem();
if (err == EDSDK.EDS_ERR_OK)
{
IntPtr imageRef = IntPtr.Zero;
err = EDSDK.EdsCreateImageRef(stream, out imageRef);
if (err == EDSDK.EDS_ERR_OK)
{
EDSDK.EdsImageInfo info;
err = EDSDK.EdsGetImageInfo(imageRef, EDSDK.EdsImageSource.FullView, out info);
if (err == EDSDK.EDS_ERR_OK)
{
item.Dimensions = new com.waynehartman.util.graphics.Dimension((int)info.Width, (int)info.Height);
EDSDK.EdsRelease(imageRef);
}
}
}
if (err == EDSDK.EDS_ERR_OK)
{
byte[] buffer = new byte[(int)dirItemInfo.Size];
GCHandle gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr address = gcHandle.AddrOfPinnedObject();
IntPtr streamPtr = IntPtr.Zero;
err = EDSDK.EdsGetPointer(stream, out streamPtr);
if (err != EDSDK.EDS_ERR_OK)
{
throw new CameraDownloadException("Unable to get resultant image.", err);
}
try
{
Marshal.Copy(streamPtr, buffer, 0, (int)dirItemInfo.Size);
item.Image = buffer;
item.Name = dirItemInfo.szFileName;
item.Size = (long)dirItemInfo.Size;
item.IsFolder = Convert.ToBoolean(dirItemInfo.isFolder);
return item;
}
catch (AccessViolationException ave)
{
throw new CameraDownloadException("Error copying unmanaged stream to managed byte[].", ave);
}
finally
{
gcHandle.Free();
EDSDK.EdsRelease(stream);
EDSDK.EdsRelease(streamPtr);
}
}
else
{
throw new CameraDownloadException("Unable to get resultant image.", err);
}
}
I spent a lot of time trying to figure out a good way to embed any file into Microsoft Word using OpenXml 2.0; Office documents are fairly easy but what about other file types such as PDF, TXT, GIF, JPG, HTML, etc....
What is a good way to get this to work for any file type, in C#?
Embedding Foreign Objects (PDF, TXT, GIF, etc…) into Microsoft Word using OpenXml 2.0
(Well, in collaboration with COM)
I got a lot from this site, so here I asked and answered my own question in order to give back a little on a topic in which I had difficulty finding answers on, hope it helps people.
There are several examples out there that show how to embed an Office Document into another Office Document using OpenXml 2.0, what’s not out there and easily understandable is how to embed just about any file into and Office Document.
I have learned a lot from other people’s code, so this is my attempt to contribute. Since I am already using OpenXml to generate documents, and I am in need of embedding other files into Word, I have decided use a collaboration of OpenXml and COM (Microsoft Office 2007 dll’s) to achieve my goal. If you are like me, “invoking the OLE server application to create an IStorage” doesn’t mean much to you.
In this example I’d like to show how I use COM to PROGRMATICALLY get the OLE-binary data information of the attached file, and then how I used that information within my OpenXml document. Basically, I am programmatically looking at the OpenXml 2.0 Document Reflector to get the information I need.
My code below is broken down into several classes, but here is an outline of what I am doing:
Create an OpenXml WordProcessingDocument, get the System.IO.FileInfo for the file you want to Embed
Create a custom OpenXmlEmbeddedObject object (this is what holds all the binary data)
Use the binary data from the above step to create Data and Image Streams
Use those Streams as the File Object and File Image for your OpenXml Document
I know there is a lot of code, and not much explanation… Hopefully it is easy to follow and will help people out
Requirements:
• DocumentFormat.OpenXml dll (OpenXml 2.0)
• WindowsBase dll
• Microsoft.Office.Interop.Word dll (Office 2007 – version 12)
• This the main class that starts everything, opens a WordProcessingDocument and class to have the file attached
using DocumentFormat.OpenXml.Packaging;
using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;
public class MyReport
{
private MainDocumentPart _mainDocumentPart;
public void CreateReport()
{
using (WordprocessingDocument wpDocument = WordprocessingDocument.Create(#"TempPath\MyReport.docx", WordprocessingDocumentType.Document))
{
_mainDocumentPart = wpDocument.AddMainDocumentPart();
_mainDocumentPart.Document = new Document(new Body());
AttachFile(#"MyFilePath\MyFile.pdf", true);
}
}
private void AttachFile(string filePathAndName, bool displayAsIcon)
{
FileInfo fileInfo = new FileInfo(filePathAndName);
OpenXmlHelper.AppendEmbeddedObject(_mainDocumentPart, fileInfo, displayAsIcon);
}
}
• This class in an OpenXml helper class, holds all the logic to embed an object into your OpenXml File
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation;
using DocumentFormat.OpenXml.Wordprocessing;
using OVML = DocumentFormat.OpenXml.Vml.Office;
using V = DocumentFormat.OpenXml.Vml;
public class OpenXmlHelper
{
/// <summary>
/// Appends an Embedded Object into the specified Main Document
/// </summary>
/// <param name="mainDocumentPart">The MainDocument Part of your OpenXml Word Doc</param>
/// <param name="fileInfo">The FileInfo object associated with the file being embedded</param>
/// <param name="displayAsIcon">Whether or not to display the embedded file as an Icon (Otherwise it will display a snapshot of the file)</param>
public static void AppendEmbeddedObject(MainDocumentPart mainDocumentPart, FileInfo fileInfo, bool displayAsIcon)
{
OpenXmlEmbeddedObject openXmlEmbeddedObject = new OpenXmlEmbeddedObject(fileInfo, displayAsIcon);
if (!String.IsNullOrEmpty(openXmlEmbeddedObject.OleObjectBinaryData))
{
using (Stream dataStream = new MemoryStream(Convert.FromBase64String(openXmlEmbeddedObject.OleObjectBinaryData)))
{
if (!String.IsNullOrEmpty(openXmlEmbeddedObject.OleImageBinaryData))
{
using (Stream emfStream = new MemoryStream(Convert.FromBase64String(openXmlEmbeddedObject.OleImageBinaryData)))
{
string imagePartId = GetUniqueXmlItemID();
ImagePart imagePart = mainDocumentPart.AddImagePart(ImagePartType.Emf, imagePartId);
if (emfStream != null)
{
imagePart.FeedData(emfStream);
}
string embeddedPackagePartId = GetUniqueXmlItemID();
if (dataStream != null)
{
if (openXmlEmbeddedObject.ObjectIsOfficeDocument)
{
EmbeddedPackagePart embeddedObjectPart = mainDocumentPart.AddNewPart<EmbeddedPackagePart>(
openXmlEmbeddedObject.FileContentType, embeddedPackagePartId);
embeddedObjectPart.FeedData(dataStream);
}
else
{
EmbeddedObjectPart embeddedObjectPart = mainDocumentPart.AddNewPart<EmbeddedObjectPart>(
openXmlEmbeddedObject.FileContentType, embeddedPackagePartId);
embeddedObjectPart.FeedData(dataStream);
}
}
if (!displayAsIcon && !openXmlEmbeddedObject.ObjectIsPicture)
{
Paragraph attachmentHeader = CreateParagraph(String.Format("Attachment: {0} (Double-Click to Open)", fileInfo.Name));
mainDocumentPart.Document.Body.Append(attachmentHeader);
}
Paragraph embeddedObjectParagraph = GetEmbeededObjectParagraph(openXmlEmbeddedObject.FileType,
imagePartId, openXmlEmbeddedObject.OleImageStyle, embeddedPackagePartId);
mainDocumentPart.Document.Body.Append(embeddedObjectParagraph);
}
}
}
}
}
/// <summary>
/// Gets Paragraph that includes the embedded object
/// </summary>
private static Paragraph GetEmbeededObjectParagraph(string fileType, string imageID, string imageStyle, string embeddedPackageID)
{
EmbeddedObject embeddedObject = new EmbeddedObject();
string shapeID = GetUniqueXmlItemID();
V.Shape shape = new V.Shape() { Id = shapeID, Style = imageStyle };
V.ImageData imageData = new V.ImageData() { Title = "", RelationshipId = imageID };
shape.Append(imageData);
OVML.OleObject oleObject = new OVML.OleObject()
{
Type = OVML.OleValues.Embed,
ProgId = fileType,
ShapeId = shapeID,
DrawAspect = OVML.OleDrawAspectValues.Icon,
ObjectId = GetUniqueXmlItemID(),
Id = embeddedPackageID
};
embeddedObject.Append(shape);
embeddedObject.Append(oleObject);
Paragraph paragraphImage = new Paragraph();
Run runImage = new Run(embeddedObject);
paragraphImage.Append(runImage);
return paragraphImage;
}
/// <summary>
/// Gets a Unique ID for an XML Item, for reference purposes
/// </summary>
/// <returns>A GUID string with removed dashes</returns>
public static string GetUniqueXmlItemID()
{
return "r" + System.Guid.NewGuid().ToString().Replace("-", "");
}
private static Paragraph CreateParagraph(string paragraphText)
{
Paragraph paragraph = new Paragraph();
ParagraphProperties paragraphProperties = new ParagraphProperties();
paragraphProperties.Append(new Justification()
{
Val = JustificationValues.Left
});
paragraphProperties.Append(new SpacingBetweenLines()
{
After = Convert.ToString(100),
Line = Convert.ToString(100),
LineRule = LineSpacingRuleValues.AtLeast
});
Run run = new Run();
RunProperties runProperties = new RunProperties();
Text text = new Text();
if (!String.IsNullOrEmpty(paragraphText))
{
text.Text = paragraphText;
}
run.Append(runProperties);
run.Append(text);
paragraph.Append(paragraphProperties);
paragraph.Append(run);
return paragraph;
}
}
• This is the most important part of this process, it is using Microsoft's internal OLE Server, creates the Binary DATA and Binary EMF information for a file. All you have to here is call the OpenXmlEmbeddedObject constructor and all get’s taken care of. It will mimic the process that goes on when you manually drag any file into Word; there is some kind of conversion that goes on when you do that, turning the file into an OLE object, so that Microsoft can recognize the file.
o The most imporant parts of this class are the OleObjectBinaryData and OleImageBinaryData properties; they contain the 64Bit string binary info for the file data and ‘.emf’ image.
o If you choose to not display the file as an icon, then the ‘.emf’ image data will create a snapshot of the file, like the first page of the pdf file for example, in which you can still double-click to open
o If you are embedding an image and choose not to display it as an Icon, then the OleObjectBinaryData and OleImageBinaryData properties will be the same
using System.Runtime.InteropServices;
using System.Xml;
using System.Diagnostics;
using System.IO;
using System.Drawing;
using Microsoft.Office.Interop.Word;
public class OpenXmlEmbeddedObject
{
#region Constants
private const string _defaultOleContentType = "application/vnd.openxmlformats-officedocument.oleObject";
private const string _oleObjectDataTag = "application/vnd";
private const string _oleImageDataTag = "image/x-emf";
#endregion Constants
#region Member Variables
private static FileInfo _fileInfo;
private static string _filePathAndName;
private static bool _displayAsIcon;
private static bool _objectIsPicture;
private object _objectMissing = System.Reflection.Missing.Value;
private object _objectFalse = false;
private object _objectTrue = true;
#endregion Member Variables
#region Properties
/// <summary>
/// The File Type, as stored in Registry (Ex: a GIF Image = 'giffile')
/// </summary>
public string FileType
{
get
{
if (String.IsNullOrEmpty(_fileType) && _fileInfo != null)
{
_fileType = GetFileType(_fileInfo, false);
}
return _fileType;
}
}
private string _fileType;
/// <summary>
/// The File Context Type, as storered in Registry (Ex: a GIF Image = 'image/gif')
/// * Is converted into the 'Default Office Context Type' for non-office files
/// </summary>
public string FileContentType
{
get
{
if (String.IsNullOrEmpty(_fileContentType) && _fileInfo != null)
{
_fileContentType = GetFileContentType(_fileInfo);
if (!_fileContentType.Contains("officedocument"))
{
_fileContentType = _defaultOleContentType;
}
}
return _fileContentType;
}
}
private string _fileContentType;
/// <summary>
/// Gets the ContentType Text for the file
/// </summary>
public static string GetFileContentType(FileInfo fileInfo)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
string mime = "application/octetstream";
string ext = System.IO.Path.GetExtension(fileInfo.Name).ToLower();
Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
if (rk != null && rk.GetValue("Content Type") != null)
{
mime = rk.GetValue("Content Type").ToString();
}
return mime;
}
public bool ObjectIsOfficeDocument
{
get { return FileContentType != _defaultOleContentType; }
}
public bool ObjectIsPicture
{
get { return _objectIsPicture; }
}
public string OleObjectBinaryData
{
get { return _oleObjectBinaryData; }
set { _oleObjectBinaryData = value; }
}
private string _oleObjectBinaryData;
public string OleImageBinaryData
{
get { return _oleImageBinaryData; }
set { _oleImageBinaryData = value; }
}
private string _oleImageBinaryData;
/// <summary>
/// The OpenXml information for the Word Application that is created (Make-Shoft Code Reflector)
/// </summary>
public string WordOpenXml
{
get { return _wordOpenXml; }
set { _wordOpenXml = value; }
}
private String _wordOpenXml;
/// <summary>
/// The XmlDocument that is created based on the OpenXml Data from WordOpenXml
/// </summary>
public XmlDocument OpenXmlDocument
{
get
{
if (_openXmlDocument == null && !String.IsNullOrEmpty(WordOpenXml))
{
_openXmlDocument = new XmlDocument();
_openXmlDocument.LoadXml(WordOpenXml);
}
return _openXmlDocument;
}
}
private XmlDocument _openXmlDocument;
/// <summary>
/// The XmlNodeList, for all Nodes containing 'binaryData'
/// </summary>
public XmlNodeList BinaryDataXmlNodesList
{
get
{
if (_binaryDataXmlNodesList == null && OpenXmlDocument != null)
{
_binaryDataXmlNodesList = OpenXmlDocument.GetElementsByTagName("pkg:binaryData");
}
return _binaryDataXmlNodesList;
}
}
private XmlNodeList _binaryDataXmlNodesList;
/// <summary>
/// Icon Object for the file
/// </summary>
public Icon ObjectIcon
{
get
{
if (_objectIcon == null)
{
_objectIcon = Enterprise.Windows.Win32.Win32.GetLargeIcon(_filePathAndName);
}
return _objectIcon;
}
}
private Icon _objectIcon;
/// <summary>
/// File Name for the Icon being created
/// </summary>
public string ObjectIconFile
{
get
{
if (String.IsNullOrEmpty(_objectIconFile))
{
_objectIconFile = String.Format("{0}.ico", _filePathAndName.Replace(".", ""));
}
return _objectIconFile;
}
}
private string _objectIconFile;
/// <summary>
/// Gets the original height and width of the emf file being created
/// </summary>
public string OleImageStyle
{
get
{
if (String.IsNullOrEmpty(_oleImageStyle) && !String.IsNullOrEmpty(WordOpenXml))
{
XmlNodeList xmlNodeList = OpenXmlDocument.GetElementsByTagName("v:shape");
if (xmlNodeList != null && xmlNodeList.Count > 0)
{
foreach (XmlAttribute attribute in xmlNodeList[0].Attributes)
{
if (attribute.Name == "style")
{
_oleImageStyle = attribute.Value;
}
}
}
}
return _oleImageStyle;
}
set { _oleImageStyle = value; }
}
private string _oleImageStyle;
#endregion Properties
#region Constructor
/// <summary>
/// Generates binary information for the file being passed in
/// </summary>
/// <param name="fileInfo">The FileInfo object for the file to be embedded</param>
/// <param name="displayAsIcon">Whether or not to display the file as an Icon (Otherwise it will show a snapshot view of the file)</param>
public OpenXmlEmbeddedObject(FileInfo fileInfo, bool displayAsIcon)
{
_fileInfo = fileInfo;
_filePathAndName = fileInfo.ToString();
_displayAsIcon = displayAsIcon;
SetupOleFileInformation();
}
#endregion Constructor
#region Methods
/// <summary>
/// Creates a temporary Word App in order to add an OLE Object, get's the OpenXML data from the file (similar to the Code Reflector info)
/// </summary>
private void SetupOleFileInformation()
{
Microsoft.Office.Interop.Word.Application wordApplication = new Microsoft.Office.Interop.Word.Application();
Microsoft.Office.Interop.Word.Document wordDocument = wordApplication.Documents.Add(ref _objectMissing, ref _objectMissing,
ref _objectMissing, ref _objectMissing);
object iconObjectFileName = _objectMissing;
object objectClassType = FileType;
object objectFilename = _fileInfo.ToString();
Microsoft.Office.Interop.Word.InlineShape inlineShape = null;
if (_displayAsIcon)
{
if (ObjectIcon != null)
{
using (FileStream iconStream = new FileStream(ObjectIconFile, FileMode.Create))
{
ObjectIcon.Save(iconStream);
iconObjectFileName = ObjectIconFile;
}
}
object objectIconLabel = _fileInfo.Name;
inlineShape = wordDocument.InlineShapes.AddOLEObject(ref objectClassType,
ref objectFilename, ref _objectFalse, ref _objectTrue, ref iconObjectFileName,
ref _objectMissing, ref objectIconLabel, ref _objectMissing);
}
else
{
try
{
Image image = Image.FromFile(_fileInfo.ToString());
_objectIsPicture = true;
OleImageStyle = String.Format("height:{0}pt;width:{1}pt", image.Height, image.Width);
wordDocument.InlineShapes.AddPicture(_fileInfo.ToString(), ref _objectMissing, ref _objectTrue, ref _objectMissing);
}
catch
{
inlineShape = wordDocument.InlineShapes.AddOLEObject(ref objectClassType,
ref objectFilename, ref _objectFalse, ref _objectFalse, ref _objectMissing, ref _objectMissing,
ref _objectMissing, ref _objectMissing);
}
}
WordOpenXml = wordDocument.Range(ref _objectMissing, ref _objectMissing).WordOpenXML;
if (_objectIsPicture)
{
OleObjectBinaryData = GetPictureBinaryData();
OleImageBinaryData = GetPictureBinaryData();
}
else
{
OleObjectBinaryData = GetOleBinaryData(_oleObjectDataTag);
OleImageBinaryData = GetOleBinaryData(_oleImageDataTag);
}
// Not sure why, but Excel seems to hang in the processes if you attach an Excel file…
// This kills the excel process that has been started < 15 seconds ago (so not to kill the user's other Excel processes that may be open)
if (FileType.StartsWith("Excel"))
{
Process[] processes = Process.GetProcessesByName("EXCEL");
foreach (Process process in processes)
{
if (DateTime.Now.Subtract(process.StartTime).Seconds <= 15)
{
process.Kill();
break;
}
}
}
wordDocument.Close(ref _objectFalse, ref _objectMissing, ref _objectMissing);
wordApplication.Quit(ref _objectMissing, ref _objectMissing, ref _objectMissing);
}
/// <summary>
/// Gets the binary data from the Xml File that is associated with the Tag passed in
/// </summary>
/// <param name="binaryDataXmlTag">the Tag to look for in the OpenXml</param>
/// <returns></returns>
private string GetOleBinaryData(string binaryDataXmlTag)
{
string binaryData = null;
if (BinaryDataXmlNodesList != null)
{
foreach (XmlNode xmlNode in BinaryDataXmlNodesList)
{
if (xmlNode.ParentNode != null)
{
foreach (XmlAttribute attr in xmlNode.ParentNode.Attributes)
{
if (String.IsNullOrEmpty(binaryData) && attr.Value.Contains(binaryDataXmlTag))
{
binaryData = xmlNode.InnerText;
break;
}
}
}
}
}
return binaryData;
}
/// <summary>
/// Gets the image Binary data, if the file is an image
/// </summary>
/// <returns></returns>
private string GetPictureBinaryData()
{
string binaryData = null;
if (BinaryDataXmlNodesList != null)
{
foreach (XmlNode xmlNode in BinaryDataXmlNodesList)
{
binaryData = xmlNode.InnerText;
break;
}
}
return binaryData;
}
/// <summary>
/// Gets the file type description ("Application", "Text Document", etc.) for the file.
/// </summary>
/// <param name="fileInfo">FileInfo containing extention</param>
/// <returns>Type Description</returns>
public static string GetFileType(FileInfo fileInfo, bool returnDescription)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
string description = "File";
if (string.IsNullOrEmpty(fileInfo.Extension))
{
return description;
}
description = string.Format("{0} File", fileInfo.Extension.Substring(1).ToUpper());
RegistryKey typeKey = Registry.ClassesRoot.OpenSubKey(fileInfo.Extension);
if (typeKey == null)
{
return description;
}
string type = Convert.ToString(typeKey.GetValue(string.Empty));
RegistryKey key = Registry.ClassesRoot.OpenSubKey(type);
if (key == null)
{
return description;
}
if (returnDescription)
{
description = Convert.ToString(key.GetValue(string.Empty));
return description;
}
else
{
return type;
}
}
#endregion Methods
}
_objectIcon = Enterprise.Windows.Win32.Win32.GetLargeIcon(_filePathAndName);
seems to be broken, but
_objectIcon = System.Drawing.Icon.ExtractAssociatedIcon(_filePathAndName);
should also work.
My answer here will tell you how to do this, but not show you with the SDK or a specific language.
This is a great answer and it helped me a lot, but the potential bug that user bic mentioned also exists
in
OpenXmlEmbeddedObject(FileInfo fileInfo, bool displayAsIcon)
at line 242,
_filePathAndName = fileInfo.ToString();
in
SetupOleFileInformation()
at line 264,
object objectFilename = _fileInfo.ToString();
line 289, and
Image image = Image.FromFile(_fileInfo.ToString());
line 293
wordDocument.InlineShapes.AddPicture(_fileInfo.toString(), ref _objectMissing, ref _objectTrue, ref _objectMissing);
All of these need to be "FullName" instead of "ToString()" if the code should work with relative paths as well. Hope this helps anyone who wants to use D Lyonnais's code!
Make a copy of the document how you wanted to get through code and then use Open XML SDK Tool 2.5 for Microsoft Office for writing code. This tool reflects code. which you can simply copy paste.