9

I have a two-fold problem and I'm familiar with neither branch of it.

Background: I've purchased a new laptop from MSI. As a feature I've never seen before, the CD-drive does not have a mechanical eject button mounted on the drive. Instead, a digital eject button is part of the "smart-bar" above the Function Keys. Unfortunately, the software underlying the smart-bar is atrocious. Even in passive mode it takes up screen real-estate and it takes up space on the task bar.* I have un-installed this software.

Project: I want to write a program that runs on startup and lies unobtrusively in the background. The only function of the program is to make that eject button eject the CD drive.

Step one: I want to find out what happens when I push that button. I suspect that I don't need to code anything but can use a utility - one of my colleagues mentioned an "event logger" but google provides nothing of use.

Step two: I want to write said program, which means I need to interact with either WIN32 or possibly the .net framework to perform the same function call windows uses when I right-click the CD-Drive under "My computer" and click "Eject."

Questions: First question: Do you know a utility that will tell me what happens when the button is pushed?

Second question: Do you know what function I need to call on that event to eject the drive?

Tertiary question: Am I overlooking some obvious other path?

I am open to any answers from "I had the same problem, here's the source code to the solution I wrote" to "I guess maybe this tutorial will help you?"

I would prefer to work in C/C++/C# but I am open to other suggestions. As the title states, I am working under Windows 7.

*For the curious, the software is the MSI S-Bar which is for some reason touted as a "feature" of this line of laptops.

shieldfoss
  • 886
  • 2
  • 12
  • 22

6 Answers6

1

Hotkeys

If they are part of the keyboard (just because they look like they are, doesn't mean they really are) they will have a scancode. SetWindowsHookEx can be used to find out the scancode, as well as react to it - the following code should help:

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        myClass.Install();
        Application.Run(new Form()); // You need a form, not sure why.
        myClass.Uninstall();
    }
}

public class MyClass : CriticalFinalizerObject
{
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(HookType idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId); 
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk); 
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
    [DllImport("winmm.dll")]
    static extern Int32 mciSendString(String command, StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback);

    private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    private enum HookType : int
    {
        WH_KEYBOARD = 2,
        WH_KEYBOARD_LL = 13
    }
    private enum WindowsMessage : int
    {
        WM_KEYUP = 0x101
    }

    private HookProc _myCallbackDelegate;
    private IntPtr _hook;

    public MyClass()
    {
        _myCallbackDelegate = MyCallbackFunction;
    }

    public void Install()
    {
        Uninstall();

        using (Process process = Process.GetCurrentProcess())
        using (ProcessModule module = process.MainModule)
        {
            _hook = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, _myCallbackDelegate, GetModuleHandle(module.ModuleName), 0);
        }
    }

    public void Uninstall()
    {
        var ptr = Interlocked.Exchange(ref _hook, IntPtr.Zero);
        if (ptr != IntPtr.Zero)
            UnhookWindowsHookEx(ptr);
    }

    private IntPtr MyCallbackFunction(int code, IntPtr wParam, IntPtr lParam)
    {
        if (code >= 0 && wParam == (IntPtr)WindowsMessage.WM_KEYUP)
        {
            var sk = Marshal.ReadInt32(lParam);
            // This can be used to find the scancode.
            // Press the key and watch the console to find out the scancode.
            Console.WriteLine("ScanCode: 0x{0:x4}", sk);

            if (sk == 0x0041) // 0x0041 is A
            {
                // We can't hold up the hook for too long; start the
                // tray open on another thread.
                new Action(OpenTray).BeginInvoke(null, null);
            }
        }
        return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }

    private void OpenTray()
    {
        mciSendString("set CDAudio door open", null, 0, IntPtr.Zero);
    }

    ~MyClass()
    {
        Uninstall();
    }
}

HID

A human interface device will be much harder to interact with. Hopefully The Code Project can help.

Jonathan Dickinson
  • 9,050
  • 1
  • 37
  • 60
1

Ejecting the CD drive. Does not require admin privilege.

#include <windows.h>
#include <vfw.h>
#include <stdio.h>

#pragma comment(lib, "Vfw32.lib")

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 
{
  char msg[512];
  HANDLE h;
  DWORD bytesreturned;

  h = CreateFile("\\\\.\\cdrom0", MAXIMUM_ALLOWED, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

  if (h == INVALID_HANDLE_VALUE) 
  {
    sprintf(msg, "CreateFile: %u\n", GetLastError());
    MessageBox(NULL, msg, "ejectcd", MB_OK);
    return 1;
  }

  if (!DeviceIoControl(h, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &bytesreturned, NULL)) 
  {
    sprintf(msg, "DeviceIoControl: %u\n", GetLastError());
    MessageBox(NULL, msg, "ejectcd", MB_OK);
    return 1;
  }

  return 0;
}
Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
1

I encountered this same problem myself. I don't know whether or not you are familiar with autohotkeys, but used it to write a script to replace s-bar.

AutoHotKey (AHK) is a open source macro-creating and scripting language for Windows with a ton of functionality. You can run the scripts through the program itself or compile them into executables that can be run on any computer.

You can make the eject CD drive work with just a couple lines of code:

;CD eject button
SC142::
Drive, Eject
return

SC142 is the keycode. It might not be the same on every MSI laptop, but if you put the line "#InstallKeybdHook" at the top of your script. You can view all of the keyboard events AHK sees through a GUI.

I was able to map most of the buttons to various functions on my MSI laptop, although AHK wouldn't capture keyboard events from one of them.

For example:

;Star key (AHK only receives KeyUp event)
SC139 UP::
Run, Control
return

;CinemaPro Key
SC13B::
Run, "C:\Program Files\Media Player Classic\mpc-hc64.exe"
return
kappamaki
  • 648
  • 8
  • 14
0

check here:

c:

CDR

c#:

OpenCD

CB.
  • 58,865
  • 9
  • 159
  • 159
0

You have to use mciSendString API . look here SO link for this, if you gonna do it programmatically.

Community
  • 1
  • 1
Tigran
  • 61,654
  • 8
  • 86
  • 123
0

You could write a simple program that simply polls the keyboard while you press the eject key. Use GetKeyboardState() and check to see what's been pressed. Or you could handle the the WM_KEYDOWN message and inspect the key codes as you hit the eject key.

Have you tried looking in the control panel ? Go to Control Panel>Monitor>Key Settings - from there you will be able to re-assign the keys on your keyboard.

Jeff Paquette
  • 7,089
  • 2
  • 31
  • 40