Drag n'Drop on desktop
Noel DANJOU -- NoelD@msn.com
Sunday, February 16, 1997
Hello,
Environment : VC++ 4.2-flat, Win 95
In one application I would like to be able to drag one or more items from a
CListView to one or more text files on the desktop. I found out that I need to
register the CFSTR_FILECONTENTS & CFSTR_FILEGROUPDESCRIPTOR clipboard formats
and use COleDataSource but that does not work with the code I wrote and I
can't figure out where I am wrong. Please could you help me debug my code I
attached it below :
Thanks for your time,
Noel
-- S O U R C E C O D E --
in View :
// refdbView.cpp : implementation of the CRefdbView class
//
#include "stdafx.h"
#include "afxole.h"
#include "refdb.h"
#include "DataSrc.h" // OLE Drag n'Drop source
#include "shlobj.h" // CFSTR_* definitions
#include "reflectr.h"
#include "refdbDoc.h"
#include "refdbVw.h"
#include "tools.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
[...]
UINT gcfFileContents = 0;
UINT gcfFileGroupDescriptor = 0;
[...]
/////////////////////////////////////////////////////////////////////////////
// CRefdbView message handlers
int CRefdbView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
lpCreateStruct->style |= LVS_REPORT | LVS_OWNERDRAWFIXED;
if (CListView::OnCreate(lpCreateStruct) == -1)
return -1;
// Give the document a pointer to this view
GetDocument()->m_pListView = this;
CreateColumns();
gcfFileContents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
gcfFileGroupDescriptor = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
return 0;
}
[...]
/////////////////////////////////////////////////////////////////////////////
// Drag n'Drop
[...]
void CRefdbView::OnLButtonDown(UINT nFlags, CPoint point)
{
CListView::OnLButtonDown(nFlags, point);
CListCtrlEx& ctlList = (CListCtrlEx&)GetListCtrl();
// Create an OLE data source on the heap
CReflectorDataSource* pDataSource = NULL;
pDataSource = new CReflectorDataSource;
pDataSource->SetListCtrl(&ctlList);
// default FORMATETC is NULL -> tymed = TYMED_HGLOBAL
pDataSource->DelayRenderData(CF_TEXT); // e.g. target is WordPad
pDataSource->DelayRenderData(gcfFileContents); // e.g. target is Desktop
pDataSource->DelayRenderData(gcfFileGroupDescriptor); // required by
FileContents
// Uses default COleDropSource implementation.
if (DROPEFFECT_MOVE ==
pDataSource->DoDragDrop(DROPEFFECT_COPY | DROPEFFECT_MOVE))
{
// TODO: remove items
}
delete pDataSource;
}
void CRefdbView::OnEditCopy()
{
CReflectorDataSource* pDataSource = NULL;
CListCtrlEx& ctlList = (CListCtrlEx&)GetListCtrl();
pDataSource = new CReflectorDataSource;
pDataSource->SetListCtrl(&ctlList);
// default FORMATETC is NULL -> tymed = TYMED_HGLOBAL
pDataSource->DelayRenderData(CF_TEXT); // e.g. target is WordPad
// The Clipboard now owns the allocated memory and will delete this
// data object when new data is put on the Clipboard
pDataSource->SetClipboard();
delete pDataSource;
}
void CRefdbView::OnUpdateEditCopy(CCmdUI* pCmdUI)
{
CListCtrl& ctlList = (CListCtrl&)GetListCtrl();
pCmdUI->Enable(ctlList.GetSelectedCount() > 0);
}
[...]
in DataSrc.cpp :
---------------------
// DataSrc.cpp : implementation file
//
#include "stdafx.h"
#include "refdb.h"
#include "reflectr.h"
#include "DataSrc.h"
#include "shlobj.h" // FILEGROUPDESCRIPTOR structure definition
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define CU_FILE_SIZE 215
#define CU_DATA_SIZE 1024
extern UINT gcfFileContents;
extern UINT gcfFileGroupDescriptor;
/////////////////////////////////////////////////////////////////////////////
// CReflectorDataSource
CReflectorDataSource::CReflectorDataSource()
{
m_pCtlList = NULL;
}
CReflectorDataSource::~CReflectorDataSource()
{
}
BEGIN_MESSAGE_MAP(CReflectorDataSource, COleDataSource)
//{{AFX_MSG_MAP(CReflectorDataSource)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CReflectorDataSource message handlers
BOOL CReflectorDataSource::OnRenderGlobalData(LPFORMATETC lpFormatEtc,
HGLOBAL* phGlobal)
{
ASSERT(gcfFileContents);
ASSERT(gcfFileGroupDescriptor);
if (lpFormatEtc->cfFormat == CF_TEXT)
{
CreateText(phGlobal);
}
else
if (lpFormatEtc->cfFormat == gcfFileContents)
{
CreateFileContents(phGlobal);
}
else
if (lpFormatEtc->cfFormat == gcfFileGroupDescriptor)
{
CreateFileDescriptor(phGlobal);
}
else
{
TRACE("OnRenderGlobalData() : Format not supported !\n");
return FALSE;
}
if (*phGlobal == NULL)
return FALSE;
return TRUE;
}
void CReflectorDataSource::CreateText(HGLOBAL* phData)
{
TRACE("CreateText()\n");
HGLOBAL hData = *phData;
ASSERT(m_pCtlList != NULL);
int count = m_pCtlList->GetSelectedCount();
// allocate space for the number of reflector items
if (hData == NULL)
hData = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)(sizeof(TCHAR) * CU_DATA_SIZE
* count));
// Failure ?
if (hData == NULL)
return;
CString strYes;
CString strNo;
CString strCountry;
CString strLetters;
CString strFullName;
CString strShortName;
CString strIPAddress;
CString strContactName;
CString strContactEmail;
CString strConferenceID;
CString strAddToMenu;
CString strSendVideo;
CString strReceiveVideo;
CString strIsRunning;
strYes.LoadString(IDS_YES);
strNo.LoadString(IDS_NO);
strCountry.LoadString(IDS_COUNTRY);
strLetters.LoadString(IDS_LETTERS);
strFullName.LoadString(IDS_FULLNAME);
strShortName.LoadString(IDS_SHORTNAME);
strIPAddress.LoadString(IDS_IPADDRESS);
strContactName.LoadString(IDS_CONTACT_NAME);
strContactEmail.LoadString(IDS_CONTACT_EMAIL);
strConferenceID.LoadString(IDS_CONFERENCE_ID);
strAddToMenu.LoadString(IDS_ADD_TO_MENU);
strSendVideo.LoadString(IDS_SEND_VIDEO);
strReceiveVideo.LoadString(IDS_RECEIVE_VIDEO);
strIsRunning.LoadString(IDS_IS_RUNNING);
CReflector* pReflector;
LPSTR lpData = (LPSTR)GlobalLock(hData);
*lpData = 0;
int iItem = m_pCtlList->GetNextItem(-1, LVNI_SELECTED);
while (iItem != -1)
{
if ((pReflector = m_pCtlList->GetReflector(iItem)) == NULL)
break;
strcat(lpData, strCountry);
strcat(lpData, pReflector->m_strCountry);
strcat(lpData, strLetters);
strcat(lpData, pReflector->m_strLetters);
strcat(lpData, strFullName);
strcat(lpData, pReflector->m_strFullName);
strcat(lpData, strShortName);
strcat(lpData, pReflector->m_strShortName);
strcat(lpData, strIPAddress);
strcat(lpData, pReflector->m_strIPAddress);
strcat(lpData, strContactName);
strcat(lpData, pReflector->m_strContactName);
strcat(lpData, strContactEmail);
strcat(lpData, pReflector->m_strContactEmail);
strcat(lpData, strConferenceID);
strcat(lpData, pReflector->m_strConferenceID);
strcat(lpData, strAddToMenu);
strcat(lpData, pReflector->m_bAddToMenu ? strYes : strNo);
strcat(lpData, strSendVideo);
strcat(lpData, pReflector->m_bSendVideo ? strYes : strNo);
strcat(lpData, strReceiveVideo);
strcat(lpData, pReflector->m_bReceiveVideo ? strYes : strNo);
strcat(lpData, strIsRunning);
strcat(lpData, pReflector->m_bIsRunning ? strYes : strNo);
strcat(lpData, _T("\n\n"));
// Move to next item
iItem = m_pCtlList->GetNextItem(iItem, LVNI_SELECTED);
}
GlobalUnlock(hData);
*phData = hData;
}
void CReflectorDataSource::CreateFileContents(HGLOBAL* phData)
{
TRACE("CreateFileContents()\n");
HGLOBAL hData = *phData;
ASSERT(m_pCtlList != NULL);
int count = m_pCtlList->GetSelectedCount();
// allocate space for the number of files
if (hData == NULL)
hData = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)(CU_FILE_SIZE * count));
// Failure ?
if (hData == NULL)
return;
LPTSTR lpData = (LPTSTR)GlobalLock(hData);
CReflector* pReflector;
CString strOptions;
strOptions.LoadString(IDS_OPTIONS_TEXT);
int iItem = m_pCtlList->GetNextItem(-1, LVNI_SELECTED);
while (iItem != -1)
{
if ((pReflector = m_pCtlList->GetReflector(iItem)) == NULL)
break;
strcpy(lpData, pReflector->m_strIPAddress);
strcat(lpData, _T("\r\n"));
strcat(lpData, pReflector->m_strConferenceID);
strcat(lpData, strOptions);
TRACE1("content : %s\n", lpData);
lpData += CU_FILE_SIZE;
// Move to next item
iItem = m_pCtlList->GetNextItem(iItem, LVNI_SELECTED);
}
GlobalUnlock(hData);
*phData = hData;
}
static TCHAR BASED_CODE szExt[] = _T(".cu");
void CReflectorDataSource::CreateFileDescriptor(HGLOBAL* phData)
{
TRACE("CreateFileDescriptor()\n");
HGLOBAL hData = *phData;
ASSERT(m_pCtlList != NULL);
int count = m_pCtlList->GetSelectedCount();
ASSERT(count);
// allocate space for FILEGROUPDESCRIPTOR structure plus the number of file
// Note: FILEGROUPDESCRIPTOR holds one FILEDESCRIPTOR that's why "count-1"
if (hData == NULL)
hData = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)(sizeof(FILEGROUPDESCRIPTOR) +
(sizeof(FILEDESCRIPTOR) * (count-1))));
// Failure ?
if (hData == NULL)
return;
LPFILEGROUPDESCRIPTOR pfgd = (LPFILEGROUPDESCRIPTOR)GlobalLock(hData);
pfgd->cItems = count;
CReflector* pReflector;
LPFILEDESCRIPTOR pfd = &pfgd->fgd[0];
CString strFileName;
int iItem = m_pCtlList->GetNextItem(-1, LVNI_SELECTED);
while (iItem != -1)
{
if ((pReflector = m_pCtlList->GetReflector(iItem)) == NULL)
break;
strFileName = pReflector->m_strFullName + szExt;
pfd->dwFlags = FD_FILESIZE; // Only the file name and size fields are valid
pfd->nFileSizeLow = CU_FILE_SIZE;
lstrcpy(pfd->cFileName, strFileName);
TRACE1("filename : %s\n", pfd->cFileName);
pfd++; // point to next FILEDESCRIPTOR structure
// Move to next item
iItem = m_pCtlList->GetNextItem(iItem, LVNI_SELECTED);
}
GlobalUnlock(hData);
*phData = hData;
}
=====================================================
Noel DANJOU 12/21/1968
Village Paillette Software Engineer @ PalmCom
50320 Saint Jean des Champs Email: noeld@msn.com
France UIN : 187712
NetMeeting 2.0 B2: callto:uls.microsoft.com/noeld@msn.com
Home Pages:
http://ourworld.compuserve.com/homepages/noeld/ (personal)
http://ourworld.compuserve.com/homepages/gil_mic/ (company)
=====================================================
Noel DANJOU -- NoelD@msn.com
Wednesday, February 19, 1997
Hello,
Environment : VC++ 4.2-flat, Win 95
In one application I would like to be able to drag one or more items from a
CListView to one or more text files on the desktop. I found out that I need to
register the CFSTR_FILECONTENTS & CFSTR_FILEGROUPDESCRIPTOR clipboard formats
and use COleDataSource but that does not work with the code I wrote and I
can't figure out where I am wrong. Please could you help me debug my code I
attached it below :
Thanks for your time,
Noel
-- S O U R C E C O D E --
in View :
// refdbView.cpp : implementation of the CRefdbView class
//
#include "stdafx.h"
#include "afxole.h"
#include "refdb.h"
#include "DataSrc.h" // OLE Drag n'Drop source
#include "shlobj.h" // CFSTR_* definitions
#include "reflectr.h"
#include "refdbDoc.h"
#include "refdbVw.h"
#include "tools.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
[...]
UINT gcfFileContents = 0;
UINT gcfFileGroupDescriptor = 0;
[...]
/////////////////////////////////////////////////////////////////////////////
// CRefdbView message handlers
int CRefdbView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
lpCreateStruct->style |= LVS_REPORT | LVS_OWNERDRAWFIXED;
if (CListView::OnCreate(lpCreateStruct) == -1)
return -1;
// Give the document a pointer to this view
GetDocument()->m_pListView = this;
CreateColumns();
gcfFileContents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
gcfFileGroupDescriptor = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
return 0;
}
[...]
/////////////////////////////////////////////////////////////////////////////
// Drag n'Drop
[...]
void CRefdbView::OnLButtonDown(UINT nFlags, CPoint point)
{
CListView::OnLButtonDown(nFlags, point);
CListCtrlEx& ctlList = (CListCtrlEx&)GetListCtrl();
// Create an OLE data source on the heap
CReflectorDataSource* pDataSource = NULL;
pDataSource = new CReflectorDataSource;
pDataSource->SetListCtrl(&ctlList);
// default FORMATETC is NULL -> tymed = TYMED_HGLOBAL
pDataSource->DelayRenderData(CF_TEXT); // e.g. target is WordPad
pDataSource->DelayRenderData(gcfFileContents); // e.g. target is Desktop
pDataSource->DelayRenderData(gcfFileGroupDescriptor); // required by
FileContents
// Uses default COleDropSource implementation.
if (DROPEFFECT_MOVE ==
pDataSource->DoDragDrop(DROPEFFECT_COPY | DROPEFFECT_MOVE))
{
// TODO: remove items
}
delete pDataSource;
}
void CRefdbView::OnEditCopy()
{
CReflectorDataSource* pDataSource = NULL;
CListCtrlEx& ctlList = (CListCtrlEx&)GetListCtrl();
pDataSource = new CReflectorDataSource;
pDataSource->SetListCtrl(&ctlList);
// default FORMATETC is NULL -> tymed = TYMED_HGLOBAL
pDataSource->DelayRenderData(CF_TEXT); // e.g. target is WordPad
// The Clipboard now owns the allocated memory and will delete this
// data object when new data is put on the Clipboard
pDataSource->SetClipboard();
delete pDataSource;
}
void CRefdbView::OnUpdateEditCopy(CCmdUI* pCmdUI)
{
CListCtrl& ctlList = (CListCtrl&)GetListCtrl();
pCmdUI->Enable(ctlList.GetSelectedCount() > 0);
}
[...]
in DataSrc.cpp :
---------------------
// DataSrc.cpp : implementation file
//
#include "stdafx.h"
#include "refdb.h"
#include "reflectr.h"
#include "DataSrc.h"
#include "shlobj.h" // FILEGROUPDESCRIPTOR structure definition
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define CU_FILE_SIZE 215
#define CU_DATA_SIZE 1024
extern UINT gcfFileContents;
extern UINT gcfFileGroupDescriptor;
/////////////////////////////////////////////////////////////////////////////
// CReflectorDataSource
CReflectorDataSource::CReflectorDataSource()
{
m_pCtlList = NULL;
}
CReflectorDataSource::~CReflectorDataSource()
{
}
BEGIN_MESSAGE_MAP(CReflectorDataSource, COleDataSource)
//{{AFX_MSG_MAP(CReflectorDataSource)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CReflectorDataSource message handlers
BOOL CReflectorDataSource::OnRenderGlobalData(LPFORMATETC lpFormatEtc,
HGLOBAL* phGlobal)
{
ASSERT(gcfFileContents);
ASSERT(gcfFileGroupDescriptor);
if (lpFormatEtc->cfFormat == CF_TEXT)
{
CreateText(phGlobal);
}
else
if (lpFormatEtc->cfFormat == gcfFileContents)
{
CreateFileContents(phGlobal);
}
else
if (lpFormatEtc->cfFormat == gcfFileGroupDescriptor)
{
CreateFileDescriptor(phGlobal);
}
else
{
TRACE("OnRenderGlobalData() : Format not supported !\n");
return FALSE;
}
if (*phGlobal == NULL)
return FALSE;
return TRUE;
}
void CReflectorDataSource::CreateText(HGLOBAL* phData)
{
TRACE("CreateText()\n");
HGLOBAL hData = *phData;
ASSERT(m_pCtlList != NULL);
int count = m_pCtlList->GetSelectedCount();
// allocate space for the number of reflector items
if (hData == NULL)
hData = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)(sizeof(TCHAR) * CU_DATA_SIZE
* count));
// Failure ?
if (hData == NULL)
return;
CString strYes;
CString strNo;
CString strCountry;
CString strLetters;
CString strFullName;
CString strShortName;
CString strIPAddress;
CString strContactName;
CString strContactEmail;
CString strConferenceID;
CString strAddToMenu;
CString strSendVideo;
CString strReceiveVideo;
CString strIsRunning;
strYes.LoadString(IDS_YES);
strNo.LoadString(IDS_NO);
strCountry.LoadString(IDS_COUNTRY);
strLetters.LoadString(IDS_LETTERS);
strFullName.LoadString(IDS_FULLNAME);
strShortName.LoadString(IDS_SHORTNAME);
strIPAddress.LoadString(IDS_IPADDRESS);
strContactName.LoadString(IDS_CONTACT_NAME);
strContactEmail.LoadString(IDS_CONTACT_EMAIL);
strConferenceID.LoadString(IDS_CONFERENCE_ID);
strAddToMenu.LoadString(IDS_ADD_TO_MENU);
strSendVideo.LoadString(IDS_SEND_VIDEO);
strReceiveVideo.LoadString(IDS_RECEIVE_VIDEO);
strIsRunning.LoadString(IDS_IS_RUNNING);
CReflector* pReflector;
LPSTR lpData = (LPSTR)GlobalLock(hData);
*lpData = 0;
int iItem = m_pCtlList->GetNextItem(-1, LVNI_SELECTED);
while (iItem != -1)
{
if ((pReflector = m_pCtlList->GetReflector(iItem)) == NULL)
break;
strcat(lpData, strCountry);
strcat(lpData, pReflector->m_strCountry);
strcat(lpData, strLetters);
strcat(lpData, pReflector->m_strLetters);
strcat(lpData, strFullName);
strcat(lpData, pReflector->m_strFullName);
strcat(lpData, strShortName);
strcat(lpData, pReflector->m_strShortName);
strcat(lpData, strIPAddress);
strcat(lpData, pReflector->m_strIPAddress);
strcat(lpData, strContactName);
strcat(lpData, pReflector->m_strContactName);
strcat(lpData, strContactEmail);
strcat(lpData, pReflector->m_strContactEmail);
strcat(lpData, strConferenceID);
strcat(lpData, pReflector->m_strConferenceID);
strcat(lpData, strAddToMenu);
strcat(lpData, pReflector->m_bAddToMenu ? strYes : strNo);
strcat(lpData, strSendVideo);
strcat(lpData, pReflector->m_bSendVideo ? strYes : strNo);
strcat(lpData, strReceiveVideo);
strcat(lpData, pReflector->m_bReceiveVideo ? strYes : strNo);
strcat(lpData, strIsRunning);
strcat(lpData, pReflector->m_bIsRunning ? strYes : strNo);
strcat(lpData, _T("\n\n"));
// Move to next item
iItem = m_pCtlList->GetNextItem(iItem, LVNI_SELECTED);
}
GlobalUnlock(hData);
*phData = hData;
}
void CReflectorDataSource::CreateFileContents(HGLOBAL* phData)
{
TRACE("CreateFileContents()\n");
HGLOBAL hData = *phData;
ASSERT(m_pCtlList != NULL);
int count = m_pCtlList->GetSelectedCount();
// allocate space for the number of files
if (hData == NULL)
hData = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)(CU_FILE_SIZE * count));
// Failure ?
if (hData == NULL)
return;
LPTSTR lpData = (LPTSTR)GlobalLock(hData);
CReflector* pReflector;
CString strOptions;
strOptions.LoadString(IDS_OPTIONS_TEXT);
int iItem = m_pCtlList->GetNextItem(-1, LVNI_SELECTED);
while (iItem != -1)
{
if ((pReflector = m_pCtlList->GetReflector(iItem)) == NULL)
break;
strcpy(lpData, pReflector->m_strIPAddress);
strcat(lpData, _T("\r\n"));
strcat(lpData, pReflector->m_strConferenceID);
strcat(lpData, strOptions);
TRACE1("content : %s\n", lpData);
lpData += CU_FILE_SIZE;
// Move to next item
iItem = m_pCtlList->GetNextItem(iItem, LVNI_SELECTED);
}
GlobalUnlock(hData);
*phData = hData;
}
static TCHAR BASED_CODE szExt[] = _T(".cu");
void CReflectorDataSource::CreateFileDescriptor(HGLOBAL* phData)
{
TRACE("CreateFileDescriptor()\n");
HGLOBAL hData = *phData;
ASSERT(m_pCtlList != NULL);
int count = m_pCtlList->GetSelectedCount();
ASSERT(count);
// allocate space for FILEGROUPDESCRIPTOR structure plus the number of file
// Note: FILEGROUPDESCRIPTOR holds one FILEDESCRIPTOR that's why "count-1"
if (hData == NULL)
hData = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)(sizeof(FILEGROUPDESCRIPTOR) +
(sizeof(FILEDESCRIPTOR) * (count-1))));
// Failure ?
if (hData == NULL)
return;
LPFILEGROUPDESCRIPTOR pfgd = (LPFILEGROUPDESCRIPTOR)GlobalLock(hData);
pfgd->cItems = count;
CReflector* pReflector;
LPFILEDESCRIPTOR pfd = &pfgd->fgd[0];
CString strFileName;
int iItem = m_pCtlList->GetNextItem(-1, LVNI_SELECTED);
while (iItem != -1)
{
if ((pReflector = m_pCtlList->GetReflector(iItem)) == NULL)
break;
strFileName = pReflector->m_strFullName + szExt;
pfd->dwFlags = FD_FILESIZE; // Only the file name and size fields are valid
pfd->nFileSizeLow = CU_FILE_SIZE;
lstrcpy(pfd->cFileName, strFileName);
TRACE1("filename : %s\n", pfd->cFileName);
pfd++; // point to next FILEDESCRIPTOR structure
// Move to next item
iItem = m_pCtlList->GetNextItem(iItem, LVNI_SELECTED);
}
GlobalUnlock(hData);
*phData = hData;
}
=====================================================
Noel DANJOU 12/21/1968
Village Paillette Software Engineer @ PalmCom
50320 Saint Jean des Champs Email: noeld@msn.com
France UIN : 187712
NetMeeting 2.0 B2: callto:uls.microsoft.com/noeld@msn.com
Home Pages:
http://ourworld.compuserve.com/homepages/noeld/ (personal)
http://ourworld.compuserve.com/homepages/gil_mic/ (company)
=====================================================
Become an MFC-L member
| Вернуться в корень Архива
|