1440 lines
34 KiB
C++
1440 lines
34 KiB
C++
/*++
|
|
|
|
Copyright (c) 1999-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
wrkspace.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the workspace implementation.
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
// #define DBG_WSP
|
|
|
|
#define WSP_ALIGN(Size) (((Size) + 7) & ~7)
|
|
#define WSP_GROW_BY 1024
|
|
|
|
#if DBG
|
|
#define SCORCH_ENTRY(Entry) \
|
|
memset((Entry) + 1, 0xdb, (Entry)->FullSize - sizeof(*(Entry)))
|
|
#else
|
|
#define SCORCH_ENTRY(Entry)
|
|
#endif
|
|
|
|
ULONG g_WspSwitchKey;
|
|
TCHAR g_WspSwitchValue[MAX_PATH];
|
|
BOOL g_WspSwitchBufferAvailable = TRUE;
|
|
|
|
Workspace* g_Workspace;
|
|
BOOL g_ExplicitWorkspace;
|
|
|
|
char* g_WorkspaceKeyNames[] =
|
|
{
|
|
"",
|
|
"Kernel",
|
|
"User",
|
|
"Dump",
|
|
"Remote",
|
|
"Explicit",
|
|
};
|
|
char* g_WorkspaceDefaultName = "Default";
|
|
|
|
char* g_WorkspaceKeyDescriptions[] =
|
|
{
|
|
"Base workspace",
|
|
"Kernel mode workspaces",
|
|
"User mode workspaces",
|
|
"Dump file workspaces",
|
|
"Remote client workspaces",
|
|
"User-saved workspaces",
|
|
};
|
|
|
|
Workspace::Workspace(void)
|
|
{
|
|
m_Flags = 0;
|
|
|
|
m_Data = NULL;
|
|
m_DataLen = 0;
|
|
m_DataUsed = 0;
|
|
|
|
m_Key = WSP_NAME_BASE;
|
|
m_Value = NULL;
|
|
}
|
|
|
|
Workspace::~Workspace(void)
|
|
{
|
|
free(m_Data);
|
|
free(m_Value);
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::Get(WSP_TAG Tag)
|
|
{
|
|
WSP_ENTRY* Entry = NULL;
|
|
while ((Entry = NextEntry(Entry)) != NULL)
|
|
{
|
|
if (Entry->Tag == Tag)
|
|
{
|
|
return Entry;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::GetNext(WSP_ENTRY* Entry, WSP_TAG Tag, WSP_TAG TagMask)
|
|
{
|
|
while ((Entry = NextEntry(Entry)) != NULL)
|
|
{
|
|
if ((Entry->Tag & TagMask) == Tag)
|
|
{
|
|
return Entry;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::GetString(WSP_TAG Tag, PSTR Str, ULONG MaxSize)
|
|
{
|
|
WSP_ENTRY* Entry = Get(Tag);
|
|
|
|
if (Entry != NULL)
|
|
{
|
|
if (Entry->DataSize > MaxSize)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(Str, WSP_ENTRY_DATA(PSTR, Entry));
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::GetAllocString(WSP_TAG Tag, PSTR* Str)
|
|
{
|
|
WSP_ENTRY* Entry = Get(Tag);
|
|
|
|
if (Entry != NULL)
|
|
{
|
|
*Str = (PSTR)malloc(Entry->DataSize);
|
|
if (*Str == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(*Str, WSP_ENTRY_DATA(PSTR, Entry));
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::GetBuffer(WSP_TAG Tag, PVOID Buf, ULONG Size)
|
|
{
|
|
WSP_ENTRY* Entry = Get(Tag);
|
|
|
|
if (Entry != NULL)
|
|
{
|
|
if (Entry->DataSize != Size)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(Buf, WSP_ENTRY_DATA(PUCHAR, Entry), Size);
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::Set(WSP_TAG Tag, ULONG Size)
|
|
{
|
|
WSP_ENTRY* Entry;
|
|
ULONG FullSize;
|
|
|
|
// Compute full rounded size.
|
|
FullSize = sizeof(WSP_ENTRY) + WSP_ALIGN(Size);
|
|
|
|
// Check and see if there's already an entry.
|
|
Entry = Get(Tag);
|
|
if (Entry != NULL)
|
|
{
|
|
// If it's already large enough use it and
|
|
// pack in remaining data.
|
|
if (Entry->FullSize >= FullSize)
|
|
{
|
|
ULONG Pack = Entry->FullSize - FullSize;
|
|
if (Pack > 0)
|
|
{
|
|
PackData((PUCHAR)Entry + FullSize, Pack);
|
|
Entry->FullSize = (USHORT)FullSize;
|
|
}
|
|
|
|
Entry->DataSize = (USHORT)Size;
|
|
SCORCH_ENTRY(Entry);
|
|
m_Flags |= WSPF_DIRTY_WRITE;
|
|
return Entry;
|
|
}
|
|
|
|
// Entry is too small so remove it.
|
|
PackData((PUCHAR)Entry, Entry->FullSize);
|
|
}
|
|
|
|
return Add(Tag, Size);
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::SetString(WSP_TAG Tag, PSTR Str)
|
|
{
|
|
ULONG Size = strlen(Str) + 1;
|
|
WSP_ENTRY* Entry = Set(Tag, Size);
|
|
|
|
if (Entry != NULL)
|
|
{
|
|
memcpy(WSP_ENTRY_DATA(PSTR, Entry), Str, Size);
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::SetStrings(WSP_TAG Tag, ULONG Count, PSTR* Strs)
|
|
{
|
|
ULONG i;
|
|
ULONG Size = 0;
|
|
|
|
for (i = 0; i < Count; i++)
|
|
{
|
|
Size += strlen(Strs[i]) + 1;
|
|
}
|
|
// Put a double terminator at the very end.
|
|
Size++;
|
|
|
|
WSP_ENTRY* Entry = Set(Tag, Size);
|
|
|
|
if (Entry != NULL)
|
|
{
|
|
PSTR Data = WSP_ENTRY_DATA(PSTR, Entry);
|
|
|
|
for (i = 0; i < Count; i++)
|
|
{
|
|
Size = strlen(Strs[i]) + 1;
|
|
memcpy(Data, Strs[i], Size);
|
|
Data += Size;
|
|
}
|
|
*Data = 0;
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::SetBuffer(WSP_TAG Tag, PVOID Buf, ULONG Size)
|
|
{
|
|
WSP_ENTRY* Entry = Set(Tag, Size);
|
|
|
|
if (Entry != NULL)
|
|
{
|
|
memcpy(WSP_ENTRY_DATA(PUCHAR, Entry), Buf, Size);
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::Add(WSP_TAG Tag, ULONG Size)
|
|
{
|
|
// Compute full rounded size.
|
|
ULONG FullSize = sizeof(WSP_ENTRY) + WSP_ALIGN(Size);
|
|
|
|
WSP_ENTRY* Entry = AllocateEntry(FullSize);
|
|
if (Entry != NULL)
|
|
{
|
|
Entry->Tag = Tag;
|
|
Entry->FullSize = (USHORT)FullSize;
|
|
Entry->DataSize = (USHORT)Size;
|
|
SCORCH_ENTRY(Entry);
|
|
m_Flags |= WSPF_DIRTY_WRITE;
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
ULONG
|
|
Workspace::Delete(WSP_TAG Tag, WSP_TAG TagMask)
|
|
{
|
|
ULONG Deleted = 0;
|
|
WSP_ENTRY* Entry = NextEntry(NULL);
|
|
|
|
while (Entry != NULL)
|
|
{
|
|
if ((Entry->Tag & TagMask) == Tag)
|
|
{
|
|
PackData((PUCHAR)Entry, Entry->FullSize);
|
|
Deleted++;
|
|
m_Flags |= WSPF_DIRTY_WRITE;
|
|
|
|
// Check and see if we packed away the last entry.
|
|
if (!ValidEntry(Entry))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Entry = NextEntry(Entry);
|
|
}
|
|
}
|
|
|
|
return Deleted;
|
|
}
|
|
|
|
void
|
|
Workspace::Empty(void)
|
|
{
|
|
// Reset used to just the header.
|
|
m_DataUsed = sizeof(WSP_HEADER);
|
|
|
|
// Nothing is dirty now except the write of emptiness.
|
|
m_Flags = (m_Flags & ~WSPF_DIRTY_ALL) | WSPF_DIRTY_WRITE;
|
|
}
|
|
|
|
HRESULT
|
|
Workspace::Create(ULONG Key, PTSTR Value,
|
|
Workspace** NewWsp)
|
|
{
|
|
Workspace* Wsp = new Workspace;
|
|
if (Wsp == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
Wsp->m_Key = Key;
|
|
if (Value != NULL)
|
|
{
|
|
Wsp->m_Value = _tcsdup(Value);
|
|
if (Wsp->m_Value == NULL)
|
|
{
|
|
delete Wsp;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
WSP_ENTRY* Entry;
|
|
WSP_HEADER* Header;
|
|
|
|
// Allocate intial space for the header and eight
|
|
// small entries. The workspace grows by large amounts
|
|
// so this will immediately allocate a reasonable chunk.
|
|
Entry = Wsp->AllocateEntry(sizeof(WSP_HEADER) +
|
|
8 * (sizeof(WSP_ENTRY) + 2 * sizeof(ULONG64)));
|
|
if (Entry == NULL)
|
|
{
|
|
delete Wsp;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
Header = (WSP_HEADER*)Entry;
|
|
Header->Signature = WSP_SIGNATURE;
|
|
Header->Version = WSP_VERSION;
|
|
|
|
// Reset used to just the header.
|
|
Wsp->m_DataUsed = sizeof(*Header);
|
|
|
|
// Start out dirty so the workspace will be written
|
|
// out and therefore can be opened later.
|
|
Wsp->m_Flags |= WSPF_DIRTY_WRITE;
|
|
|
|
*NewWsp = Wsp;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Workspace::Read(ULONG Key, PTSTR Value,
|
|
Workspace** NewWsp)
|
|
{
|
|
// Make sure basic structures preserve alignment.
|
|
C_ASSERT(sizeof(WSP_HEADER) == WSP_ALIGN(sizeof(WSP_HEADER)));
|
|
C_ASSERT(sizeof(WSP_ENTRY) == WSP_ALIGN(sizeof(WSP_ENTRY)));
|
|
C_ASSERT(sizeof(WSP_COMMONWIN_HEADER) ==
|
|
WSP_ALIGN(sizeof(WSP_COMMONWIN_HEADER)));
|
|
|
|
HRESULT Status;
|
|
|
|
Workspace* Wsp = new Workspace;
|
|
if (Wsp == NULL)
|
|
{
|
|
Status = E_OUTOFMEMORY;
|
|
goto EH_Fail;
|
|
}
|
|
|
|
Wsp->m_Key = Key;
|
|
if (Value != NULL)
|
|
{
|
|
Wsp->m_Value = _tcsdup(Value);
|
|
if (Wsp->m_Value == NULL)
|
|
{
|
|
delete Wsp;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
HKEY RegKey;
|
|
LONG RegStatus;
|
|
BOOL InPrimary;
|
|
|
|
//
|
|
// First check and see if the value exists under the
|
|
// primary key. If not, check the secondary key.
|
|
//
|
|
|
|
RegKey = OpenKey(TRUE, Key, FALSE);
|
|
if (RegKey)
|
|
{
|
|
RegStatus = RegQueryValueEx(RegKey, Value, NULL, NULL, NULL, NULL);
|
|
if (RegStatus != ERROR_SUCCESS && RegStatus != ERROR_MORE_DATA)
|
|
{
|
|
RegCloseKey(RegKey);
|
|
RegKey = NULL;
|
|
}
|
|
}
|
|
if (RegKey == NULL)
|
|
{
|
|
RegKey = OpenKey(FALSE, Key, FALSE);
|
|
if (RegKey == NULL)
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
goto EH_Wsp;
|
|
}
|
|
|
|
InPrimary = FALSE;
|
|
}
|
|
else
|
|
{
|
|
InPrimary = TRUE;
|
|
}
|
|
|
|
DWORD Type;
|
|
DWORD Size;
|
|
|
|
Size = 0;
|
|
RegStatus = RegQueryValueEx(RegKey, Value, NULL, &Type, NULL, &Size);
|
|
if (RegStatus != ERROR_SUCCESS && RegStatus != ERROR_MORE_DATA)
|
|
{
|
|
if (RegStatus == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
else
|
|
{
|
|
Status = HRESULT_FROM_WIN32(RegStatus);
|
|
}
|
|
goto EH_Key;
|
|
}
|
|
if (Type != REG_BINARY ||
|
|
WSP_ALIGN(Size) != Size)
|
|
{
|
|
Status = E_INVALIDARG;
|
|
goto EH_Key;
|
|
}
|
|
|
|
WSP_ENTRY* Entry;
|
|
WSP_HEADER* Header;
|
|
|
|
Entry = Wsp->AllocateEntry(Size);
|
|
if (Entry == NULL)
|
|
{
|
|
Status = E_OUTOFMEMORY;
|
|
goto EH_Key;
|
|
}
|
|
Header = (WSP_HEADER*)Entry;
|
|
|
|
if (RegQueryValueEx(RegKey, Value, NULL, &Type, (LPBYTE)Header, &Size) !=
|
|
ERROR_SUCCESS ||
|
|
Header->Signature != WSP_SIGNATURE ||
|
|
Header->Version != WSP_VERSION)
|
|
{
|
|
Status = E_INVALIDARG;
|
|
goto EH_Key;
|
|
}
|
|
|
|
RegCloseKey(RegKey);
|
|
|
|
//
|
|
// If the workspace was read from the secondary key
|
|
// migrate it to the primary and remove the secondary
|
|
// entry.
|
|
//
|
|
|
|
if (!InPrimary)
|
|
{
|
|
if (Wsp->WriteReg() == S_OK)
|
|
{
|
|
Wsp->DeleteReg(FALSE);
|
|
}
|
|
}
|
|
|
|
*NewWsp = Wsp;
|
|
return S_OK;
|
|
|
|
EH_Key:
|
|
RegCloseKey(RegKey);
|
|
EH_Wsp:
|
|
delete Wsp;
|
|
EH_Fail:
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
Workspace::ChangeName(ULONG Key, PTSTR Value, BOOL Force)
|
|
{
|
|
if (!Force)
|
|
{
|
|
HKEY RegKey;
|
|
|
|
//
|
|
// Check and see if a workspace entry already
|
|
// exists under the given name. We only need
|
|
// to check the primary key as we're only concerned
|
|
// with overwriting and writing always occurs
|
|
// to the primary key.
|
|
//
|
|
|
|
RegKey = OpenKey(TRUE, Key, FALSE);
|
|
if (RegKey != NULL)
|
|
{
|
|
LONG RegStatus;
|
|
|
|
RegStatus = RegQueryValueEx(RegKey, Value, NULL, NULL,
|
|
NULL, NULL);
|
|
|
|
RegCloseKey(RegKey);
|
|
|
|
if (RegStatus == ERROR_SUCCESS || RegStatus == ERROR_MORE_DATA)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Swap the workspace name.
|
|
//
|
|
|
|
PTSTR NewValue;
|
|
|
|
if (Value != NULL)
|
|
{
|
|
NewValue = _tcsdup(Value);
|
|
if (NewValue == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NewValue = NULL;
|
|
}
|
|
|
|
delete m_Value;
|
|
m_Key = Key;
|
|
m_Value = NewValue;
|
|
// Need to write data out to the new location.
|
|
m_Flags |= WSPF_DIRTY_WRITE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
Workspace::UpdateBreakpointInformation(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = g_BpCmdsBuffer->UiLockForRead();
|
|
if (Status == S_OK)
|
|
{
|
|
// Clear old information.
|
|
Delete(WSP_GLOBAL_BREAKPOINTS, WSP_TAG_MASK);
|
|
|
|
// Only save an entry if there are breakpoints.
|
|
// Minimum output is a newline and terminator so
|
|
// don't count those.
|
|
if (g_BpCmdsBuffer->GetDataLen() > 2)
|
|
{
|
|
PSTR Cmds = (PSTR)g_BpCmdsBuffer->GetDataBuffer();
|
|
SetString(WSP_GLOBAL_BREAKPOINTS, Cmds);
|
|
}
|
|
|
|
UnlockStateBuffer(g_BpCmdsBuffer);
|
|
}
|
|
}
|
|
|
|
void
|
|
Workspace::UpdateWindowInformation(void)
|
|
{
|
|
// Clear old information.
|
|
Delete(DEF_WSP_TAG(WSP_GROUP_WINDOW, 0), WSP_GROUP_MASK);
|
|
|
|
//
|
|
// Record the frame window state.
|
|
//
|
|
|
|
WINDOWPLACEMENT Place;
|
|
|
|
Place.length = sizeof(Place);
|
|
GetWindowPlacement(g_hwndFrame, &Place);
|
|
SetBuffer(WSP_WINDOW_FRAME_PLACEMENT, &Place, sizeof(Place));
|
|
|
|
//
|
|
// Persist windows from the bottom of the Z order up
|
|
// so that when they're recreated in the same order
|
|
// the Z order is also recreated.
|
|
//
|
|
|
|
HWND Win = MDIGetActive(g_hwndMDIClient, NULL);
|
|
if (Win == NULL ||
|
|
(Win = GetWindow(Win, GW_HWNDLAST)) == NULL)
|
|
{
|
|
// No windows.
|
|
return;
|
|
}
|
|
|
|
while (Win != NULL)
|
|
{
|
|
PCOMMONWIN_DATA WinData = GetCommonWinData(Win);
|
|
if (WinData != NULL)
|
|
{
|
|
WSP_ENTRY* Entry;
|
|
ULONG Size;
|
|
|
|
Size = WinData->GetWorkspaceSize();
|
|
Entry = Add(WSP_WINDOW_COMMONWIN_1,
|
|
Size + sizeof(WSP_COMMONWIN_HEADER));
|
|
if (Entry != NULL)
|
|
{
|
|
WSP_COMMONWIN_HEADER* Hdr =
|
|
WSP_ENTRY_DATA(WSP_COMMONWIN_HEADER*, Entry);
|
|
Hdr->Type = WinData->m_enumType;
|
|
Hdr->Reserved = 0;
|
|
|
|
if (Size > 0)
|
|
{
|
|
WinData->SetWorkspace((PUCHAR)(Hdr + 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
Win = GetWindow(Win, GW_HWNDPREV);
|
|
}
|
|
}
|
|
|
|
void
|
|
Workspace::UpdateLogFileInformation(void)
|
|
{
|
|
HRESULT Status;
|
|
char LogFile[MAX_PATH];
|
|
BOOL Append;
|
|
ULONG FileLen;
|
|
|
|
Status = g_pUiControl->GetLogFile(LogFile, sizeof(LogFile), NULL,
|
|
&Append);
|
|
if (Status != S_OK && Status != E_NOINTERFACE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Clear old information.
|
|
Delete(WSP_GLOBAL_LOG_FILE, WSP_TAG_MASK);
|
|
|
|
if (Status == E_NOINTERFACE)
|
|
{
|
|
// No log is open.
|
|
return;
|
|
}
|
|
|
|
FileLen = strlen(LogFile) + 1;
|
|
|
|
WSP_ENTRY* Entry = Set(WSP_GLOBAL_LOG_FILE, sizeof(BOOL) + FileLen);
|
|
if (Entry != NULL)
|
|
{
|
|
PSTR Data = WSP_ENTRY_DATA(PSTR, Entry);
|
|
*(PBOOL)Data = Append;
|
|
strcpy(Data + sizeof(Append), LogFile);
|
|
}
|
|
}
|
|
|
|
void
|
|
Workspace::UpdatePathInformation(void)
|
|
{
|
|
HRESULT Status;
|
|
char Path[MAX_ENGINE_PATH];
|
|
|
|
Status = g_pUiSymbols->GetSymbolPath(Path, sizeof(Path), NULL);
|
|
if (Status == S_OK)
|
|
{
|
|
SetString(WSP_GLOBAL_SYMBOL_PATH, Path);
|
|
}
|
|
Status = g_pUiSymbols->GetImagePath(Path, sizeof(Path), NULL);
|
|
if (Status == S_OK)
|
|
{
|
|
SetString(WSP_GLOBAL_IMAGE_PATH, Path);
|
|
}
|
|
Status = g_pUiSymbols->GetSourcePath(Path, sizeof(Path), NULL);
|
|
if (Status == S_OK)
|
|
{
|
|
SetString(WSP_GLOBAL_SOURCE_PATH, Path);
|
|
}
|
|
|
|
// Local source path is only set explicitly.
|
|
}
|
|
|
|
void
|
|
Workspace::UpdateFilterInformation(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = g_FilterBuffer->UiLockForRead();
|
|
if (Status == S_OK)
|
|
{
|
|
// Clear old information.
|
|
Delete(WSP_GLOBAL_FILTERS, WSP_TAG_MASK);
|
|
|
|
// Only save an entry if there are changes.
|
|
// Minimum output is a newline and terminator so
|
|
// don't count those.
|
|
if (g_FilterWspCmdsOffset < g_FilterBuffer->GetDataLen() - 2)
|
|
{
|
|
PSTR Cmds = (PSTR)g_FilterBuffer->GetDataBuffer() +
|
|
g_FilterWspCmdsOffset;
|
|
SetString(WSP_GLOBAL_FILTERS, Cmds);
|
|
}
|
|
|
|
UnlockStateBuffer(g_FilterBuffer);
|
|
}
|
|
}
|
|
|
|
void
|
|
Workspace::UpdateMruListInformation(void)
|
|
{
|
|
ULONG Size;
|
|
WSP_ENTRY* Entry;
|
|
|
|
// Clear old information.
|
|
Delete(WSP_GLOBAL_MRU_LIST, WSP_TAG_MASK);
|
|
|
|
Size = GetMruSize();
|
|
Entry = Set(WSP_GLOBAL_MRU_LIST, Size);
|
|
if (Entry != NULL)
|
|
{
|
|
WriteMru(WSP_ENTRY_DATA(PUCHAR, Entry));
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
Workspace::WriteReg(void)
|
|
{
|
|
// Writing always occurs to the primary key.
|
|
HKEY RegKey = OpenKey(TRUE, m_Key, TRUE);
|
|
if (RegKey == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
LONG Status = RegSetValueEx(RegKey, m_Value, 0, REG_BINARY,
|
|
m_Data, m_DataUsed);
|
|
|
|
RegCloseKey(RegKey);
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
return HRESULT_FROM_WIN32(Status);
|
|
}
|
|
else
|
|
{
|
|
m_Flags &= ~WSPF_DIRTY_ALL;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
void
|
|
Workspace::DeleteReg(BOOL Primary)
|
|
{
|
|
DeleteRegKey(Primary, m_Key, m_Value);
|
|
|
|
// We don't want to leave any dirty bits
|
|
// on because the workspace would just be written
|
|
// out again at the next flush.
|
|
m_Flags &= ~WSPF_DIRTY_ALL;
|
|
}
|
|
|
|
void
|
|
Workspace::DeleteRegKey(BOOL Primary, ULONG Key, PTSTR Value)
|
|
{
|
|
HKEY RegKey = OpenKey(Primary, Key, FALSE);
|
|
if (RegKey != NULL)
|
|
{
|
|
RegDeleteValue(RegKey, Value);
|
|
RegCloseKey(RegKey);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
Workspace::Flush(BOOL ForceSave, BOOL Cancellable)
|
|
{
|
|
if (getenv("WINDBG_NO_WORKSPACE_WINDOWS") != NULL)
|
|
{
|
|
// Window layout saving is suppressed so don't
|
|
// consider them dirty.
|
|
m_Flags &= ~WSPF_DIRTY_WINDOWS;
|
|
}
|
|
|
|
if ((m_Flags & WSPF_DIRTY_ALL) == 0 ||
|
|
(g_QuietMode && !ForceSave))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
#ifdef DBG_WSP
|
|
DebugPrint("Workspace dirty flags %X\n", m_Flags & WSPF_DIRTY_ALL);
|
|
#endif
|
|
|
|
WORD Str;
|
|
PTSTR Arg;
|
|
|
|
if (!strcmp(m_Value, g_WorkspaceDefaultName))
|
|
{
|
|
Arg = g_WorkspaceKeyNames[m_Key];
|
|
if (m_Key == WSP_NAME_BASE)
|
|
{
|
|
Str = STR_Save_Base_Workspace;
|
|
}
|
|
else
|
|
{
|
|
Str = STR_Save_Default_Workspace;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Str = STR_Save_Specific_Workspace;
|
|
Arg = m_Value;
|
|
}
|
|
|
|
int Answer;
|
|
|
|
if (ForceSave)
|
|
{
|
|
Answer = IDOK;
|
|
}
|
|
else
|
|
{
|
|
Answer = QuestionBox(Str, Cancellable ? MB_YESNOCANCEL : MB_YESNO,
|
|
Arg);
|
|
}
|
|
|
|
if (Answer == IDNO)
|
|
{
|
|
return S_OK;
|
|
}
|
|
else if (Answer == IDCANCEL)
|
|
{
|
|
Assert(Cancellable);
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (m_Flags & WSPF_DIRTY_BREAKPOINTS)
|
|
{
|
|
UpdateBreakpointInformation();
|
|
}
|
|
if (m_Flags & WSPF_DIRTY_WINDOWS)
|
|
{
|
|
UpdateWindowInformation();
|
|
}
|
|
if (m_Flags & WSPF_DIRTY_LOG_FILE)
|
|
{
|
|
UpdateLogFileInformation();
|
|
}
|
|
if (m_Flags & WSPF_DIRTY_PATHS)
|
|
{
|
|
UpdatePathInformation();
|
|
}
|
|
if (m_Flags & WSPF_DIRTY_FILTERS)
|
|
{
|
|
UpdateFilterInformation();
|
|
}
|
|
if (m_Flags & WSPF_DIRTY_MRU_LIST)
|
|
{
|
|
UpdateMruListInformation();
|
|
}
|
|
|
|
return WriteReg();
|
|
}
|
|
|
|
WSP_ENTRY*
|
|
Workspace::AllocateEntry(ULONG FullSize)
|
|
{
|
|
// Sizes must fit in USHORTs. This shouldn't be
|
|
// a big problem since workspaces shouldn't have
|
|
// huge data items in them.
|
|
if (FullSize > 0xffff)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (m_DataUsed + FullSize > m_DataLen)
|
|
{
|
|
ULONG NewLen = m_DataLen;
|
|
do
|
|
{
|
|
NewLen += WSP_GROW_BY;
|
|
}
|
|
while (m_DataUsed + FullSize > NewLen);
|
|
|
|
PUCHAR NewData = (PUCHAR)realloc(m_Data, NewLen);
|
|
if (NewData == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
m_Data = NewData;
|
|
m_DataLen = NewLen;
|
|
}
|
|
|
|
WSP_ENTRY* Entry = (WSP_ENTRY*)(m_Data + m_DataUsed);
|
|
m_DataUsed += FullSize;
|
|
return Entry;
|
|
}
|
|
|
|
void
|
|
Workspace::GetKeyName(ULONG Key, PSTR KeyName)
|
|
{
|
|
_tcscpy(KeyName, WSP_REG_KEY);
|
|
if (Key > WSP_NAME_BASE)
|
|
{
|
|
_tcscat(KeyName, "\\");
|
|
_tcscat(KeyName, g_WorkspaceKeyNames[Key]);
|
|
}
|
|
}
|
|
|
|
HKEY
|
|
Workspace::OpenKey(BOOL Primary, ULONG Key, BOOL Create)
|
|
{
|
|
TCHAR KeyName[MAX_PATH];
|
|
HKEY RegKey;
|
|
HKEY Base = Primary ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
|
|
|
GetKeyName(Key, KeyName);
|
|
if (Create)
|
|
{
|
|
if (RegCreateKeyEx(Base, KeyName, 0, NULL, 0,
|
|
KEY_ALL_ACCESS, NULL, &RegKey,
|
|
NULL) == ERROR_SUCCESS)
|
|
{
|
|
return RegKey;
|
|
}
|
|
}
|
|
else if (RegOpenKeyEx(Base, KeyName, 0, KEY_ALL_ACCESS,
|
|
&RegKey) == ERROR_SUCCESS)
|
|
{
|
|
return RegKey;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
Workspace::Apply(ULONG Flags)
|
|
{
|
|
WSP_ENTRY* Entry;
|
|
ULONG Count;
|
|
PUCHAR Data;
|
|
BOOL UpdateColors = FALSE;
|
|
int SessionStarts;
|
|
ULONG MemWins = 0;
|
|
|
|
#ifdef DBG_WSP
|
|
DebugPrint("Applying workspace %s%s%s with:\n",
|
|
m_Key == NULL ? "" : m_Key,
|
|
m_Key == NULL ? "" : "\\",
|
|
m_Value);
|
|
#endif
|
|
|
|
//
|
|
// Scan for explicit session start entries first.
|
|
// If any are present and a session is active
|
|
// fail the apply before anything actually happens.
|
|
//
|
|
|
|
if ((Flags & (WSP_APPLY_AGAIN |
|
|
WSP_APPLY_EXPLICIT)) == WSP_APPLY_EXPLICIT &&
|
|
g_EngineThreadId)
|
|
{
|
|
Entry = NULL;
|
|
while ((Entry = NextEntry(Entry)) != NULL)
|
|
{
|
|
switch(Entry->Tag)
|
|
{
|
|
case WSP_GLOBAL_EXE_COMMAND_LINE:
|
|
case WSP_GLOBAL_DUMP_FILE_NAME:
|
|
case WSP_GLOBAL_ATTACH_KERNEL_FLAGS:
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
SessionStarts = 0;
|
|
Entry = NULL;
|
|
while ((Entry = NextEntry(Entry)) != NULL)
|
|
{
|
|
#ifdef DBG_WSP
|
|
DebugPrint(" %04X: Tag: %08X Size %X:%X\n",
|
|
(PUCHAR)Entry - m_Data, Entry->Tag,
|
|
Entry->DataSize, Entry->FullSize);
|
|
#endif
|
|
|
|
// If this is a reapply only a subset of the
|
|
// workspace is applied to prevent duplication
|
|
// and problems.
|
|
if ((Flags & WSP_APPLY_AGAIN) &&
|
|
Entry->Tag != WSP_GLOBAL_BREAKPOINTS &&
|
|
Entry->Tag != WSP_GLOBAL_REGISTER_MAP)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (WSP_TAG_GROUP(Entry->Tag) == WSP_GROUP_COLORS)
|
|
{
|
|
if (SetColor(WSP_TAG_ITEM(Entry->Tag),
|
|
*WSP_ENTRY_DATA(COLORREF*, Entry)))
|
|
{
|
|
UpdateColors = TRUE;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
switch(Entry->Tag)
|
|
{
|
|
case WSP_GLOBAL_SYMBOL_PATH:
|
|
g_pUiSymbols->SetSymbolPath(WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
case WSP_GLOBAL_IMAGE_PATH:
|
|
g_pUiSymbols->SetImagePath(WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
case WSP_GLOBAL_SOURCE_PATH:
|
|
g_pUiSymbols->SetSourcePath(WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
case WSP_GLOBAL_WINDOW_OPTIONS:
|
|
g_WinOptions = *WSP_ENTRY_DATA(PULONG, Entry);
|
|
if (g_WinOptions & WOPT_AUTO_ARRANGE)
|
|
{
|
|
Arrange();
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_REGISTER_MAP:
|
|
Count = Entry->DataSize / sizeof(*g_RegisterMap);
|
|
g_RegisterMap = new USHORT[Count];
|
|
if (g_RegisterMap != NULL)
|
|
{
|
|
memcpy(g_RegisterMap, WSP_ENTRY_DATA(PUSHORT, Entry),
|
|
Count * sizeof(*g_RegisterMap));
|
|
g_RegisterMapEntries = Count;
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_BREAKPOINTS:
|
|
Assert(Entry->DataSize > 1);
|
|
AddStringMultiCommand(UIC_INVISIBLE_EXECUTE,
|
|
WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
case WSP_GLOBAL_LOG_FILE:
|
|
Data = WSP_ENTRY_DATA(PUCHAR, Entry);
|
|
g_pUiControl->OpenLogFile((PSTR)Data + sizeof(BOOL), *(PBOOL)Data);
|
|
break;
|
|
case WSP_GLOBAL_LOCAL_SOURCE_PATH:
|
|
if (g_RemoteClient)
|
|
{
|
|
g_pUiLocSymbols->SetSourcePath(WSP_ENTRY_DATA(PSTR, Entry));
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_FILTERS:
|
|
Assert(Entry->DataSize > 1);
|
|
AddStringMultiCommand(UIC_INVISIBLE_EXECUTE,
|
|
WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
case WSP_GLOBAL_FIXED_LOGFONT:
|
|
g_Fonts[FONT_FIXED].LogFont = *WSP_ENTRY_DATA(LPLOGFONT, Entry);
|
|
CreateIndexedFont(FONT_FIXED, TRUE);
|
|
break;
|
|
case WSP_GLOBAL_TAB_WIDTH:
|
|
SetTabWidth(*WSP_ENTRY_DATA(PULONG, Entry));
|
|
break;
|
|
case WSP_GLOBAL_MRU_LIST:
|
|
Data = WSP_ENTRY_DATA(PUCHAR, Entry);
|
|
ReadMru(Data, Data + Entry->DataSize);
|
|
break;
|
|
case WSP_GLOBAL_REPEAT_COMMANDS:
|
|
if (*WSP_ENTRY_DATA(PULONG, Entry))
|
|
{
|
|
g_pUiControl->
|
|
RemoveEngineOptions(DEBUG_ENGOPT_NO_EXECUTE_REPEAT);
|
|
}
|
|
else
|
|
{
|
|
g_pUiControl->
|
|
AddEngineOptions(DEBUG_ENGOPT_NO_EXECUTE_REPEAT);
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_COM_SETTINGS:
|
|
if (Entry->DataSize <= sizeof(g_ComSettings))
|
|
{
|
|
memcpy(g_ComSettings, WSP_ENTRY_DATA(PSTR, Entry),
|
|
Entry->DataSize);
|
|
PrintAllocString(&g_KernelConnectOptions, 256,
|
|
"com:port=%s,baud=%s", g_ComSettings,
|
|
g_ComSettings + strlen(g_ComSettings) + 1);
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_1394_SETTINGS:
|
|
if (Entry->DataSize <= sizeof(g_1394Settings))
|
|
{
|
|
memcpy(g_1394Settings, WSP_ENTRY_DATA(PSTR, Entry),
|
|
Entry->DataSize);
|
|
PrintAllocString(&g_KernelConnectOptions, 256,
|
|
"1394:channel=%s", g_1394Settings);
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_DISASM_ACTIVATE_SOURCE:
|
|
g_DisasmActivateSource = *WSP_ENTRY_DATA(PULONG, Entry);
|
|
break;
|
|
case WSP_GLOBAL_VIEW_TOOL_BAR:
|
|
CheckMenuItem(g_hmenuMain, IDM_VIEW_TOOLBAR,
|
|
*WSP_ENTRY_DATA(PULONG, Entry) ?
|
|
MF_CHECKED : MF_UNCHECKED);
|
|
Show_Toolbar(*WSP_ENTRY_DATA(PULONG, Entry));
|
|
break;
|
|
case WSP_GLOBAL_VIEW_STATUS_BAR:
|
|
CheckMenuItem(g_hmenuMain, IDM_VIEW_STATUS,
|
|
*WSP_ENTRY_DATA(PULONG, Entry) ?
|
|
MF_CHECKED : MF_UNCHECKED);
|
|
Show_StatusBar(*WSP_ENTRY_DATA(PULONG, Entry));
|
|
break;
|
|
case WSP_GLOBAL_AUTO_CMD_SCROLL:
|
|
g_AutoCmdScroll = *WSP_ENTRY_DATA(PULONG, Entry);
|
|
break;
|
|
case WSP_GLOBAL_SRC_FILE_PATH:
|
|
strcpy(g_SrcFilePath, WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
case WSP_GLOBAL_EXE_COMMAND_LINE:
|
|
if ((Flags & WSP_APPLY_EXPLICIT) &&
|
|
DupAllocString(&g_DebugCommandLine,
|
|
WSP_ENTRY_DATA(PSTR, Entry)))
|
|
{
|
|
SessionStarts++;
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_EXE_CREATE_FLAGS:
|
|
g_DebugCreateFlags = *WSP_ENTRY_DATA(PULONG, Entry);
|
|
break;
|
|
case WSP_GLOBAL_DUMP_FILE_NAME:
|
|
if ((Flags & WSP_APPLY_EXPLICIT) &&
|
|
DupAllocString(&g_DumpFile,
|
|
WSP_ENTRY_DATA(PSTR, Entry)))
|
|
{
|
|
SessionStarts++;
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_ATTACH_KERNEL_FLAGS:
|
|
if ((Flags & WSP_APPLY_EXPLICIT))
|
|
{
|
|
g_AttachKernelFlags = *WSP_ENTRY_DATA(PULONG, Entry);
|
|
SessionStarts++;
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_TYPE_OPTIONS:
|
|
{
|
|
g_TypeOptions = *WSP_ENTRY_DATA(PULONG, Entry);
|
|
if (g_pUiSymbols2 != NULL)
|
|
{
|
|
g_pUiSymbols2->SetTypeOptions(g_TypeOptions);
|
|
}
|
|
}
|
|
break;
|
|
case WSP_GLOBAL_DUMP_FILE_PATH:
|
|
strcpy(g_DumpFilePath, WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
case WSP_GLOBAL_EXE_FILE_PATH:
|
|
strcpy(g_ExeFilePath, WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
|
|
case WSP_WINDOW_COMMONWIN_1:
|
|
WSP_COMMONWIN_HEADER* Hdr;
|
|
HWND Win;
|
|
PCOMMONWIN_DATA WinData;
|
|
|
|
Hdr = WSP_ENTRY_DATA(WSP_COMMONWIN_HEADER*, Entry);
|
|
Win = New_OpenDebugWindow(Hdr->Type, TRUE, MemWins);
|
|
if (Win != NULL &&
|
|
(WinData = GetCommonWinData(Win)) != NULL &&
|
|
Entry->DataSize > sizeof(WSP_COMMONWIN_HEADER))
|
|
{
|
|
Data = (PUCHAR)(Hdr + 1);
|
|
WinData->m_InAutoOp++;
|
|
WinData->ApplyWorkspace1(Data, Data +
|
|
(Entry->DataSize -
|
|
sizeof(WSP_COMMONWIN_HEADER)));
|
|
WinData->m_InAutoOp--;
|
|
}
|
|
|
|
// A user can have as many open memory windows as
|
|
// they like, which makes things a little tricky
|
|
// for workspaces as applying stacked workspaces
|
|
// could result in memory windows multiplying out
|
|
// of control if the same set of memory windows
|
|
// is saved in each workspace level. To avoid
|
|
// this and to function more like the other windows
|
|
// we reuse memory windows from any that are
|
|
// already in existence.
|
|
if (Hdr->Type == MEM_WINDOW)
|
|
{
|
|
MemWins++;
|
|
}
|
|
break;
|
|
case WSP_WINDOW_FRAME_PLACEMENT:
|
|
LPWINDOWPLACEMENT Place;
|
|
|
|
Place = WSP_ENTRY_DATA(LPWINDOWPLACEMENT, Entry);
|
|
SetWindowPlacement(g_hwndFrame, Place);
|
|
break;
|
|
case WSP_WINDOW_FRAME_TITLE:
|
|
SetTitleExplicitText(WSP_ENTRY_DATA(PSTR, Entry));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (UpdateColors)
|
|
{
|
|
UpdateAllColors();
|
|
}
|
|
|
|
return SessionStarts;
|
|
}
|
|
|
|
HRESULT
|
|
UiSwitchWorkspace(ULONG Key, PTSTR Value, BOOL Create,
|
|
ULONG Flags, int* SessionStarts)
|
|
{
|
|
if (getenv("WINDBG_NO_WORKSPACE") != NULL)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT Status;
|
|
Workspace* OldWsp;
|
|
Workspace* NewWsp;
|
|
int Starts = 0;
|
|
|
|
Status = Workspace::Read(Key, Value, &NewWsp);
|
|
if (Status != S_OK)
|
|
{
|
|
if (Status == E_NOINTERFACE && Create)
|
|
{
|
|
// Workspace does not exist so create a new one.
|
|
Status = Workspace::Create(Key, Value, &NewWsp);
|
|
}
|
|
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// We have a new workspace ready to go so flush the old one.
|
|
OldWsp = g_Workspace;
|
|
if (OldWsp != NULL)
|
|
{
|
|
OldWsp->Flush(FALSE, FALSE);
|
|
}
|
|
|
|
// Apply the new workspace with no global workspace to
|
|
// avoid writing changes into the workspace as we apply it.
|
|
g_Workspace = NULL;
|
|
if (NewWsp != NULL)
|
|
{
|
|
Starts = NewWsp->Apply(Flags);
|
|
|
|
// Clear any window messages queued during the workspace
|
|
// application so that they're processed with no
|
|
// active workspace.
|
|
ProcessPendingMessages();
|
|
}
|
|
|
|
if (SessionStarts != NULL)
|
|
{
|
|
*SessionStarts = Starts;
|
|
}
|
|
|
|
if (Starts < 0)
|
|
{
|
|
// Apply failed so put the old workspace back.
|
|
g_Workspace = OldWsp;
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
// Apply succeeded to replace the old workspace.
|
|
g_Workspace = NewWsp;
|
|
delete OldWsp;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UiDelayedSwitchWorkspace(void)
|
|
{
|
|
Assert(!g_WspSwitchBufferAvailable);
|
|
|
|
HRESULT Status = UiSwitchWorkspace(g_WspSwitchKey, g_WspSwitchValue, TRUE,
|
|
WSP_APPLY_DEFAULT, NULL);
|
|
|
|
// Mark the delayed switch buffer as available and
|
|
// wait for acknowledgement.
|
|
g_WspSwitchBufferAvailable = TRUE;
|
|
while (g_WspSwitchValue[0])
|
|
{
|
|
Sleep(50);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
EngSwitchWorkspace(ULONG Key, PTSTR Value)
|
|
{
|
|
// If the user explicitly selected a workspace
|
|
// don't override it due to engine activity.
|
|
if (g_ExplicitWorkspace ||
|
|
g_Exit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// We can't switch workspaces on the engine thread
|
|
// because of the UI work involved. Send the
|
|
// switch over to the UI thread and wait for
|
|
// it to be processed.
|
|
|
|
Assert(g_WspSwitchBufferAvailable);
|
|
g_WspSwitchBufferAvailable = FALSE;
|
|
|
|
g_WspSwitchKey = Key;
|
|
_tcscpy(g_WspSwitchValue, Value);
|
|
PostMessage(g_hwndFrame, WU_SWITCH_WORKSPACE, 0, 0);
|
|
|
|
if (g_pDbgClient != NULL)
|
|
{
|
|
// Temporarily disable event callbacks to keep
|
|
// activity at a minimum while we're in this halfway state.
|
|
g_pDbgClient->SetEventCallbacks(NULL);
|
|
|
|
while (!g_WspSwitchBufferAvailable)
|
|
{
|
|
if (FAILED(g_pDbgClient->DispatchCallbacks(50)))
|
|
{
|
|
Sleep(50);
|
|
}
|
|
}
|
|
|
|
g_pDbgClient->SetEventCallbacks(&g_EventCb);
|
|
}
|
|
else
|
|
{
|
|
while (!g_WspSwitchBufferAvailable)
|
|
{
|
|
Sleep(100);
|
|
}
|
|
}
|
|
|
|
// We know that at this point the new workspace cannot be dirty
|
|
// so just clear the dirty flags.
|
|
|
|
if (g_Workspace)
|
|
{
|
|
g_Workspace->ClearDirty();
|
|
}
|
|
|
|
// Let the UI thread continue.
|
|
g_WspSwitchKey = WSP_NAME_BASE;
|
|
g_WspSwitchValue[0] = 0;
|
|
Sleep(50);
|
|
|
|
//
|
|
// Warn the user is the workspace was not be created properly.
|
|
//
|
|
|
|
if (!g_Workspace)
|
|
{
|
|
InformationBox(ERR_NULL_Workspace, NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
PSTR g_WspGlobalNames[] =
|
|
{
|
|
"Symbol path", "Image path", "Source path", "Window menu checks",
|
|
"Register customization", "Breakpoints", "Log file settings",
|
|
"Local source path", "Event filter settings", "Fixed-width font",
|
|
"Tab width", "MRU list", "Repeat commands setting", "COM port settings",
|
|
"1394 settings", "Activate source windows in disassembly mode",
|
|
"Show tool bar", "Show status bar", "Automatically scroll command window",
|
|
"Source open dialog path", "Executable command line",
|
|
"Executable create flags", "Dump file name", "Kernel attach flags",
|
|
"Type options", "Dump open dialog path", "Executable open dialog path",
|
|
};
|
|
|
|
PSTR g_WspWindowNames[] =
|
|
{
|
|
"Child window settings", "WinDBG window settings", "WinDBG window title",
|
|
};
|
|
|
|
PSTR
|
|
GetWspTagName(WSP_TAG Tag)
|
|
{
|
|
ULONG Item = WSP_TAG_ITEM(Tag);
|
|
static char Buffer[128];
|
|
|
|
switch(WSP_TAG_GROUP(Tag))
|
|
{
|
|
case WSP_GROUP_GLOBAL:
|
|
if (Item < WSP_GLOBAL_COUNT)
|
|
{
|
|
return g_WspGlobalNames[Item];
|
|
}
|
|
break;
|
|
case WSP_GROUP_WINDOW:
|
|
if (Item < WSP_WINDOW_COUNT)
|
|
{
|
|
return g_WspWindowNames[Item];
|
|
}
|
|
break;
|
|
case WSP_GROUP_COLORS:
|
|
INDEXED_COLOR* IdxCol = GetIndexedColor(Item);
|
|
if (IdxCol != NULL)
|
|
{
|
|
sprintf(Buffer, "%s color", IdxCol->Name);
|
|
return Buffer;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return "Unknown tag";
|
|
}
|