mirror of
https://github.com/reactos/reactos.git
synced 2025-08-06 17:33:18 +00:00
[SHELL32] Multiple drives property sheet support (#7787)
Supports multiple drives in a single property sheet CORE-20025
This commit is contained in:
parent
d769f5675d
commit
7f49ae633b
8 changed files with 163 additions and 53 deletions
|
@ -689,11 +689,39 @@ CDrvDefExt::~CDrvDefExt()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CDrop
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
STGMEDIUM stgm;
|
||||||
|
HDROP hDrop;
|
||||||
|
|
||||||
|
explicit CDrop(IDataObject *pDO)
|
||||||
|
{
|
||||||
|
FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||||
|
hDrop = SUCCEEDED(hr = pDO->GetData(&format, &stgm)) ? (HDROP)stgm.hGlobal : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
~CDrop()
|
||||||
|
{
|
||||||
|
if (hDrop)
|
||||||
|
ReleaseStgMedium(&stgm);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT GetCount()
|
||||||
|
{
|
||||||
|
return DragQueryFileW(hDrop, -1, NULL, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsValidDrivePath(PCWSTR Path)
|
||||||
|
{
|
||||||
|
return GetDriveTypeW(Path) > DRIVE_NO_ROOT_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT WINAPI
|
HRESULT WINAPI
|
||||||
CDrvDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
|
CDrvDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
|
||||||
{
|
{
|
||||||
FORMATETC format;
|
|
||||||
STGMEDIUM stgm;
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
|
TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
|
||||||
|
@ -701,24 +729,20 @@ CDrvDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY
|
||||||
if (!pDataObj)
|
if (!pDataObj)
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
|
|
||||||
format.cfFormat = CF_HDROP;
|
|
||||||
format.ptd = NULL;
|
|
||||||
format.dwAspect = DVASPECT_CONTENT;
|
|
||||||
format.lindex = -1;
|
|
||||||
format.tymed = TYMED_HGLOBAL;
|
|
||||||
|
|
||||||
hr = pDataObj->GetData(&format, &stgm);
|
CDrop drop(pDataObj);
|
||||||
if (FAILED(hr))
|
if (FAILED_UNEXPECTEDLY(hr = drop.hr))
|
||||||
return hr;
|
return hr;
|
||||||
|
|
||||||
if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszDrive, _countof(m_wszDrive)))
|
if (!DragQueryFileW(drop.hDrop, 0, m_wszDrive, _countof(m_wszDrive)))
|
||||||
{
|
{
|
||||||
ERR("DragQueryFileW failed\n");
|
ERR("DragQueryFileW failed\n");
|
||||||
ReleaseStgMedium(&stgm);
|
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseStgMedium(&stgm);
|
if (drop.GetCount() > 1)
|
||||||
|
m_Multiple = pDataObj;
|
||||||
|
|
||||||
TRACE("Drive properties %ls\n", m_wszDrive);
|
TRACE("Drive properties %ls\n", m_wszDrive);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
@ -745,37 +769,75 @@ CDrvDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR
|
||||||
return E_NOTIMPL;
|
return E_NOTIMPL;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT WINAPI
|
HRESULT
|
||||||
CDrvDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
|
CDrvDefExt::AddMainPage(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
|
||||||
{
|
{
|
||||||
HPROPSHEETPAGE hPage;
|
WCHAR szTitle[MAX_PATH], *pszTitle = NULL;
|
||||||
|
if (m_Multiple)
|
||||||
|
{
|
||||||
|
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl(SHSimpleIDListFromPathW(m_wszDrive));
|
||||||
|
if (SUCCEEDED(SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, szTitle, _countof(szTitle), NULL)))
|
||||||
|
pszTitle = szTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
HPROPSHEETPAGE hPage;
|
||||||
hPage = SH_CreatePropertySheetPageEx(IDD_DRIVE_PROPERTIES, GeneralPageProc, (LPARAM)this,
|
hPage = SH_CreatePropertySheetPageEx(IDD_DRIVE_PROPERTIES, GeneralPageProc, (LPARAM)this,
|
||||||
NULL, &PropSheetPageLifetimeCallback<CDrvDefExt>);
|
pszTitle, &PropSheetPageLifetimeCallback<CDrvDefExt>);
|
||||||
HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam);
|
HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam);
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
if (FAILED_UNEXPECTEDLY(hr))
|
||||||
return hr;
|
return hr;
|
||||||
else
|
else
|
||||||
AddRef(); // For PropSheetPageLifetimeCallback
|
AddRef(); // For PropSheetPageLifetimeCallback
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
if (GetDriveTypeW(m_wszDrive) == DRIVE_FIXED)
|
HRESULT WINAPI
|
||||||
|
CDrvDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
|
||||||
|
{
|
||||||
|
HRESULT hr = AddMainPage(pfnAddPage, lParam);
|
||||||
|
if (FAILED_UNEXPECTEDLY(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
if (m_Multiple)
|
||||||
{
|
{
|
||||||
hPage = SH_CreatePropertySheetPage(IDD_DRIVE_TOOLS,
|
CDrop drop(m_Multiple);
|
||||||
ExtraPageProc,
|
UINT count = SUCCEEDED(drop.hr) ? drop.GetCount() : 0;
|
||||||
(LPARAM)this,
|
for (UINT i = 0; ++i < count;) // Skipping the first drive since it already has a page
|
||||||
NULL);
|
{
|
||||||
if (hPage)
|
CComPtr<CDrvDefExt> SheetExt;
|
||||||
pfnAddPage(hPage, lParam);
|
if (FAILED_UNEXPECTEDLY(hr = ShellObjectCreator(SheetExt)))
|
||||||
|
continue;
|
||||||
|
if (!DragQueryFileW(drop.hDrop, i, SheetExt->m_wszDrive, _countof(SheetExt->m_wszDrive)))
|
||||||
|
continue;
|
||||||
|
if (!IsValidDrivePath(SheetExt->m_wszDrive))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SheetExt->m_Multiple = m_Multiple;
|
||||||
|
SheetExt->AddMainPage(pfnAddPage, lParam);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (GetDriveTypeW(m_wszDrive) != DRIVE_REMOTE)
|
|
||||||
{
|
{
|
||||||
hPage = SH_CreatePropertySheetPage(IDD_DRIVE_HARDWARE,
|
HPROPSHEETPAGE hPage;
|
||||||
HardwarePageProc,
|
if (GetDriveTypeW(m_wszDrive) == DRIVE_FIXED)
|
||||||
(LPARAM)this,
|
{
|
||||||
NULL);
|
hPage = SH_CreatePropertySheetPage(IDD_DRIVE_TOOLS,
|
||||||
if (hPage)
|
ExtraPageProc,
|
||||||
pfnAddPage(hPage, lParam);
|
(LPARAM)this,
|
||||||
|
NULL);
|
||||||
|
if (hPage)
|
||||||
|
pfnAddPage(hPage, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetDriveTypeW(m_wszDrive) != DRIVE_REMOTE)
|
||||||
|
{
|
||||||
|
hPage = SH_CreatePropertySheetPage(IDD_DRIVE_HARDWARE,
|
||||||
|
HardwarePageProc,
|
||||||
|
(LPARAM)this,
|
||||||
|
NULL);
|
||||||
|
if (hPage)
|
||||||
|
pfnAddPage(hPage, lParam);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
|
@ -38,6 +38,9 @@ private:
|
||||||
|
|
||||||
WCHAR m_wszDrive[MAX_PATH];
|
WCHAR m_wszDrive[MAX_PATH];
|
||||||
UINT m_FreeSpacePerc;
|
UINT m_FreeSpacePerc;
|
||||||
|
CComPtr<IDataObject> m_Multiple;
|
||||||
|
|
||||||
|
HRESULT AddMainPage(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CDrvDefExt();
|
CDrvDefExt();
|
||||||
|
|
|
@ -17,6 +17,9 @@ SHELL_GetCaptionFromDataObject(IDataObject *pDO, LPWSTR Buf, UINT cchBuf)
|
||||||
{
|
{
|
||||||
hr = SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, Buf, cchBuf, NULL);
|
hr = SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, Buf, cchBuf, NULL);
|
||||||
ILFree(pidl);
|
ILFree(pidl);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr) && DataObject_GetHIDACount(pDO) > 1)
|
||||||
|
StringCchCatW(Buf, cchBuf, L", ...");
|
||||||
}
|
}
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +133,15 @@ FSFolderItemPropDialogInitCallback(LPCWSTR InitString, IDataObject *pDO, HKEY *h
|
||||||
CDataObjectHIDA cida(pDO);
|
CDataObjectHIDA cida(pDO);
|
||||||
if (SUCCEEDED(cida.hr()) && cida->cidl)
|
if (SUCCEEDED(cida.hr()) && cida->cidl)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
|
CCidaChildArrayHelper items(cida);
|
||||||
|
if (FAILED(hr = items.hr()))
|
||||||
|
return hr;
|
||||||
|
#else
|
||||||
|
// Note: Since we are only passing a single item to AddFSClassKeysToArray,
|
||||||
|
// we don't need the rest of the array to be valid.
|
||||||
PCUITEMID_CHILD pidl = HIDA_GetPIDLItem(cida, 0);
|
PCUITEMID_CHILD pidl = HIDA_GetPIDLItem(cida, 0);
|
||||||
|
#endif
|
||||||
AddFSClassKeysToArray(1, &pidl, hKeys, cKeys); // Add file-type specific pages
|
AddFSClassKeysToArray(1, &pidl, hKeys, cKeys); // Add file-type specific pages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +165,7 @@ SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO)
|
||||||
ShellPropSheetDialog::PFNINITIALIZE InitFunc = NULL;
|
ShellPropSheetDialog::PFNINITIALIZE InitFunc = NULL;
|
||||||
LPCWSTR InitString = NULL;
|
LPCWSTR InitString = NULL;
|
||||||
|
|
||||||
if (_ILIsDrive(HIDA_GetPIDLItem(cida, 0)))
|
if (cida->cidl && _ILIsDrive(HIDA_GetPIDLItem(cida, 0)))
|
||||||
{
|
{
|
||||||
pClsid = &CLSID_ShellDrvDefExt;
|
pClsid = &CLSID_ShellDrvDefExt;
|
||||||
InitFunc = ClassPropDialogInitCallback;
|
InitFunc = ClassPropDialogInitCallback;
|
||||||
|
@ -169,6 +180,18 @@ SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO)
|
||||||
return Dialog.ShowAsync(pClsid, pDO, InitFunc, InitString);
|
return Dialog.ShowAsync(pClsid, pDO, InitFunc, InitString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
SHELL32_ShowFilesystemItemsPropertiesDialogAsync(HWND hOwner, IDataObject *pDO)
|
||||||
|
{
|
||||||
|
if (DataObject_GetHIDACount(pDO) == 1)
|
||||||
|
return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pDO);
|
||||||
|
|
||||||
|
ERR("SHMultiFileProperties is not implemented yet\n");
|
||||||
|
HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
||||||
|
SHELL_ErrorBox(hOwner, hr);
|
||||||
|
return hr; // TODO: return SHMultiFileProperties(pDO, 0);
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO)
|
SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO)
|
||||||
{
|
{
|
||||||
|
|
|
@ -172,6 +172,8 @@ SHELL32_ShowPropertiesDialog(IDataObject *pdtobj);
|
||||||
HRESULT
|
HRESULT
|
||||||
SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO);
|
SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO);
|
||||||
HRESULT
|
HRESULT
|
||||||
|
SHELL32_ShowFilesystemItemsPropertiesDialogAsync(HWND hOwner, IDataObject *pDO);
|
||||||
|
HRESULT
|
||||||
SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO);
|
SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO);
|
||||||
HRESULT
|
HRESULT
|
||||||
SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl);
|
SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl);
|
||||||
|
@ -296,6 +298,7 @@ BOOL PathIsDosDevice(_In_ LPCWSTR pszName);
|
||||||
HRESULT SHELL32_GetDllFromRundll32CommandLine(LPCWSTR pszCmd, LPWSTR pszOut, SIZE_T cchMax);
|
HRESULT SHELL32_GetDllFromRundll32CommandLine(LPCWSTR pszCmd, LPWSTR pszOut, SIZE_T cchMax);
|
||||||
HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl);
|
HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl);
|
||||||
|
|
||||||
|
HRESULT DataObject_GetHIDACount(IDataObject *pdo);
|
||||||
PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index);
|
PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index);
|
||||||
PIDLIST_ABSOLUTE SHELL_DataObject_ILCloneFullItem(_In_ IDataObject *pDO, _In_ UINT Index);
|
PIDLIST_ABSOLUTE SHELL_DataObject_ILCloneFullItem(_In_ IDataObject *pDO, _In_ UINT Index);
|
||||||
HRESULT SHELL_CloneDataObject(_In_ IDataObject *pDO, _Out_ IDataObject **ppDO);
|
HRESULT SHELL_CloneDataObject(_In_ IDataObject *pDO, _Out_ IDataObject **ppDO);
|
||||||
|
|
|
@ -98,6 +98,15 @@ HRESULT WINAPI SHGetAttributesFromDataObject(IDataObject* pDataObject, DWORD dwA
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT DataObject_GetHIDACount(IDataObject *pdo)
|
||||||
|
{
|
||||||
|
if (!pdo)
|
||||||
|
return E_INVALIDARG;
|
||||||
|
CDataObjectHIDA cida(pdo);
|
||||||
|
HRESULT hr = cida.hr();
|
||||||
|
return SUCCEEDED(hr) ? cida->cidl : hr;
|
||||||
|
}
|
||||||
|
|
||||||
PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index)
|
PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index)
|
||||||
{
|
{
|
||||||
if (Index < pCIDA->cidl)
|
if (Index < pCIDA->cidl)
|
||||||
|
@ -120,8 +129,10 @@ HRESULT SHELL_CloneDataObject(_In_ IDataObject *pDO, _Out_ IDataObject **ppDO)
|
||||||
HRESULT hr = cida.hr();
|
HRESULT hr = cida.hr();
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
PCUITEMID_CHILD items = HIDA_GetPIDLItem(cida, 0);
|
CCidaChildArrayHelper items(cida);
|
||||||
hr = SHCreateFileDataObject(HIDA_GetPIDLFolder(cida), cida->cidl, &items, NULL, ppDO);
|
if (FAILED(hr = items.hr()))
|
||||||
|
return hr;
|
||||||
|
hr = SHCreateFileDataObject(HIDA_GetPIDLFolder(cida), cida->cidl, items.GetItems(), NULL, ppDO);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
POINT pt;
|
POINT pt;
|
||||||
|
|
|
@ -509,16 +509,7 @@ SHELL32_ShowPropertiesDialog(IDataObject *pdtobj)
|
||||||
{
|
{
|
||||||
if (!pdtobj)
|
if (!pdtobj)
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
return SHELL32_ShowFilesystemItemsPropertiesDialogAsync(NULL, pdtobj);
|
||||||
CDataObjectHIDA cida(pdtobj);
|
|
||||||
if (FAILED_UNEXPECTEDLY(cida.hr()))
|
|
||||||
return cida.hr();
|
|
||||||
if (cida->cidl > 1)
|
|
||||||
{
|
|
||||||
ERR("SHMultiFileProperties is not yet implemented\n");
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pdtobj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
|
|
|
@ -84,15 +84,6 @@ SHParseDarwinIDFromCacheW(LPCWSTR lpUnknown1, LPWSTR lpUnknown2)
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT DataObject_GetHIDACount(IDataObject *pdo)
|
|
||||||
{
|
|
||||||
if (!pdo)
|
|
||||||
return E_INVALIDARG;
|
|
||||||
CDataObjectHIDA cida(pdo);
|
|
||||||
HRESULT hr = cida.hr();
|
|
||||||
return SUCCEEDED(hr) ? cida->cidl : hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unimplemented
|
* Unimplemented
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -91,6 +91,7 @@ SHELL_CreateFallbackExtractIconForNoAssocFile(REFIID riid, LPVOID *ppvOut)
|
||||||
return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut);
|
return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
struct ClipboardViewerChain
|
struct ClipboardViewerChain
|
||||||
{
|
{
|
||||||
HWND m_hWndNext = HWND_BOTTOM;
|
HWND m_hWndNext = HWND_BOTTOM;
|
||||||
|
@ -124,3 +125,28 @@ struct ClipboardViewerChain
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CCidaChildArrayHelper
|
||||||
|
{
|
||||||
|
// Note: This just creates an array pointing to the items and has the same lifetime as the CIDA.
|
||||||
|
// Use _ILCopyCidaToaPidl if you need the items to outlive the CIDA!
|
||||||
|
explicit CCidaChildArrayHelper(const CIDA *pCida)
|
||||||
|
{
|
||||||
|
m_hr = E_OUTOFMEMORY;
|
||||||
|
m_array = (PCUIDLIST_RELATIVE_ARRAY)SHAlloc(pCida->cidl * sizeof(LPITEMIDLIST));
|
||||||
|
if (m_array)
|
||||||
|
{
|
||||||
|
m_hr = S_OK;
|
||||||
|
for (UINT i = 0; i < pCida->cidl; ++i)
|
||||||
|
*(LPITEMIDLIST*)(&m_array[i]) = (LPITEMIDLIST)HIDA_GetPIDLItem(pCida, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~CCidaChildArrayHelper() { SHFree((LPITEMIDLIST*)m_array); }
|
||||||
|
|
||||||
|
HRESULT hr() const { return m_hr; }
|
||||||
|
PCUIDLIST_RELATIVE_ARRAY GetItems() const { return m_array; }
|
||||||
|
|
||||||
|
HRESULT m_hr;
|
||||||
|
PCUIDLIST_RELATIVE_ARRAY m_array;
|
||||||
|
};
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue