Page 1 of 1

API/SDK for UDF/packet-writing software

PostPosted: Thu Jan 01, 1970 1:12 am
by sash
Hi all,

I am looking for an UDF/packet-writing (something like DirectCD) CD-R recording software that has some kind of API or SDK or COM or any other way to control it from a C or C++ program, at least for what concerns mounting, checking available space, finalizing and unmounting a CD.

I know Nero announced an API for the 5.5 release, but the docs are not on their site yet.
Does anybody know of any other one ?

Thanks a lot,
Sergio

PostPosted: Thu Jan 01, 1970 4:34 am
by Kjell-Ake
Hello from Sweden.

What you really need is a way to finalize and eject the CD-RW-disc, else it won't be readable from a common CD-ROM.
The rest of what you want is pretty straight-forward, as long as you have got the drive letter for it.

You cannot do a DeviceIoControl(hDrive, IOCTL_DISK_EJECT_MEDIA, ...) because it just ejects the disc without finalizing it first.
I'm using DirectCD and I've solved the problem this way:
If you use the right-click menu for the drive letter in question (in "My Computer"), en select the "Eject"-command, then the disc IS finalized before it's ejected.

So this is my approach to the probelm.

I've written a C++-class (CShellEject) that does just this. It finds "My Computer", finds the drive, browses through the popup menu for it, finds the "Eject" command and executes it.
(MS VC++ 4.2 - 6.0)


Here is the header file (ShellEject.h):
// ShellEject.h : header file
//

#ifndef SHELLEJECTH
#define SHELLEJECTH

#include <shlobj.h>

/////////////////////////////////////////////////////////////////////////////
// CShellEject window

class CShellEject : public CStatic
{
// Construction
public:
BOOL ShellEject(char drive);
CShellEject();

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CShellEject)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CShellEject();

// Generated message map functions
protected:
//{{AFX_MSG(CShellEject)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
private:
BOOL ExecuteTheEjectCMD(HWND hwnd, LPSHELLFOLDER lpsfParent, LPITEMIDLIST lpi);
LPMALLOC g_pMalloc;
};

/////////////////////////////////////////////////////////////////////////////


#endif // SHELLEJECTH

Here is the source code (ShellEject.cpp):
// ShellEject.cpp : implementation file
//

#include "stdafx.h"
#include "RSVKNVNT.h" // My main project, shouldn't be req:ed
#include "ShellEject.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CShellEject

CShellEject::CShellEject()
{
}

CShellEject::~CShellEject()
{
}


BEGIN_MESSAGE_MAP(CShellEject, CStatic)
//{{AFX_MSG_MAP(CShellEject)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CShellEject message handlers


BOOL CShellEject::ShellEject(char drive)
{

BOOL bCDBurnerFound = FALSE;
char driveStringToEject[20];
BOOL bRetVal = TRUE;

// toupper the drive in the form "G:\"
sprintf(driveStringToEject, "%c:\\", (char) toupper((int) drive));


LPMALLOC pMalloc;
if (SUCCEEDED(SHGetMalloc(&pMalloc))) {
// Hämta desktop
LPSHELLFOLDER psfFolder, psfNewFolder;
if (!SUCCEEDED(SHGetDesktopFolder(&psfFolder))) {
pMalloc->Release();
return FALSE;
}

LPITEMIDLIST pidlMyComputer;

// Find "My Computer"
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES,
&pidlMyComputer))) {
// OK, bind.
// Bind to the folder specified in the new item ID list.
if (!SUCCEEDED(psfFolder->BindToObject(pidlMyComputer,
NULL, IID_IShellFolder, (LPVOID *)&psfNewFolder))) {
pMalloc->Free(pidlMyComputer);
psfFolder->Release();
pMalloc->Release();
AfxMessageBox("no bind");
return FALSE;
}

// Release the IShellFolder pointer to the parent folder
// and set psfFolder equal to the IShellFolder pointer for
// the My Computer folder.

psfFolder->Release();
psfFolder = psfNewFolder;
}
else {
AfxMessageBox("ShellEject: Couldn't get My Computer.", MB_ICONSTOP);
psfFolder->Release();
return FALSE;
}

LPENUMIDLIST lpEnumIds;
LPITEMIDLIST lpCDRompidl = NULL;; // !!!!!!!!!!!!!!!!!!!!!!!!!!
HRESULT result = psfFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
&lpEnumIds);
if (SUCCEEDED(result)) {
ULONG celtFetched;
HRESULT result = lpEnumIds->Next(1, &pidlMyComputer, &celtFetched);

while ((result != S_FALSE && celtFetched == 1)) {
STRRET strDispName;

if (SUCCEEDED(psfFolder->GetDisplayNameOf(pidlMyComputer,
SHGDN_FORPARSING, &strDispName))) {
// Get the text and compare it to the drive we wants.
char *filename;
switch (strDispName.uType) {
case STRRET_WSTR:
{
int cch = WideCharToMultiByte(CP_OEMCP, NULL, strDispName.pOleStr,
-1, NULL, 0, NULL, NULL);

// Allocate the memeory for copying string.
filename = (char *)pMalloc->Alloc(cch);
WideCharToMultiByte(CP_OEMCP, NULL, strDispName.pOleStr,
-1, filename, cch, NULL, NULL);

if(!strcmp(filename, driveStringToEject)) {
bCDBurnerFound = TRUE;
lpCDRompidl = pidlMyComputer;
}
break;
}
case STRRET_OFFSET:
{
filename = ((char *)pidlMyComputer) +
strDispName.uOffset;
if(!strcmp(filename, driveStringToEject)) {
bCDBurnerFound = TRUE;
lpCDRompidl = pidlMyComputer;
}
break;
}
case STRRET_CSTR:
{
if(!strcmp(filename, driveStringToEject)) {
bCDBurnerFound = TRUE;
lpCDRompidl = pidlMyComputer;
}
break;
}
}
}
else {
AfxMessageBox("ShellEject: Couldn't get display name.", MB_ICONSTOP);
bRetVal = FALSE;
}
result = lpEnumIds->Next(1, &pidlMyComputer, &celtFetched);
}
}
else {
AfxMessageBox("ShellEject: Couldn't enum the objects in My Computer.", MB_ICONSTOP);
bRetVal = FALSE;
}


