My task is to reorder the columns in an Excel spreadsheet. The files are generally quite large (75-100K rows, up to 200 columns).
My first inclination was, as usual, EPPlus. It took a bit of doing, but I got it working. Only it was slow. Really slow. It will stream properly and not overload memory, but too slow.
After reading this post that mentioned the benefits of using Excel automation, I thought I'd give that a try. I was amazed. Even though I'm not actually saving the resulting file until after I've copy/pasted all columns of data in, I never see the memory spike. There's some really good memory management in there. Yes, having a dependency on a COM object isn't optimal, but when we're talking Excel, I think it's acceptable. It freakin rocks, and the code is very simple.
Anyway, I'm including both here (if you have a need for this and your files aren't large, you might want to use EPPlus).
In either case the call is like:
EPPlus version:
Helpers:
Main Class:
Excel Automation version:
Thursday, December 04, 2014
Wednesday, January 08, 2014
MP3 Player Hack (one way Windows 8 on a tablet is so useful)
I have a few requirements for an MP3 player on my tablet:
After quite a search, I couldn't find anything in the windows store that worked (one app came close, had those features, but simply wigged out whenever resuming play after turning the tablet back on). I probably could find something in the google or apple stores that worked, but for myriad other reasons, I love my windows tablet.
Legacy windows to the rescue. I can run legacy windows applications, so surely the ever-loved, classic, most fully featured MP3 player in history; WinAmp will fill my needs, right? Well, it came really close. Of course, it installed and worked fine on the legacy side of Windows 8, which right there is awesome. The other vendor's tablets just aren't going to run a 15 year old (or however old WinAmp is) application.
Close. Prefs allowed me to zoom the interface (using the "modern" skin) so the buttons became large. Pause is pretty easy to find and push now. Amazingly, WinAmp doesn't remember where you left off (unless it's a podcast) so I needed a plug-in for that. Now I'm close. I need it to keep the screen on (not sure there's a plug-in for that), and I still can't rewind. The closest way is to invoke the on-screen keyboard and hit the back arrow.
Simple. I want a controller for WinAmp. I want something that starts WinAmp (allowing it to simply resume where it left off), issues 2 keystroke commands to it (pause, rewind), and prevents the tablet from going to sleep. So I created that in very little time. I threw together an old-school WinForms (damn that technology was useful in many situations, wasn't it?) app that starts WinAmp when it starts, shows itself full-screen with ~ 75% of it's surface for pause, the rest for rewind, and sends a mouse move command every 2 minutes to prevent screen shutdown. Ye olde Win32 API is ugly, but having the ability to still use it is pretty cool.
If someone out there actually wants the executable, contact me and I'll send it.
The Win32 Code.
The WinFormCode. Set the form state to maximized. There is a timer hard coded to fire every 2 minutes (to issue the mouse move command to prevent tablet sleep), and the path to WinAmp is hard coded to where it's installed by default.
- I need playlists. Pretty basic.
- I need a large area that, when tapped, will pause play.
- Another fairly large area that will rewind 15-30 seconds.
- I need the app to remember where I left off and resume there if I close/reopen.
- I need the dang thing not to shut off or for the screen to go black disallowing input.
After quite a search, I couldn't find anything in the windows store that worked (one app came close, had those features, but simply wigged out whenever resuming play after turning the tablet back on). I probably could find something in the google or apple stores that worked, but for myriad other reasons, I love my windows tablet.
Legacy windows to the rescue. I can run legacy windows applications, so surely the ever-loved, classic, most fully featured MP3 player in history; WinAmp will fill my needs, right? Well, it came really close. Of course, it installed and worked fine on the legacy side of Windows 8, which right there is awesome. The other vendor's tablets just aren't going to run a 15 year old (or however old WinAmp is) application.
Close. Prefs allowed me to zoom the interface (using the "modern" skin) so the buttons became large. Pause is pretty easy to find and push now. Amazingly, WinAmp doesn't remember where you left off (unless it's a podcast) so I needed a plug-in for that. Now I'm close. I need it to keep the screen on (not sure there's a plug-in for that), and I still can't rewind. The closest way is to invoke the on-screen keyboard and hit the back arrow.
Simple. I want a controller for WinAmp. I want something that starts WinAmp (allowing it to simply resume where it left off), issues 2 keystroke commands to it (pause, rewind), and prevents the tablet from going to sleep. So I created that in very little time. I threw together an old-school WinForms (damn that technology was useful in many situations, wasn't it?) app that starts WinAmp when it starts, shows itself full-screen with ~ 75% of it's surface for pause, the rest for rewind, and sends a mouse move command every 2 minutes to prevent screen shutdown. Ye olde Win32 API is ugly, but having the ability to still use it is pretty cool.
If someone out there actually wants the executable, contact me and I'll send it.
The Win32 Code.
- class Win32Trash
- {
- [DllImport( "User32" )]
- public static extern int SetForegroundWindow( IntPtr hwnd );
- /// <summary>
- /// Synthesizes keystrokes, mouse motions, and button clicks.
- /// </summary>
- [DllImport( "User32.dll" )]
- public static extern uint SendInput( uint numberOfInputs, [MarshalAs( UnmanagedType.LPArray, SizeConst = 1 )] INPUT[] input, int structSize );
- public enum InputType
- {
- INPUT_MOUSE = 0,
- INPUT_KEYBOARD = 1,
- INPUT_HARDWARE = 2
- };
- public enum MouseEventFlags
- {
- MOVE = 0x00000001,
- LEFTDOWN = 0x00000002,
- LEFTUP = 0x00000004,
- RIGHTDOWN = 0x00000008,
- MIDDLEDOWN = 0x00000020,
- MIDDLEUP = 0x00000040,
- ABSOLUTE = 0x00008000,
- RIGHTUP = 0x00000010
- };
- public struct MOUSEINPUT
- {
- public int dx;
- public int dy;
- public int mouseData;
- public MouseEventFlags dwFlags;
- public int time;
- public IntPtr dwExtraInfo;
- };
- public struct KEYBDINPUT
- {
- public short wVk;
- public short wScan;
- public int dwFlags;
- public int time;
- public IntPtr dwExtraInfo;
- };
- public struct HARDWAREINPUT
- {
- public int uMsg;
- public short wParamL;
- public short wParamH;
- };
- [StructLayout(LayoutKind.Explicit)]
- public struct MOUSEKEYBDHARDWAREINPUT
- {
- [FieldOffset(0)]
- public MOUSEINPUT mi;
- [FieldOffset( 0 )]
- public KEYBDINPUT ki;
- [FieldOffset( 0 )]
- public HARDWAREINPUT hi;
- };
- public struct INPUT
- {
- public InputType dwType;
- public MOUSEKEYBDHARDWAREINPUT mkhi;
- };
- }
The WinFormCode. Set the form state to maximized. There is a timer hard coded to fire every 2 minutes (to issue the mouse move command to prevent tablet sleep), and the path to WinAmp is hard coded to where it's installed by default.
- public partial class WinAmpController : Form
- {
- private static Win32Trash.INPUT[] _junkForMouseInput = CreateMouseInput();
- private static Process _winampProcess;
- public WinAmpController()
- {
- InitializeComponent();
- }
- public void launchWinAmp()
- {
- try
- {
- // We don't want to attach to an existing process if one exists
- var si = new ProcessStartInfo( @"C:\Program Files\Winamp\winamp.exe" ) { UseShellExecute = false, WindowStyle = ProcessWindowStyle.Normal };
- _winampProcess = Process.Start( si );
- _winampProcess.WaitForInputIdle();
- }
- catch( Exception e )
- {
- MessageBox.Show( "Error launching Winamp: " + e.Message );
- }
- }
- private void WinAmpController_Load( object sender, EventArgs e )
- {
- launchWinAmp();
- /// Expected that the plug-in will begin playing where left off.
- this.TopMost = true;
- }
- private void WinAmpController_FormClosing( object sender, FormClosingEventArgs e )
- {
- if ( _winampProcess != null )
- {
- _winampProcess.CloseMainWindow();
- _winampProcess.Close();
- }
- }
- private static void SendKey( string key )
- {
- IntPtr windowHandle = _winampProcess.MainWindowHandle;
- //ShowWindow( windowHandle, 1 ); // ain't shit
- Win32Trash.SetForegroundWindow( windowHandle );
- SendKeys.SendWait( key );
- }
- private void btnPause_Click( object sender, EventArgs e )
- {
- SendKey("c");
- }
- /// <summary>
- /// Each left command is only 5 secs back. This results in about 19 secs back. Putting multiple calls to SendWait inside the SendKey method didn't work.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnBack_Click( object sender, EventArgs e )
- {
- SendKey( "{LEFT}" );
- SendKey( "{LEFT}" );
- SendKey( "{LEFT}" );
- }
- /// <summary>
- /// Set for every 2 min.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void timer1_Tick( object sender, EventArgs e )
- {
- this.Text = "timer clicked";
- MoveMouse();
- }
- private static Win32Trash.INPUT[] CreateMouseInput()
- {
- var i = new Win32Trash.INPUT()
- {
- dwType = Win32Trash.InputType.INPUT_MOUSE,
- mkhi = new Win32Trash.MOUSEKEYBDHARDWAREINPUT()
- {
- mi = new Win32Trash.MOUSEINPUT()
- {
- dx = 0,
- dy = 0,
- mouseData = 0,
- dwFlags = Win32Trash.MouseEventFlags.MOVE,
- time = 0,
- dwExtraInfo = IntPtr.Zero
- }
- }
- };
- return new Win32Trash.INPUT[] { i };
- }
- private void MoveMouse()
- {
- Win32Trash.SendInput( 1, _junkForMouseInput, Marshal.SizeOf( _junkForMouseInput[0] ) );
- }
- }
Subscribe to:
Posts (Atom)