// Next turn, issue an eject on the unit (if we found it)
if(bCDBurnerFound == TRUE) {
// We found it, eject!
if(ExecuteTheEjectCMD(GetSafeHwnd(), psfFolder, lpCDRompidl) == FALSE) {
// Gick inte.
AfxMessageBox("ShellEject: Couldn't execute the Eject-command.", MB_ICONSTOP);
bRetVal = FALSE;
}
}
else {
AfxMessageBox("ShellEject: Couldn't find the unit.", MB_ICONSTOP);
bRetVal = FALSE;
}

// clean up -- must use task allocator's IMalloc
// interface to free the item ID list
psfFolder->Release();
pMalloc->Free(pidlMyComputer);
pMalloc->Release();
}
return bRetVal;
}


BOOL CShellEject::ExecuteTheEjectCMD(HWND hwnd, LPSHELLFOLDER lpsfParent, LPITEMIDLIST lpi)
{
LPCONTEXTMENU lpcm;
HRESULT hr;
char szTemp[64];
CMINVOKECOMMANDINFO cmi;
DWORD dwAttribs=0;
HMENU hMenu;
BOOL bSuccess=TRUE;

hr=lpsfParent->GetUIObjectOf(hwnd,
1, // get attributes for this many objects
(const struct _ITEMIDLIST **)&lpi,
IID_IContextMenu,
0,
(LPVOID *)&lpcm);

if (SUCCEEDED(hr)) {
hMenu = CreatePopupMenu();

if (hMenu) {
// Get the context menu for the item.
hr=lpcm->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
if (SUCCEEDED(hr)) {
// Find the menu item with the text "&Mata ut" ("&Eject")
// May contain the strings "mata" (Swedish) or "eject"
// When it's found, execute it.

int i;
BOOL bFoundIt = FALSE;
MENUITEMINFO mi;
char text2[200];
int total;
int menuIndex;

total = GetMenuItemCount(hMenu); // Total number of menu alternatives

for(i = 0; i < total && bFoundIt == FALSE; i++) {
// Browse through to find "&Mata ut" or "&Eject"
mi.cbSize = sizeof(MENUITEMINFO);
mi.fMask = MIIM_TYPE;
mi.fType = MFT_STRING;
mi.fState = MFS_ENABLED;
mi.dwTypeData = text2;
mi.cch = 200;

menuIndex = GetMenuItemID(hMenu, i);
if(GetMenuItemInfo(hMenu, menuIndex, FALSE, &mi) == TRUE) {
// We found something, is it the right one?
if(strstr(strlwr(text2), "mata") != NULL ||
strstr(text2, "eject") != NULL) {
// Yesssss !!!!
bFoundIt = TRUE;

// Execute the command that was selected.
cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
cmi.fMask = 0;
cmi.hwnd = hwnd;
cmi.lpVerb = MAKEINTRESOURCE(menuIndex-1);
cmi.lpParameters = NULL;
cmi.lpDirectory = NULL;
cmi.nShow = SW_SHOWNORMAL;
cmi.dwHotKey = 0;
cmi.hIcon = NULL;
hr=lpcm->InvokeCommand(&cmi);
// When a CD-RW, running under Direct-CD is ejected,
// then hr == 80070057 (I dont know why).
// But the eject itself is performed beautifully.
if (!SUCCEEDED(hr)) {
if(hr == 80070057) {
wsprintf(szTemp, "InvokeCommand failed. hr = %lx", hr);
AfxMessageBox(szTemp, MB_ICONSTOP);
bSuccess = FALSE;
}
}
}
}
}
if(bFoundIt == FALSE) {
AfxMessageBox("Couldn't find 'Eject' in the popup menu", MB_ICONSTOP);
bSuccess = FALSE;
}
}
else bSuccess = FALSE;

DestroyMenu(hMenu);
}
else bSuccess = FALSE;

lpcm->Release();
}
else
{
wsprintf(szTemp, "GetUIObjectOf failed! hr = %lx", hr);
AfxMessageBox(szTemp, MB_ICONSTOP );
bSuccess = FALSE;
}
return bSuccess;
}

This should do it.
Let me know if it works for you
Regards
Kjell-Ake