This is a subsequent question to my fist one, asking how to register a custom CodeInsight manager to the IDE with C++Builder.
I switched my code to Remy Lebeau's version with the #pragma startup and #pragma exitdirectives.
What now happens when I try to use it (see my code below) is this:
With CodeGuard set to active:
I hit F9 -> Project compiles well (except for [TLIB Warning] Warning: library was too large for page size, rebuilt with page size 32)
A second instance of bds.exe starts (because i set it as the host app)
In the 2nd instance I try to install my package wich results in the following error:
I deactivate CodeGuard in the project debugging settings and try it again and now it seems to work, as I can see a sequence of MessageBoxes:
TMyCodeInsightSymbolList::TMyCodeInsightSymbolList
TMyCodeInsightManager::TMyCodeInsightManager
TMyCodeInsightManager::GetIDString
TMyCodeInsightManager::GetIDString
TMyCodeInsightManager::GetIDString
TMyCodeInsightManager::GetIDString
TMyCodeInsightManager::GetIDString
Now I create a new VCL Forms Application in the debugging instance and enter
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
this->|
}
in TForm1's constructor.
The resulting MessageBoxes:
TMyCodeInsightManager::HandlesFile
TMyCodeInsightManager::GetEnabled
TMyCodeInsightManager::AllowCodeInsight
TMyCodeInsightManager::EditorTokenValidChars
TMyCodeInsightManager::PreValidateCodeInsight
TMyCodeInsightManager::EditorTokenValidChars
TMyCodeInsightManager::InvokeCodeCompletion
TMyCodeInsightManager::GetSymbolList
TMyCodeInsightSymbolList::GetCount
TMyCodeInsightSymbolList::~TMyCodeInsightSymbolList
TMyCodeInsightManager::GetSymbolList
Error:
It drives me crazy...so what the heck am I doing wrong here?
{...}
Furthermore, in the IDE setting in the 2nd instance at Editor Options -> CodeInsight I see my new entry in the ComboBox Source file type but when I select it, there's an out of range error, so I think the IDE is missing some registry entries for my completion manager or stuff. Am I right?
Now, here's my code:
my_completion.cpp
#pragma hdrstop
#pragma package(smart_init)
#include "my_codeinsight_manager.h"
static int FCodeManagerIndex = -1;
void DoRegister()
{
_di_IOTACodeInsightServices CIS;
if (BorlandIDEServices->Supports(CIS))
FCodeManagerIndex = CIS->AddCodeInsightManager(new TMyCodeInsightManager);
}
#pragma startup DoRegister
void DoUnregister()
{
_di_IOTACodeInsightServices CIS;
if ((FCodeManagerIndex != -1) && BorlandIDEServices->Supports(CIS))
CIS->RemoveCodeInsightManager(FCodeManagerIndex);
}
#pragma exit DoUnregister
#pragma argsused
extern "C" int _libmain(unsigned long reason)
{
return 1;
}
my_codeinsight_manager.h
#ifndef my_codeinsight_managerH
#define my_codeinsight_managerH
//---------------------------------------------------------------------------
#include <memory>
#include <System.SysUtils.hpp>
#include <ToolsAPI.hpp>
#include "my_codeinsight_symbollist.h"
//---------------------------------------------------------------------------
class TMyCodeInsightManager : public TCppInterfacedObject<IOTANotifier, IOTACodeInsightManager>
{
public:
TMyCodeInsightManager();
virtual __fastcall ~TMyCodeInsightManager();
String __fastcall GetName();
String __fastcall GetIDString();
bool __fastcall GetEnabled();
void __fastcall SetEnabled(bool AValue) ;
TSysCharSet __fastcall EditorTokenValidChars(bool APreValidating);
void __fastcall AllowCodeInsight(bool& Allow, const WideChar AKey);
bool __fastcall PreValidateCodeInsight(const String AStr);
bool __fastcall IsViewerBrowsable(int AIndex);
bool __fastcall GetMultiSelect();
void __fastcall GetSymbolList(_di_IOTACodeInsightSymbolList& ASymbolList);
void __fastcall OnEditorKey(WideChar AKey, bool& ACloseViewer, bool& AAccept);
bool __fastcall HandlesFile(const String AFileName);
String __fastcall GetLongestItem();
void __fastcall GetParameterList(_di_IOTACodeInsightParameterList& AParameterList);
void __fastcall GetCodeInsightType(WideChar AChar, int AElement, TOTACodeInsightType& ACodeInsightType, TOTAInvokeType& AInvokeType);
bool __fastcall InvokeCodeCompletion(TOTAInvokeType AHowInvoked, String& AStr);
bool __fastcall InvokeParameterCodeInsight(TOTAInvokeType AHowInvoked, int& ASelectedIndex);
void __fastcall ParameterCodeInsightAnchorPos(TOTAEditPos& AEdPos);
int __fastcall ParameterCodeInsightParamIndex(const TOTAEditPos& AEdPos);
String __fastcall GetHintText(int AHintLine, int AHintCol);
String __fastcall GetOptionSetName();
bool __fastcall GotoDefinition(String& AFileName, int& ALineNum, int AIndex=0xFFFFFFFF);
void __fastcall Done(bool AAccepted, bool& ADisplayParams);
__property String Name = {read=GetName};
__property bool MultiSelect = {read=GetMultiSelect};
__property bool Enabled = {read=GetEnabled, write=SetEnabled};
/* IOTANotifier */
void __fastcall AfterSave() {}
void __fastcall BeforeSave() {}
void __fastcall Destroyed() {}
void __fastcall Modified() {}
private:
std::unique_ptr<TMyCodeInsightSymbolList> FSymbolList;
TSysCharSet FSysCharSet;
};
my_codeinsight_manager.cpp
#include <vcl.h>
#pragma hdrstop
#include "my_codeinsight_manager.h"
// ---------------------------------------------------------------------------
#pragma package(smart_init)
TMyCodeInsightManager::TMyCodeInsightManager()
: FSymbolList(std::unique_ptr<TMyCodeInsightSymbolList>(new TMyCodeInsightSymbolList))
{
FSysCharSet = TSysCharSet() << 'A' << 'B' << 'C' << 'D' << 'E' << 'F' << 'G' << 'H' << 'I' << 'J' << 'K'
<< 'L' << 'M' << 'N' << 'O' << 'P' << 'Q' << 'R' << 'S' << 'T' << 'U' << 'V' << 'W'
<< 'X' << 'Y' << 'Z'
<< 'a' << 'b' << 'c' << 'd' << 'e' << 'f' << 'g' << 'h' << 'i' << 'j' << 'k' << 'l'
<< 'm' << 'n' << 'o' << 'p' << 'q' << 'r' << 's' << 't' << 'u' << 'v' << 'w' << 'x'
<< 'y' << 'z'
<< '0' << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9'
<< '+' << '-' << '*' << '/' << '^' << '\\' << '(' << ')' << '[' << ']' << '{' << '}'
<< '=' << '!' << '<' << '>' << '\'' << '\"' << '$' << ',' << ';' << ':' << '5' << '!'
<< '&' << '?' << '_' << '#' << '#'
<< 0x08 << 0x09 << 0x0B << 0x0C << 0x0D;
ShowMessage(L"TMyCodeInsightManager::TMyCodeInsightManager");
}
//---------------------------------------------------------------------------
__fastcall TMyCodeInsightManager::~TMyCodeInsightManager()
{
ShowMessage(L"TMyCodeInsightManager::~TMyCodeInsightManager");
}
//---------------------------------------------------------------------------
String __fastcall TMyCodeInsightManager::GetName()
{
ShowMessage(L"TMyCodeInsightManager::GetName");
return L"MyCodeInsightLanguage";
}
//----------------------------------------------------------------------------
String __fastcall TMyCodeInsightManager::GetIDString()
{
ShowMessage(L"TMyCodeInsightManager::GetIDString");
return L"My.CodeInsight";
}
//----------------------------------------------------------------------------
bool __fastcall TMyCodeInsightManager::GetEnabled()
{
ShowMessage(L"TMyCodeInsightManager::GetEnabled");
return true;
}
//----------------------------------------------------------------------------
void __fastcall TMyCodeInsightManager::SetEnabled(bool AValue)
{
ShowMessage(L"TMyCodeInsightManager::SetEnabled");
}
//----------------------------------------------------------------------------
TSysCharSet __fastcall TMyCodeInsightManager::EditorTokenValidChars(bool APreValidating)
{
ShowMessage(L"TMyCodeInsightManager::EditorTokenValidChars");
return FSysCharSet;
}
//----------------------------------------------------------------------------
void __fastcall TMyCodeInsightManager::AllowCodeInsight(bool& AAllow, const WideChar AKey)
{
ShowMessage(L"TMyCodeInsightManager::AllowCodeInsight");
AAllow = true;
}
//----------------------------------------------------------------------------
bool __fastcall TMyCodeInsightManager::PreValidateCodeInsight(const String AStr)
{
ShowMessage(L"TMyCodeInsightManager::PreValidateCodeInsight");
return true;
}
//----------------------------------------------------------------------------
bool __fastcall TMyCodeInsightManager::IsViewerBrowsable(int AIndex)
{
ShowMessage(L"TMyCodeInsightManager::IsViewerBrowsable");
return true; // # TO DO #
}
//----------------------------------------------------------------------------
bool __fastcall TMyCodeInsightManager::GetMultiSelect()
{
ShowMessage(L"TMyCodeInsightManager::GetMultiSelect");
return false;
}
//----------------------------------------------------------------------------
void __fastcall TMyCodeInsightManager::GetSymbolList(_di_IOTACodeInsightSymbolList& ASymbolList)
{
ASymbolList = FSymbolList.get();
ShowMessage(L"TMyCodeInsightManager::GetSymbolList");
}
//----------------------------------------------------------------------------
void __fastcall TMyCodeInsightManager::OnEditorKey(WideChar AKey, bool& ACloseViewer, bool& AAccept)
{
ACloseViewer = true;
AAccept = false;
ShowMessage(L"TMyCodeInsightManager::OnEditorKey");
}
//----------------------------------------------------------------------------
bool __fastcall TMyCodeInsightManager::HandlesFile(const String AFileName)
{
ShowMessage(L"TMyCodeInsightManager::HandlesFile");
String FileExt = ExtractFileExt(AFileName);
if ( (FileExt == L".c")
|| (FileExt == L".cpp")
|| (FileExt == L".h")
|| (FileExt == L".hpp")
|| (FileExt == L".cc")
|| (FileExt == L".cp")
|| (FileExt == L".cxx") )
{
return true;
}
else
{
return false;
}
}
//----------------------------------------------------------------------------
String __fastcall TMyCodeInsightManager::GetLongestItem()
{
ShowMessage(L"TMyCodeInsightManager::GetLongestItem");
return String(L"dddddddddddddddddddddd");
}
//----------------------------------------------------------------------------
void __fastcall TMyCodeInsightManager::GetParameterList(_di_IOTACodeInsightParameterList& AParameterList)
{
AParameterList = NULL;
ShowMessage(L"TMyCodeInsightManager::GetParameterList");
}
//----------------------------------------------------------------------------
void __fastcall TMyCodeInsightManager::GetCodeInsightType(WideChar AChar, int AElement, TOTACodeInsightType& ACodeInsightType, TOTAInvokeType& AInvokeType)
{
AInvokeType = itManual;
if ( (AElement != atString) && (AChar != 0x0001) && (AElement == atComment) )
{
switch (AChar)
{
case 0x0000:
ACodeInsightType = citCodeInsight;
break;
case 0x0001:
ACodeInsightType = citParameterCodeInsight;
break;
case 0x0002:
ACodeInsightType = citBrowseCodeInsight;
break;
case 0x0003:
ACodeInsightType = citHintCodeInsight;
break;
case L'.':
ACodeInsightType = citCodeInsight;
AInvokeType = itTimer;
break;
case L'(':
ACodeInsightType = citParameterCodeInsight;
AInvokeType = itTimer;
break;
default:
ACodeInsightType = citNone;
}
}
ShowMessage(L"TMyCodeInsightManager::GetCodeInsightType");
}
//----------------------------------------------------------------------------
bool __fastcall TMyCodeInsightManager::InvokeCodeCompletion(TOTAInvokeType AHowInvoked, String& AStr)
{
switch (AHowInvoked)
{
case itAuto:
break;
case itManual:
break;
case itTimer:
break;
}
AStr = L"IAmASymbolText";
return true;
}
//----------------------------------------------------------------------------
bool __fastcall TMyCodeInsightManager::InvokeParameterCodeInsight(TOTAInvokeType AHowInvoked, int& ASelectedIndex)
{
switch (AHowInvoked)
{
case itAuto:
break;
case itManual:
break;
case itTimer:
break;
}
ShowMessage(L"TMyCodeInsightManager::OnvokeParameterCodeInsight");
return true; // # TO DO #
}
//----------------------------------------------------------------------------
void __fastcall TMyCodeInsightManager::ParameterCodeInsightAnchorPos(TOTAEditPos& AEdPos)
{
ShowMessage(L"TMyCodeInsightManager::ParameterCodeInsightAnchorPos");
}
//----------------------------------------------------------------------------
int __fastcall TMyCodeInsightManager::ParameterCodeInsightParamIndex(const TOTAEditPos& AEdPos)
{
ShowMessage(L"TMyCodeInsightManager::ParameterCodeInsightParamIndex");
return 0;
}
//----------------------------------------------------------------------------
String __fastcall TMyCodeInsightManager::GetHintText(int AHintLine, int AHintCol)
{
ShowMessage(L"TMyCodeInsightManager::GetHintText");
return L"NoHint";
}
//----------------------------------------------------------------------------
String __fastcall TMyCodeInsightManager::GetOptionSetName()
{
ShowMessage(L"TMyCodeInsightManager::GetOptionSetName");
return L"MyCodeInsight";
}
//---------------------------------------------------------------------------
bool __fastcall TMyCodeInsightManager::GotoDefinition(String &AFileName, int& ALineNum, int AIndex)
{
ShowMessage(L"TMyCodeInsightManager::GotoDefinition");
return true;
}
//----------------------------------------------------------------------------
void __fastcall TMyCodeInsightManager::Done(bool AAccepted, bool& ADisplayParams)
{
ShowMessage(L"TMyCodeInsightManager::Done");
}
//----------------------------------------------------------------------------
my_codeinsight_symbollist.h
#ifndef my_codeinsight_symbollistH
#define my_codeinsight_symbollistH
//---------------------------------------------------------------------------
#include <vector>
#include <algorithm>
#include <System.SysUtils.hpp>
#include <ToolsAPI.hpp>
//---------------------------------------------------------------------------
class TMySymbolList
{
public:
TMySymbolList(const String AText=L"", const String AType=L"", const String AClass=L"", const bool AIsReadWrite=false, const bool AIsAbstract=false, const TOTAViewerSymbolFlags AFlag=vsfUnknown, const TOTAViewerVisibilityFlags AVisibilityFlag=vvfPrivate, TOTAProcDispatchFlags AProcDispatchFlag=pdfNone)
: FText(AText),
FType(AType),
FClass(AClass),
FFlag(AFlag),
FVisibilityFlag(AVisibilityFlag),
FProcDispatchFlag(AProcDispatchFlag)
{}
__property String Text = {read=FText, write=FText}; // i.e. 'Form1'
__property String Type = {read=FType, write=FType}; // i.e. 'TForm1'
__property String Class = {read=FClass, write=FClass}; // i.e. 'var', 'function', 'type', etc.
__property bool IsReadWrite = {read=FIsReadWrite, write=FIsReadWrite};
__property bool IsAbstract = {read=FIsAbstract, write=FIsAbstract};
__property TOTAViewerSymbolFlags Flag = {read=FFlag, write=FFlag};
__property TOTAViewerVisibilityFlags VisibilityFlag = {read=FVisibilityFlag, write=FVisibilityFlag};
__property TOTAProcDispatchFlags ProcDispatchFlag = {read=FProcDispatchFlag, write=FProcDispatchFlag};
private:
String FText;
String FType;
String FClass;
bool FIsReadWrite;
bool FIsAbstract;
TOTAViewerSymbolFlags FFlag;
TOTAViewerVisibilityFlags FVisibilityFlag;
TOTAProcDispatchFlags FProcDispatchFlag;
};
//---------------------------------------------------------------------------
class TMyCodeInsightSymbolList : public TCppInterfacedObject<IOTACodeInsightSymbolList>
{
public:
TMyCodeInsightSymbolList();
virtual __fastcall ~TMyCodeInsightSymbolList();
void __fastcall Clear(void);
int __fastcall GetCount(void);
bool __fastcall GetSymbolIsReadWrite(int AIndex);
bool __fastcall GetSymbolIsAbstract(int AIndex);
TOTAViewerSymbolFlags __fastcall GetViewerSymbolFlags(int AIndex);
int __fastcall GetViewerVisibilityFlags(int AIndex);
TOTAProcDispatchFlags __fastcall GetProcDispatchFlags(int AIndex);
void __fastcall SetSortOrder(const TOTASortOrder AValue);
TOTASortOrder __fastcall GetSortOrder(void);
int __fastcall FindIdent(const String AIdent);
bool __fastcall FindSymIndex(const String AIdent, int& AIndex);
void __fastcall SetFilter(const String AFilterText);
String __fastcall GetSymbolText(int AIndex);
String __fastcall GetSymbolTypeText(int AIndex);
String __fastcall GetSymbolClassText(int AIndex);
__property String SymbolClassText[int Index] = {read=GetSymbolClassText};
__property String SymbolTypeText[int Index] = {read=GetSymbolTypeText};
__property String SymbolText[int Index] = {read=GetSymbolText};
__property TOTAViewerSymbolFlags SymbolFlags[int i] = {read=GetViewerSymbolFlags};
__property int SymbolVisibility[int Index] = {read=GetViewerVisibilityFlags};
__property bool SymbolIsAbstract[int Index] = {read=GetSymbolIsAbstract};
__property bool SymbolIsReadWrite[int Index] = {read=GetSymbolIsReadWrite};
__property TOTAProcDispatchFlags FuncDispatchFlags[int Index] = {read=GetProcDispatchFlags};
__property TOTASortOrder SortOrder = {read=GetSortOrder, write=SetSortOrder};
__property int Count = {read=GetCount};
private:
static bool SymbolTextCompare(const TMySymbolList& AX, const TMySymbolList& AY);
std::vector<TMySymbolList> FSymbols;
TOTASortOrder FSortOrder;
};
//---------------------------------------------------------------------------
#endif
my_codeinsight_symbollist.cpp
#include <vcl.h>
#pragma hdrstop
#include "my_codeinsight_symbollist.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
// Constructor
TMyCodeInsightSymbolList::TMyCodeInsightSymbolList()
: FSortOrder(soAlpha)
{
TMySymbol S;
S.Text = L"IAmASymbolText";
S.Type = L"IAmASymbolType";
S.Class = L"IAmASymbolClass";
S.Flag = vsfFunction;
S.VisibilityFlag = vvfPublic;
FSymbols.push_back(S);
FSymbols.push_back(S);
FSymbols.push_back(S);
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::TMyCodeInsightSymbolList");
#endif
}
//---------------------------------------------------------------------------
// Destructor
__fastcall TMyCodeInsightSymbolList::~TMyCodeInsightSymbolList()
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::~TMyCodeInsightSymbolList");
#endif
}
//---------------------------------------------------------------------------
// Implementor should clear its symbol list
void __fastcall TMyCodeInsightSymbolList::Clear(void)
{
FSymbols.clear();
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::Clear");
#endif
}
//----------------------------------------------------------------------------
// Returns the count of the symbols in the list - may be modified by setting a filter
int __fastcall TMyCodeInsightSymbolList::GetCount(void)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetCount");
#endif
return static_cast<int>(FSymbols.size());
}
//----------------------------------------------------------------------------
// Returns whether the symbol is able to be read from and written to
bool __fastcall TMyCodeInsightSymbolList::GetSymbolIsReadWrite(int AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetSymbolIsReadWrite");
#endif
if (AIndex < static_cast<int>(FSymbols.size()))
return FSymbols[AIndex].IsReadWrite;
else
return false;
}
//----------------------------------------------------------------------------
// Returns whether the symbols is abstract. Viewer draws these in the 'need to implement' color
bool __fastcall TMyCodeInsightSymbolList::GetSymbolIsAbstract(int AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetSymbolIsAbstract");
#endif
if (AIndex < static_cast<int>(FSymbols.size()))
return FSymbols[AIndex].IsAbstract;
else
return false;
}
//----------------------------------------------------------------------------
// Return the symbol flags for the item at index 'AIndex'. AIndex is the index in the filtered list
TOTAViewerSymbolFlags __fastcall TMyCodeInsightSymbolList::GetViewerSymbolFlags(int AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetViewerSymbolFlags");
#endif
if (AIndex < static_cast<int>(FSymbols.size()))
return FSymbols[AIndex].Flag;
else
return vsfUnknown;
}
//----------------------------------------------------------------------------
// Return the visibility flags for the item at index 'AIndex'. AIndex is the index in the filtered list
int __fastcall TMyCodeInsightSymbolList::GetViewerVisibilityFlags(int AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetViewerVisibilityFlags");
#endif
if (AIndex < static_cast<int>(FSymbols.size()))
return FSymbols[AIndex].VisibilityFlag;
else
return vvfPrivate; // # TO DO #
}
//----------------------------------------------------------------------------
// Return the procedure flags for the item at index 'AIndex'. AIndex is the index in the filtered list
TOTAProcDispatchFlags __fastcall TMyCodeInsightSymbolList::GetProcDispatchFlags(int AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetProcDispatchFlags");
#endif
if (AIndex < static_cast<int>(FSymbols.size()))
return FSymbols[AIndex].ProcDispatchFlag;
else
return pdfNone;
}
//----------------------------------------------------------------------------
// The list was requested to be sorted by 'AValue'
void __fastcall TMyCodeInsightSymbolList::SetSortOrder(const TOTASortOrder AValue)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::SetSortOrder");
#endif
FSortOrder = AValue;
// Sort order is alphabetically
if (FSortOrder == soAlpha)
{
// Sort Symbol vector alphabetically
std::sort(FSymbols.begin(), FSymbols.end(), SymbolTextCompare);
}
// Sort order is by scope
else if (FSortOrder == soScope)
{
// Sort Symbol vector by scope
// # TO DO #
}
}
//----------------------------------------------------------------------------
// Returns the sort order of the list
TOTASortOrder __fastcall TMyCodeInsightSymbolList::GetSortOrder(void)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetSortOrder");
#endif
return FSortOrder;
}
//----------------------------------------------------------------------------
// Given an identifier, return the index of the closest partial match
int __fastcall TMyCodeInsightSymbolList::FindIdent(const String AIdent)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::FindIdent");
#endif
return 0; // # TO DO # We're not searching at the moment
}
//----------------------------------------------------------------------------
// Given an identifier, find the 'Index' of an exact match in the list and return true. Otherwise return false
bool __fastcall TMyCodeInsightSymbolList::FindSymIndex(const String AIdent, int& AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::FindSymIndex");
#endif
return false; // # TO DO # We aren't matching at the moment
}
//----------------------------------------------------------------------------
// Set the lists filter to 'FilterText'. It is up to the implementor to determine how to filter or if they even want to filter
void __fastcall TMyCodeInsightSymbolList::SetFilter(const String AFilterText)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::SetFilter");
#endif
// # TO DO # : We don't filter at the moment
}
//----------------------------------------------------------------------------
// Return the symbol text for item 'AIndex'. i.e. Form1
String __fastcall TMyCodeInsightSymbolList::GetSymbolText(int AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetSymbolText");
#endif
if (AIndex < static_cast<int>(FSymbols.size()))
return FSymbols[AIndex].Text;
else
return L"";
}
//----------------------------------------------------------------------------
// Return the symbol type text for item 'AIndex'. i.e. TForm1
String __fastcall TMyCodeInsightSymbolList::GetSymbolTypeText(int AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetSymbolTypeText");
#endif
if (AIndex < static_cast<int>(FSymbols.size()))
return FSymbols[AIndex].Type;
else
return L"";
}
//----------------------------------------------------------------------------
// Return the symbol class text for item 'AIndex'. i.e. 'var', 'function', 'type', etc
String __fastcall TMyCodeInsightSymbolList::GetSymbolClassText(int AIndex)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::GetSymbolClassText");
#endif
if (AIndex < static_cast<int>(FSymbols.size()))
return FSymbols[AIndex].Class;
else
return L"";
}
//----------------------------------------------------------------------------
// Returns true, if AX.Text's position is alphabetically before AY.Text's
bool TMyCodeInsightSymbolList::SymbolTextCompare(const TMySymbol& AX, const TMySymbol& AY)
{
#ifdef _DEBUG
ShowMessage(L"TMyCodeInsightSymbolList::SymbolTextCompare");
#endif
return (AnsiCompareStr(AX.Text, AY.Text) == -1);
}
//---------------------------------------------------------------------------
Related
I have an object in function z which I'm accessing from functions x and y. The problem is that it's initialized through different functions in the afxmem.cpp (VS 11.0 file).
Here's my code;
classA
{
public:
ADesign *a_d;
};
void classA::functionZ()
{
a_d = new ADesign;
}
//functionX and functionY both calls same function
void classB::functionX()
{
ca.functionZ();
}
void classB::functionY()
{
ca.functionZ();
}
//sample initializer for ADesign
ADesign::ADesign()
:doc_(0)
{
version_number = 7.82f;
d_file = "";
c_file = "";
angle = ID_ANGLE_MER;
machine_type = MACHINE_COMPRESS;
//...etc
}
When it's being initialized, it goes through these functions in afxmem.cpp for function x
void* PASCAL
CObject::operator new(size_t nSize, LPCSTR lpszFileName, int nLine)
{
return ::operator new(nSize, _AFX_CLIENT_BLOCK, lpszFileName, nLine);
}
void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine)
{
#ifdef _AFX_NO_DEBUG_CRT
UNUSED_ALWAYS(nType);
UNUSED_ALWAYS(lpszFileName);
UNUSED_ALWAYS(nLine);
return ::operator new(nSize);
#else
void* pResult;
#ifdef _AFXDLL
_PNH pfnNewHandler = _pfnUninitialized;
#endif
for (;;)
{
pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine);
if (pResult != NULL)
return pResult;
#ifdef _AFXDLL
if (pfnNewHandler == _pfnUninitialized)
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
pfnNewHandler = pState->m_pfnNewHandler;
}
if (pfnNewHandler == NULL || (*pfnNewHandler)(nSize) == 0)
break;
#else
if (_afxNewHandler == NULL || (*_afxNewHandler)(nSize) == 0)
break;
#endif
}
return pResult;
#endif
}
And this function in afxmem.cpp for function y;
void* __cdecl operator new(size_t nSize)
{
void* pResult;
#ifdef _AFXDLL
_PNH pfnNewHandler = _pfnUninitialized;
#endif
for (;;)
{
#if !defined(_AFX_NO_DEBUG_CRT) && defined(_DEBUG)
pResult = _malloc_dbg(nSize, _NORMAL_BLOCK, NULL, 0);
#else
pResult = malloc(nSize);
#endif
if (pResult != NULL)
return pResult;
#ifdef _AFXDLL
if (pfnNewHandler == _pfnUninitialized)
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
pfnNewHandler = pState->m_pfnNewHandler;
}
if (pfnNewHandler == NULL || (*pfnNewHandler)(nSize) == 0)
break;
#else
if (_afxNewHandler == NULL || (*_afxNewHandler)(nSize) == 0)
break;
#endif
}
return pResult;
}
When it goes through function y, it doesn't get initialized properly. I'm accessing the program through the command line for function y.
Is there a reason as to why the initialization is happening in 2 different ways for the same object?
Thanks.
At the beginning of your .cpp files you may or may not have this line:
#define new DEBUG_NEW
If this line is present, then new will end up invoking this function:
void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine)
if it is not present then new will end up invoking this function:
void* __cdecl operator new(size_t nSize)
That's internal MFC debugging stuff.
But that doesn't explain why something doesn't get initialized properly. You need to elaborate your question, it's not clear enough.
In the following code, I have a simple server that sends a message to all clients 2 times a second, and another message 8-10 times a minute.
The problem is I am getting an error at runtime:
access violation at 00479740 read address FFFFFFD0
But only in a few systems, and only 1 or 2 times a day. This software works about 10 hours a day.
I have tried to use similar code with the ICS library and is seems to work well.
What's wrong in this code? Is there a better way to code it?
void __fastcall TDataNet::DataModuleCreate(TObject *Sender)
{
listaClient= new TThreadList();
psTx= new TStringList();
psRx= new TStringList();
}
void __fastcall TDataNet::DataModuleDestroy(TObject *Sender)
{
IdTCPServer1->Active= false;
listaClient->Free();
delete psTx;
delete psRx;
}
void __fastcall TDataNet::Send( TStrings *ps, TIdContext *AContext)
{
TList *lista;
static int cntSend= 0;
try
{
lista= listaClient->LockList();
if( AContext != NULL )
{
AContext->Connection->IOHandler->Write( ps, true, TIdTextEncoding_UTF8);
}
else
{
for( int i=0; i < lista->Count; i++ )
((TDatiClient*)lista->Items[i])->pThread->Connection->IOHandler->Write( ps, true, TIdTextEncoding_UTF8);
}
}
__finally
{
listaClient->UnlockList();
}
}
void __fastcall TDataNet::SetCambioPilota( void)
{
unsigned short hh, mm, ss, ms, hh1, mm1, ss1, ms1;
unsigned short hh2, mm2, ss2, ms2, hh3, mm3, ss3, ms3;
unsigned short hh4, mm4, ss4, ms4, dd4;
unsigned short hh5, mm5, ss5, ms5, dd5;
TStrings *ps;
UnicodeString s;
try
{
ps= psTx;
ps->Clear();
s= "<CAMBIO_PILOTA>";
ps->Add( s);
for( int i=0; i < MAX_PILOTI; i++ )
{
s.sprintf( L"<Pilota%02x= I%x,\"A%s\",\"C%s\",\"F%s\",f%x>",
i+1, gara.pilota[i].idnome,
gara.pilota[i].nome.c_str(), gara.pilota[i].nick.c_str(),
gara.pilota[i].nomeTeam.c_str(), gara.pilota[i].idPilotaT );
ps->Add( s);
}
s= "<END_CAMBIO_PILOTA>";
ps->Add( s);
Send( ps );
}
catch(...){}
}
void __fastcall TDataNet::SetDatiGara( void)
{
TStrings *ps;
UnicodeString s;
try
{
ps= psTx;
ps->Clear();
s= "<DATI_GARA>";
ps->Add( s);
s.sprintf( L"<eve=%d,A%x,B%x,C%x,D%x,E%x,F%x,G%x,H%x,I%x,J%x,K%x>", DataB->GetEventoInCorso().idEvento,
DataB->GetEventoInCorso().numEvento, DataB->GetEventoInCorso().subEvento,
DataB->GetNextEvento().idEvento, DataB->GetNextEvento().numEvento, DataB->GetNextEvento().subEvento,
gara.tkTempo, gara.tkDurata - gara.tkTempo,
gara.laps, gara.gDurata > 0 ? (gara.gDurata - gara.laps):0, gara.flInCorso ? (gara.gDurata > 0 ? 2:1):0,
gara.flFineGara );
ps->Add( s);
s= "<END_DATI_GARA>";
ps->Add( s);
Send( ps );
}
catch(...){}
}
void __fastcall TDataNet::Timer1Timer(TObject *Sender)
{
Timer1->Enabled= false;
SetDatiGara();
Timer1->Enabled= true;
}
void __fastcall TDataNet::IdTCPServer1Connect(TIdContext *AContext)
{
TDatiClient* dati;
dati= new TDatiClient;
dati->pThread= AContext;
AContext->Connection->IOHandler->ReadTimeout= 200;
AContext->Data= (TObject*)dati;
try
{
TList* lista;
lista= listaClient->LockList();
lista->Add( dati);
connessioni= lista->Count;
if( FmainWnd )
PostMessage( FmainWnd, WM_EVENTO_TCP, ID_CONNESSO, lista->Count);
int idEvento= DataB->GetEventoInCorso().idEvento;
if( idEvento )
SetCambioStato( idEvento, STATO_EVENTO_START, AContext);
}
__finally
{
listaClient->UnlockList();
}
}
void __fastcall TDataNet::IdTCPServer1Disconnect(TIdContext *AContext)
{
TDatiClient* dati;
dati= (TDatiClient*)AContext->Data;
AContext->Data= NULL;
try
{
listaClient->Remove( dati);
TList* lista;
lista= listaClient->LockList();
connessioni= lista->Count;
if( FmainWnd )
PostMessage( FmainWnd, WM_EVENTO_TCP, ID_DISCONNESSO, lista->Count);
}
__finally
{
listaClient->UnlockList();
}
delete dati;
}
void __fastcall TDataNet::IdTCPServer1Execute(TIdContext *AContext)
{
Sleep( 100);
try
{
AContext->Connection->IOHandler->ReadStrings( psRx, -1);
if( psRx->Count >= 2 && psRx->Strings[0] == "<LAST_MINUTE>" && psRx->Strings[psRx->Count-1] == "<END_LAST_MINUTE>" )
{
psRx->Delete(0);
psRx->Delete(psRx->Count-1);
if( FmainWnd )
SendMessage( FmainWnd, WM_EVENTO_TCP, ID_LAST_MINUTE, (unsigned int)psRx);
}
psRx->Clear();
}
catch( ...) {}
AContext->Connection->CheckForGracefulDisconnect();
}
The error message means you are accessing something that is -48 bytes offset from a NULL pointer. I see all kinds of problems with this code, the least of which is that you are accessing things in a thread-unsafe manner, so you have race conditions that could corrupt memory, amongst other possible issues. For example, your OnExecute event handler is not protecting the psRx object from concurrent access, so multiple clients could populate it with data at the exact same time, corrupting its contents.
TIdTCPServer is a multi-threaded component, its events are fired in the context of worker threads, not the main UI thread, so your event handlers MUST use thread-safe coding.
Besides, what you are doing is not the safest way to handle asynchronous communications with TIdTCPServer anyway. I would suggest something more like the following instead:
class TDatiClient : public TIdServerContext
{
public:
TIdThreadSafeObjectList *Queue;
bool QueueHasObjects;
__fastcall TDatiClient(TIdTCPConnection *AConnection, TIdYarn *AYarn, TThreadList* AList = NULL)
: TIdServerContext(AConnection, AYarn, AList)
{
Queue = new TIdThreadSafeObjectList;
}
__fastcall ~TDatiClient()
{
delete Queue;
}
void __fastcall Send(TStrings *ps)
{
TStringList *toSend = new TStringList;
try
{
toSend->Assign(ps);
TList *lista = Queue->LockList();
try
{
lista->Add(toSend);
QueueHasObjects = true;
}
__finally
{
Queue->UnlockList();
}
}
catch (const Exception &)
{
delete toSend;
}
}
};
void __fastcall TDataNet::TDataNet(TComponent *Owner)
: TDataModule(Owner)
{
// this must be set before you activate the server...
IdTCPServer1->ContextClass = __classid(TDatiClient);
// do this at runtime instead of design-time so
// ContextClass can be set first...
IdTCPServer1->Active = true;
}
void __fastcall TDataNet::~TDataNet()
{
IdTCPServer1->Active = false;
}
void __fastcall TDataNet::Send(TStrings *ps, TIdContext *AContext)
{
static int cntSend = 0;
TList *lista = IdTCPServer1->Contexts->LockList();
try
{
if (AContext)
{
// make sure the client is still in the list...
if (lista->IndexOf(AContext) != -1)
static_cast<TDatiClient*>(AContext)->Send(ps);
}
else
{
for (int i = 0; i < lista->Count; ++i)
static_cast<TDatiClient*>(static_cast<TIdContext*>(lista->Items[i]))->Send(ps);
}
}
__finally
{
IdTCPServer1->Contexts->UnlockList();
}
}
void __fastcall TDataNet::SetCambioPilota()
{
UnicodeString s;
try
{
TStringList *ps = new TStringList;
try
{
s = _D("<CAMBIO_PILOTA>");
ps->Add(s);
for (int i = 0; i < MAX_PILOTI; ++i)
{
s.sprintf( _D("<Pilota%02x= I%x,\"A%s\",\"C%s\",\"F%s\",f%x>),
// TODO: if SetCambioPilota() is ever called in a worker thread,
// make sure these values are accessed in a thread-safe manner!
i+1, gara.pilota[i].idnome,
gara.pilota[i].nome.c_str(), gara.pilota[i].nick.c_str(),
gara.pilota[i].nomeTeam.c_str(), gara.pilota[i].idPilotaT);
ps->Add(s);
}
s = _D("<END_CAMBIO_PILOTA>");
ps->Add(s);
Send(ps);
}
__finally
{
delete ps;
}
}
catch (const Exception &)
{
}
}
void __fastcall TDataNet::SetDatiGara()
{
UnicodeString s;
try
{
TStringList *ps = new TStringList;
try
{
s = _D("<DATI_GARA>");
ps->Add(s);
s.sprintf( _D("<eve=%d,A%x,B%x,C%x,D%x,E%x,F%x,G%x,H%x,I%x,J%x,K%x>"),
// TODO: if SetDatiGara() is ever called in a worker thread,
// make sure these values are accessed in a thread-safe manner!
DataB->GetEventoInCorso().idEvento,
DataB->GetEventoInCorso().numEvento, DataB->GetEventoInCorso().subEvento,
DataB->GetNextEvento().idEvento, DataB->GetNextEvento().numEvento, DataB->GetNextEvento().subEvento,
gara.tkTempo, gara.tkDurata - gara.tkTempo, gara.laps,
(gara.gDurata > 0) ? (gara.gDurata - gara.laps) : 0,
gara.flInCorso ? ((gara.gDurata > 0) ? 2 : 1) : 0,
gara.flFineGara);
ps->Add(s);
s = _D("<END_DATI_GARA>");
ps->Add(s);
Send(ps);
}
__finally
{
delete ps;
}
}
catch (const Exception &)
{
}
}
void __fastcall TDataNet::Timer1Timer(TObject *Sender)
{
Timer1->Enabled = false;
SetDatiGara();
Timer1->Enabled = true;
}
void __fastcall TDataNet::IdTCPServer1Connect(TIdContext *AContext)
{
TDatiClient* dati = static_cast<TDatiClient*>(AContext);
AContext->Connection->IOHandler->DefStringEncoding = TIdTextEncoding_UTF8;
TList* lista = IdTCPServer1->Contexts->LockList();
try
{
// TODO: this event is fired in a worker thread, so make sure
// that connessioni, DataB, and SetCambioStato() are all being
// accessed in a thread-safe manner!
int connessioni = lista->Count;
if (FmainWnd)
PostMessage(FmainWnd, WM_EVENTO_TCP, ID_CONNESSO, connessioni);
int idEvento = DataB->GetEventoInCorso().idEvento;
if (idEvento)
SetCambioStato(idEvento, STATO_EVENTO_START, AContext);
}
__finally
{
IdTCPServer1->Contexts->UnlockList();
}
}
void __fastcall TDataNet::IdTCPServer1Disconnect(TIdContext *AContext)
{
TDatiClient* dati = static_cast<TDatiClient*>(AContext);
TList* lista = IdTCPServer1->Contexts->LockList();
try
{
int connessioni = lista->Count - 1;
if (FmainWnd)
PostMessage(FmainWnd, WM_EVENTO_TCP, ID_DISCONNESSO, connessioni);
}
__finally
{
IdTCPServer1->Contexts->UnlockList();
}
}
void __fastcall TDataNet::IdTCPServer1Execute(TIdContext *AContext)
{
TDatiClient* dati = static_cast<TDatiClient*>(AContext);
TStringList *ps;
if (dati->QueueHasObjects)
{
TObjectList *objs = new TObjectList(false);
try
{
TList *lista = dati->Queue->LockList();
try
{
objs->Assign(lista);
lista->Clear();
objs->OwnsObjects = true;
}
__finally
{
dati->QueueHasObjects = (lista->Count > 0);
dati->Queue->UnlockList();
}
for (int i = 0; i < objs->Count; ++i)
{
ps = static_cast<TStringList*>(objs->Items[i]);
AContext->Connection->IOHandler->Write(ps, true);
}
}
__finally
{
delete objs;
}
}
if (AContext->Connection->IOHandler->InputBufferIsEmpty())
{
AContext->Connection->IOHandler->CheckForDataOnSource(200);
if (AContext->Connection->IOHandler->InputBufferIsEmpty())
{
AContext->Connection->IOHandler->CheckForDisconnect();
return;
}
}
ps = new TStringList;
try
{
AContext->Connection->IOHandler->ReadStrings(ps, -1);
if ((ps->Count >= 2) && (ps->Strings[0] == _D("<LAST_MINUTE>")) && (ps->Strings[ps->Count-1] == _D("<END_LAST_MINUTE>")))
{
ps->Delete(0);
ps->Delete(ps->Count-1);
if (FmainWnd)
SendMessage(FmainWnd, WM_EVENTO_TCP, ID_LAST_MINUTE, reinterpret_cast<LPARAM>(ps));
}
}
__finally
{
delete ps;
}
}
While coding a simple game engine framework, I encountered a strange error using std::wcout to log messages during shutdown and destruction of objects. The output is truncated and the app does not terminate, but yet throws no exceptions. I suspect some object is being destroyed before the message is completely displayed.
Here is the output when the app goes through a "normal" shutdown sequence.
Here is WinMain -
using namespace sge;
extern BaseApp *SGEApp;
LRESULT CALLBACK NullWinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, message, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpszCmdLine, int nCmdShow)
{
#if defined(_DEBUG) | !defined(NDEBUG)
SGE_LEAK_CHECK;
SGE_LEAK_CHECK_BREAK(0);
#endif
/////////////////////
// TODO: move this block of code to a console object
if (!GetConsoleWindow()) {
AllocConsole();
FILE *pCout;
FILE *pCerr;
FILE *pCin;
freopen_s(&pCout, "CONOUT$", "w", stdout);
freopen_s(&pCerr, "CONOUT$", "w", stderr);
freopen_s(&pCin, "CONIN$", "r", stdin);
std::wcout.clear();
std::wcerr.clear();
std::wcin.clear();
SetConsoleTitle(L"Framework Console");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
}
SetThreadAffinityMask(GetCurrentThread(), 1);
bool bRun(true);
MSG msg;
if (!SGEApp->initialize())
return -1;
while (bRun) {
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if (GetAsyncKeyState(VK_PAUSE) || msg.message == WM_QUIT)
bRun = false;
SGEApp->update();
SGEApp->draw();
}
delete SGEApp;
FreeConsole();
return int(0);
}
Application objects -
#pragma once
#ifndef _APP_H
#define _APP_H
#ifndef SGE_BASEAPP_H
#include "Application/OpenglApp.h"
#endif
using namespace sge;
class app : public OpenglApp
{
public:
app();
~app();
};
#include "App.h"
BaseApp *SGEApp = new app();
app::app()
{
}
app::~app()
{
}
#pragma once
#ifndef SGE_BASEAPP_H
#define SGE_BASEAPP_H
#ifndef SGE_LOGGER_H
#include "Core/Logger/Logger.h"
#endif
namespace sge
{
class BaseApp
{
public:
BaseApp();
virtual ~BaseApp();
virtual bool initialize();
virtual bool shutDown();
virtual void update();
virtual void draw();
virtual Logger *getLogger() const { return app_logger; }
protected:
Logger *app_logger;
};
} // namespace sge
#endif // !SGE_BASEAPP_H
#include "BaseApp.h"
namespace sge
{
BaseApp::BaseApp()
{
}
BaseApp::~BaseApp()
{
}
bool BaseApp::initialize()
{
return bool(false);
}
bool BaseApp::shutDown()
{
return bool(false);
}
void BaseApp::update()
{
}
void BaseApp::draw()
{
}
} // namespace sge
#pragma once
#ifndef SGE_OPENGLAPP_H
#define SGE_OPENGLAPP_H
#ifndef SGE_BASEAPP_H
#include "BaseApp.h"
#endif
namespace sge
{
class OpenglApp : public BaseApp
{
public:
OpenglApp();
virtual ~OpenglApp();
virtual bool initialize();
virtual bool shutDown();
virtual void update();
virtual void draw();
};
} // namespace sge
#endif // SGE_OPENGLAPP_H
#include "Application/OpenglApp.h"
namespace sge
{
OpenglApp::OpenglApp()
{
}
OpenglApp::~OpenglApp()
{
shutDown();
app_logger->writeLogFile();
delete app_logger;
}
bool OpenglApp::initialize()
{
app_logger = new Logger();
app_logger->log(SGEString(L"Begin initialization."), LOG_TRACE);
app_logger->log(SGEString(L"Initialization succeeded."), LOG_TRACE);
return bool(true);
}
bool OpenglApp::shutDown()
{
SGEString msg();
app_logger->log(SGEString(L"Begin shutdown."), LOG_TRACE);
app_logger->log(SGEString(L"Shutdown succeeded."), LOG_TRACE);
return bool(true);
}
void OpenglApp::update()
{
}
void OpenglApp::draw()
{
}
} // namespace sge
If I comment the two log() calls in the shutDown() method above, everything works fine.
This is the logger class -
#pragma once
#ifndef SGE_LOGGER_H
#define SGE_LOGGER_H
#ifndef _VECTOR_
#include <vector>
#endif
#ifndef _FSTREAM_
#include <fstream>
#endif
#ifndef SGE_TYPES_H
#include "Common/SGE_Types.h"
#endif
namespace sge
{
const SGEString DEFAULT_LOGFILE(L"Scavenger.log");
enum SGELogLevel : uint32 {
LOG_ERROR = 0x0001,
LOG_WARN = 0x0002,
LOG_INFO = 0x0004,
LOG_TRACE = 0x0008,
LOG_DEBUG = 0x0010,
LOG_CODE = 0x0020,
};
struct SGELogData {
SGELogData(uint32 severity, SGEString msg)
: level(severity)
, message(msg)
{}
uint32 level;
SGEString message;
};
class Logger
{
public:
Logger();
Logger(uint32 loglevel);
~Logger();
bool initialize();
bool shutdown();
void log(SGEString message, uint32 level = LOG_ERROR);
void log(uint32 level = 0xffff);
void setLogFile(SGEString filespec, bool flush = false);
bool writeLogFile(uint32 level = 0xffff);
protected:
SGEString logger_filespec;
uint32 logger_loglevel;
std::vector<SGELogData> logger_logs;
};
} // namespace sge
#endif // !SGE_LOGGER_H
#include "Logger.h"
namespace sge
{
Logger::Logger()
: logger_loglevel(0xffff)
, logger_filespec(L"")
{
}
Logger::Logger(uint32 loglevel)
: logger_loglevel(loglevel)
, logger_filespec(L"")
{
}
Logger::~Logger()
{
shutdown();
}
bool Logger::initialize()
{
return bool(true);
}
bool Logger::shutdown()
{
logger_logs.clear();
return bool(true);
}
void Logger::log(SGEString message, uint32 level)
{
SGEStringstream msg;
SGEString prefix;
if (!logger_loglevel & level)
return;
prefix = L"";
if(level & LOG_CODE)
prefix = L"**CODE :";
if(level & LOG_DEBUG)
prefix = L"**DEBUG:";
if(level & LOG_TRACE)
prefix = L"**TRACE:";
if(level & LOG_INFO)
prefix = L"**INFO :";
if(level & LOG_WARN)
prefix = L"**WARN :";
if(level & LOG_ERROR)
prefix = L"**ERROR:";
msg << prefix << L" " << message;
logger_logs.push_back(SGELogData(level, message));
std::wcout << msg.str().c_str() << std::endl;
}
void Logger::log(uint32 level)
{
if (!logger_loglevel & level)
return;
for (auto i : logger_logs) {
if (level & i.level) {
uint32 l(level & i.level);
SGEStringstream msg;
SGEString prefix;
if (l & LOG_CODE)
prefix = L"**CODE :";
if (l & LOG_DEBUG)
prefix = L"**DEBUG:";
if (l & LOG_TRACE)
prefix = L"**TRACE:";
if (l & LOG_INFO)
prefix = L"**INFO :";
if (l & LOG_WARN)
prefix = L"**WARN :";
if (l & LOG_ERROR)
prefix = L"**ERROR:";
msg << prefix << L" " << i.message;
std::wcout << msg.str() << std::endl;
}
}
}
void Logger::setLogFile(SGEString filespec, bool flush)
{
if (flush)
writeLogFile();
logger_filespec = filespec;
}
bool Logger::writeLogFile(uint32 level)
{
bool result(false);
if (logger_filespec.empty())
logger_filespec = DEFAULT_LOGFILE;
std::wofstream file(logger_filespec, std::ios::in | std::ios::out | std::ios::trunc);
if (!file) {
log(SGEString(L"Unable to create log file!", LOG_ERROR));
return result;
}
file << L"==================================================" << std::endl;
file << L"Scavenger Log created: " << __DATE__ << " " << __TIME__ << std::endl;
file << L"Logger log level: 0x" << std::hex << logger_loglevel << " ";
file << L" File log level : 0x" << std::hex << level << std::endl;
file << L"==================================================" << std::endl;
for (auto i : logger_logs) {
if (level & i.level) {
uint32 l(level & i.level);
//SGEStringstream msg;
SGEString prefix;
if (l & LOG_CODE)
prefix = L"**CODE :";
if (l & LOG_DEBUG)
prefix = L"**DEBUG:";
if (l & LOG_TRACE)
prefix = L"**TRACE:";
if (l & LOG_INFO)
prefix = L"**INFO :";
if (l & LOG_WARN)
prefix = L"**WARN :";
if (l & LOG_ERROR)
prefix = L"**ERROR:";
file << prefix.c_str() << L" " << i.message.c_str() << std::endl;
}
}
file.close();
return bool(true);
}
} // namespace sge
And a snippet for certain defined objects I use-
typedef std::wstring SGEString;
typedef std::wstringstream SGEStringstream;
typedef std::wostream SGECout;
I am using MSVC 2015 Community Edition. When I single step through the shutdown, all messages are displayed and the application exits normally. It only fails when running normally. Any help would be appreciated.
Following is my code, from open source only. Why StackWalk64 does not work with release build and debug build without pdbs
HEADER FILE
#ifndef STACK_TRACE_H_
#define STACK_TRACE_H_
#include <string>
#include <deque>
struct StackItem {
std::string m_name;
std::string m_file;
int m_line;
StackItem() : m_line(0) {}
};
class StackTraceImpl;
class StackTrace {
public:
typedef std::deque<StackItem> ItemContainer;
typedef ItemContainer::const_iterator ItemIterator;
ItemContainer m_items;
StackTraceImpl *m_impl;
public:
StackTrace();
virtual ~StackTrace();
void print() const;
void popFront() {
m_items.pop_front();
}
ItemIterator begin() const {
return m_items.begin();
}
ItemIterator end() const {
return m_items.end();
}
};
#endif
CPP FILE
#include <windows.h>
#include <DbgHelp.h>
#include <tlhelp32.h>
#include <vector>
#include <iostream>
#include "StackTrace.h"
std::size_t const SYMBOL_NAME_MAXLEN = 1024;
struct SymStartup {
HANDLE process;
SymStartup(HANDLE process) : process(process) {
char current[MAX_PATH];
std::string path;
if (GetCurrentDirectoryA(MAX_PATH, current) > 0) {
path += current;
path += ";";
}
if (GetModuleFileNameA(NULL, current, MAX_PATH) > 0) {
std::string filePath = current;
std::string::size_type pos = filePath.find_last_of('\\');
if (pos != std::string::npos)
filePath.erase(pos);
path += filePath;
path += ";";
}
if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", current, MAX_PATH) > 0) {
path += current;
path += ";";
}
if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", current, MAX_PATH) > 0) {
path += current;
path += ";";
}
if (GetEnvironmentVariableA("SYSTEMROOT", current, MAX_PATH) > 0) {
path += current;
path += ";";
path += current;
path += "\\system32";
path += ";";
}
if (!SymInitialize(process, path.c_str(), FALSE))
throw 1;
DWORD options = SymGetOptions();
options |= SYMOPT_LOAD_LINES;
options |= SYMOPT_FAIL_CRITICAL_ERRORS;
options = SymSetOptions(options);
}
~SymStartup() {
if (process)
SymCleanup(process);
}
};
//inline std::string wstr2str(std::wstring const& ws)
//{
// using namespace std;
// std::string mbs;
// ctype<wchar_t> const& conv(use_facet<ctype<wchar_t> >(locale()));
//
// mbs.reserve(ws.size());
// for (wstring::const_iterator it = ws.begin(); it != ws.end(); ++it)
// mbs.push_back(conv.narrow(*it, '?'));
//
// return mbs;
//}
std::string wstr2str(const std::wstring &wstr) {
std::string strTo;
char *szTo = new char[wstr.length() + 1];
szTo[wstr.size()] = '\0';
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, szTo, (int)wstr.length(), NULL, NULL);
strTo = szTo;
delete[] szTo;
return strTo;
}
class StackTraceImpl {
private:
void load_modules(HANDLE process, DWORD processID) {
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);
if (snap == INVALID_HANDLE_VALUE)
return;
MODULEENTRY32 entry;
entry.dwSize = sizeof(entry);
if (Module32First(snap, &entry)) {
do {
std::string fileName = wstr2str(entry.szExePath);
std::string moduleName = wstr2str(entry.szModule);
SymLoadModule64(process, NULL, fileName.c_str(), moduleName.c_str(), (DWORD64) entry.modBaseAddr, entry.modBaseSize);
} while (Module32Next(snap, &entry));
}
CloseHandle(snap);
}
void retrieve_context(CONTEXT& context) {
std::memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
}
void retrieve_frame(CONTEXT& context, STACKFRAME64& frame, DWORD& imageType) {
std::memset(&frame, 0, sizeof(frame));
#ifdef _M_IX86
imageType = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
imageType = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rsp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
imageType = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif
}
public:
void retrieve(StackTrace::ItemContainer& items) {
HANDLE process = 0;
try {
items.clear();
process = GetCurrentProcess();
SymStartup startup(process);
load_modules(process, GetCurrentProcessId());
HANDLE thread = GetCurrentThread();
CONTEXT context;
retrieve_context(context);
DWORD imageType = 0;
STACKFRAME64 frame;
retrieve_frame(context, frame, imageType);
std::vector<char> symbolData(sizeof(IMAGEHLP_SYMBOL64) + SYMBOL_NAME_MAXLEN, 0);
IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(&symbolData[0]);
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = SYMBOL_NAME_MAXLEN;
IMAGEHLP_LINE64 m_line;
std::memset(&m_line, 0, sizeof(IMAGEHLP_LINE64));
m_line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (int frameNum = 0; true; ++frameNum) {
if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, NULL))
break;
if (frame.AddrPC.Offset == frame.AddrReturn.Offset)
break;
if (frame.AddrPC.Offset != 0) {
StackItem item;
DWORD64 displacement64 = 0;
if (SymGetSymFromAddr64(process, frame.AddrPC.Offset, &displacement64, symbol)) {
char symbolName[SYMBOL_NAME_MAXLEN];
std::strncpy(symbolName, symbol->Name, SYMBOL_NAME_MAXLEN);
UnDecorateSymbolName(symbol->Name, symbolName, SYMBOL_NAME_MAXLEN, UNDNAME_COMPLETE);
item.m_name.assign(symbolName, symbolName + std::strlen(symbolName));
}
DWORD displacement = 0;
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, &m_line)) {
item.m_line = m_line.LineNumber;
item.m_file.assign(m_line.FileName, m_line.FileName + std::strlen(m_line.FileName));
}
items.push_back(item);
}
if (frame.AddrReturn.Offset == 0)
break;
}
} catch (...) {
}
}
};
StackTrace::StackTrace() : m_impl(new StackTraceImpl) {
m_impl->retrieve(m_items);
if (m_items.size() > 1)
m_items.erase(m_items.begin(), m_items.begin() + 2);
}
StackTrace::~StackTrace() {
}
void StackTrace::print() const {
for (StackTrace::ItemIterator it = m_items.begin(), end = m_items.end(); it != end; ++it)
std::cout << it->m_file << "(" << it->m_line << ") : " << it->m_name << std::endl;
}
MAIN FILE
#include "StackTrace.h"
void func1() {
StackTrace st;
st.print();
}
void func2() {
func1();
}
void func3() {
func2();
}
void func4() {
func3();
}
void func5() {
func4();
}
void func6() {
func5();
}
int main ( int argc, char **argv ) {
func5();
return 0;
}
DEBUG BUILD OUTPUT
E:\Avinash\my_work\StackWalk64>Debug\StackWalk64.exe
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(3) : func1
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(8) : func2
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(11) : func3
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(14) : func4
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(17) : func5
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(23) : main
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c(555) : __tmainCRTStartup
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c(371) : mainCRTStartup
(0) : BaseThreadInitThunk
(0) : RtlInitializeExceptionChain
(0) : RtlInitializeExceptionChain
RELEASE BUILD OUTPUT
E:\Avinash\my_work\StackWalk64>Release\StackWalk64.exe
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c(555) : __tmainCRTStartup
(0) : BaseThreadInitThunk
(0) : RtlInitializeExceptionChain
(0) : RtlInitializeExceptionChain
AFTER DELETING PDB FILES
E:\Avinash\my_work\StackWalk64\Debug>StackWalk64.exe
(0) :
(0) :
(0) :
(0) :
(0) :
(0) :
(0) :
(0) :
(0) : BaseThreadInitThunk
(0) : RtlInitializeExceptionChain
(0) : RtlInitializeExceptionChain
You cannot expect to reliably walk the stack on optimized code. Eliminating stack frames is on the top of the hit list for the code optimizer. The "Omit frame pointer" optimization is an important one, that frees up an extra register (EBP), always important for x86 code. It is usually off by default but the code generator applies it anyway when it can inline functions.
The strongest one is the "Inline function expansion" optimization, it replaces a function call by the code in the target function body. Which does a good number on your test code, it completely eliminates all of your functions. In other words, the code inside func1() gets moved into main().
Disabling these optimizations is unwise, it can greatly affect the efficiency of your code. The C and C++ languages were designed to be fast, debuggability was not a primary consideration. Which is why a Debug and Release configuration exists in the first place. Do keep in mind that your test code is too artificial to be a reliable indicator of what will happen in real code. You'll get something out of a stack trace, just not what you are used to when debugging code.
For the life of me I can't figure out why this is not working. Basically, I created the pipe with the inherit bit set to true, and created two child processes, and used the STARTUPINFO structure to set the input and output handles as one would need, but the pipe seems broken (the second process writes no output to the console, even though output is expected)
I know the problem does not lie in my test program (BitTwiddler.exe) because I performed the same operation using CMD.exe, and everything works as expected.
Below is a minimal reproduction of what I have. What have I done incorrectly?
#include "windows.h"
int main()
{
PROCESS_INFORMATION piSource, piDest;
HANDLE hPipeIn, hPipeOut;
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
STARTUPINFOW suSource, suDest;
ZeroMemory(&suSource, sizeof(suSource));
ZeroMemory(&suDest, sizeof(suDest));
suSource.cb = suDest.cb = sizeof(STARTUPINFOW);
suSource.dwFlags = suDest.dwFlags = STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = TRUE;
if (CreatePipe(&hPipeIn, &hPipeOut, &sa, 0) == 0)
{
return GetLastError();
}
suSource.hStdInput = hIn;
suSource.hStdError = suSource.hStdOutput = hPipeIn;
suDest.hStdInput = hPipeOut;
suDest.hStdError = suDest.hStdOutput = hOut;
std::wstring cmdLineA(L"BitTwiddler 1"), cmdLineB(L"BitTwiddler 0");
cmdLineA.push_back(0); cmdLineB.push_back(0);
if (CreateProcessW(0, &cmdLineA[0], 0, 0, TRUE, 0, 0, 0, &suSource, &piSource) == 0)
{
return GetLastError();
}
CloseHandle(piSource.hThread);
if (CreateProcessW(0, &cmdLineB[0], 0, 0, TRUE, 0, 0, 0, &suDest, &piDest) == 0)
{
return GetLastError();
}
CloseHandle(piDest.hThread);
HANDLE hArray[2];
hArray[0] = piSource.hProcess;
hArray[1] = piDest.hProcess;
WaitForMultipleObjects(2, hArray, TRUE, INFINITE);
CloseHandle(hArray[0]);
CloseHandle(hArray[1]);
return 0;
}
(In case anyone's interested, BitTwiddler is:
#include <windows.h>
#include <sstream>
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
std::size_t opt = 0;
argc--; argv++;
if (argc == 0)
{
return 0;
}
else
{
std::istringstream converter(*argv);
converter >> opt;
}
switch(opt)
{
case 0:
{
std::wstring currentLine;
while(std::getline(std::wcin, currentLine))
{
std::wcout << "Got somepin: " << currentLine << std::endl;
}
}
break;
case 1:
for (;;)
{
std::wcout << L"Hello World!" << std::endl;
Sleep(1000);
}
break;
case 2:
return -1;
default:
std::wcout << "Unknown option.";
return 0;
}
return 0;
}
), but I really don't think that matters.
You misplaced the read and write ends :)
CreatePipe has the prototype
BOOL CreatePipe(
PHANDLE hReadPipe, // can only read from this
PHANDLE hWritePipe, // can only write to this
LPSECURITY_ATTRIBUTES lpPipeAttributes,
DWORD nSize
);
You can't call ReadFile (or in your case std::getline) from a Write-only handle, and vice versa. If you replaced your std::getline calls with a simple ReadFile call, you'd get an ACCESS_DENIED error, confirming this fact, since your STD_INPUT_HANDLE in the child process was not opened for GENERIC_READ.
The fix is as follows:
suSource.hStdError = suSource.hStdOutput = hPipeOut; // must be the write pipe!
suDest.hStdInput = hPipeIn; // must be the read pipe.
Perhaps the names you assigned are confusing. If you called them as per the formal parameters, the error would be clearer:
suSource.hStdError = suSource.hStdOutput = hReadPipe; // clearly wrong.
suDest.hStdInput = hWritePipe; // as above -- expects a read-handle.
I upvoted your question as it is a very important one. Since I learnt Windows programming from FamTrinli's demos years ago, CreateProcess has been one of the harddest functions of the Win32 API. If not, the harddest.
CreateProcess has alot of arguments. Each argument requires careful reading of the documentation to properly use. Alot of arguments are complicated structures; such as SECURITY_ATTRIBUTES and STARTUPINFO. The names for the HANDLE(s) in STARTUPINFO structure can be confusing. As you were confused. The steps that are involved in using CreateProcess. whether or not you need to immediately close the handles(nb: you don't). And the actual communication between the processes can cause deadlocks.
After using CreateProcess for years I did an extensive research and testing of the API. I then designed a library that encapsulates all that knowledge. This library is apart of my Jav/win32 library. This library is similar to Java's Process class.
I am not able to post every last bit of source code here. If you are interested in the source code I can email it to you. Keeping Cpp a goto for programming.
Jav/win32/Process.h
#ifndef JAV_WIN32_PROCESS_HPP
#define JAV_WIN32_PROCESS_HPP
#include <Jav/File.h>
#include <Jav/string/cstring.h>
#include <Jav/error/error.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/container/flat_map.hpp>
#include <sstream>
#include <Jav/win32/win32.h>
namespace Jav { namespace win32 {
/** class Process makes handling win32 CreateProcess a bit easier.
Parent Process refers to the process that is calling CreateProcess.
Child Process refers to the process being created.
Note: CreateProcess uses the parent process's CurrentDirectory and
EnvironmentVariable when deducing relative filepaths used in AppName and CommandLine String.
Thus I have no idea the purpose of passing a current directory and environment to the CreateProcess.
Note: Rather than using Process::setCurrentDir and Process::addEnv and Process::addParentEnv; which
seems to make no sense at all as CreateProcess doesn't use these settings to deduce relative filepaths
passed to CreateProcess; instead use Process::addPathArg to pass absolute filepaths on the commandline.
Note: Using Process::addEnv or Process::addParentEnv will temporarily adjust parent process Environment,
and pass the adjusted Environment to the child process as well. This allows you to set Path variable
so that CreateProcess deduces relative paths. However using Process::addPath is probably better.
Note: Asynchronous read and write functions are not implemented.
Probably synchronous read and writes are good enough.
To properly read and write the child process, be sure to know when it makes read and write request.
If you sync read a process that never writes to stdout or stderror your program will deadlock.
If you write to a process that didn't read stdinput that makes no sense. You are just filling up the buffer.
Also ensure to read out data written by child process so that the buffer is not clogged up causing deadlock.
*/
class ProcessA
{
public:
enum { INVALID_EXIT_CODE = -1 };
enum { AUTO_KILL=1, WAIT=2, DEBUG=4, ADD_PARENT_ENV=8 };
struct Builder
{
struct ICompare { bool operator()(const std::string &l,const std::string &r)const; };
using EnvList = boost::container::flat_map<std::string,std::ostringstream,ICompare>;
std::ostringstream cmd;
EnvList env_list;
Jav::cstring app_name;
Jav::cstring current_dir;
STARTUPINFOA si = {sizeof(STARTUPINFOA)};
};
public:
ProcessA(const char *cmd=NULL,const char *current_dir=NULL,const char *app_name=NULL);
~ProcessA();
public:
bool launch();
int stop();
int wait(uint time=INFINITE);
void pause();
void resume();
bool isOpen();
bool isValid() { return info.hProcess; }
size_t getMsgSize();
size_t getErrorMsgSize();
size_t read(void*,size_t);
size_t readError(void*,size_t);
size_t write(const void*,size_t);
size_t write(const char*);
bool read_async(void*,size_t);
bool readError_async(void*,size_t);
bool write_async(const void*,size_t);
bool write_async(const char*);
/** Set up process for creation */
void setAppName(const char*);
void setCurrentDir(const char*);
void addArg(const char*);
void addQuotedArg(const char*);
void addPathArg(const char *parent_dir,const char *child_name);
void addEnv(const char *var,const char *val);
bool addParentEnv();
template<class ...ARGS>
void addArgEx(const char*,ARGS ...rest);
void addArgEx(const char*);
void setConsoleTitle(const char*);
void setConsoleWidth(int);
void setConsoleHeight(int);
void setConsoleTextAndFillColor(int);
void setWindowVisibility(int); //SW_SHOW, SW_HIDE, etc
void setWindowXpos(int);
void setWindowYpos(int);
void setWindowWidth(int);
void setWindowHeight(int);
void setWaitTime(uint);
void setParentMode(uint);
void addParentMode(uint);
void removeParentMode(uint);
Jav::cstring toString();
Jav::cstring getError() { return error; }
private:
Jav::cstring buildEnvironment();
Jav::cstring getCombinedEnvVar(const std::string &name);
private:
PROCESS_INFORMATION info = {};
Jav::rFile m_read_end;
Jav::rFile m_error_end;
Jav::wFile m_write_end;
OVERLAPPED async_struct;
uint flags = 0;
uint waitTime = INFINITE;
Builder *builder;
Jav::Error error = "";
};
Jav::cstring getEnvVarA(const char *name);
Jav::cstring FindFileExtExeA(const char *ext);
Jav::cstring FindFileNameExeA(const char *fname);
void setEnvironmentStringsA(const char *env);
class ProcessW
{
public:
enum { INVALID_EXIT_CODE = -1 };
enum { AUTO_KILL=1, WAIT=2, DEBUG=4, ADD_PARENT_ENV=8 };
struct Builder
{
struct ICompare { bool operator()(const std::wstring &l,const std::wstring &r)const; };
using EnvList = boost::container::flat_map<std::wstring,std::wostringstream,ICompare>;
std::wostringstream cmd;
EnvList env_list;
Jav::cstringw app_name;
Jav::cstringw current_dir;
STARTUPINFOW si = {sizeof(STARTUPINFOW)};
};
public:
ProcessW(const wchar_t *cmd=NULL,const wchar_t *current_dir=NULL,const wchar_t *app_name=NULL);
~ProcessW();
public:
bool launch();
int stop();
int wait(uint time=INFINITE);
void pause();
void resume();
bool isOpen();
bool isValid() { return info.hProcess; }
size_t getMsgSize();
size_t getErrorMsgSize();
size_t read(void*,size_t);
size_t readError(void*,size_t);
size_t write(const void*,size_t);
size_t write(const wchar_t*);
size_t write(const char*);
size_t read_async(void*,size_t);
size_t readError_async(void*,size_t);
size_t write_async(const void*,size_t);
size_t write_async(const wchar_t*);
size_t write_async(const char*);
void setAppName(const wchar_t*);
void setCurrentDir(const wchar_t*);
void addArg(const wchar_t*);
void addQuotedArg(const wchar_t*);
void addPathArg(const wchar_t *parent_dir,const wchar_t *child_name);
void addEnv(const wchar_t *var,const wchar_t *val);
bool addParentEnv();
void setAppName(const char*);
void setCurrentDir(const char*);
void addArg(const char*);
void addQuotedArg(const char*);
void addPathArg(const char *parent_dir,const char *child_name);
void addEnv(const char *var,const char *val);
template<class ...ARGS>
void addArgEx(const wchar_t*,ARGS ...rest);
template<class ...ARGS>
void addArgEx(const char*,ARGS ...rest);
void addArgEx(const wchar_t*);
void addArgEx(const char*);
void setConsoleTitle(const wchar_t*);
void setConsoleTitle(const char*);
void setConsoleWidth(int);
void setConsoleHeight(int);
void setConsoleTextAndFillColor(int);
void setWindowVisibility(int);
void setWindowXpos(int);
void setWindowYpos(int);
void setWindowWidth(int);
void setWindowHeight(int);
void setWaitTime(uint);
void setParentMode(uint);
void addParentMode(uint);
void removeParentMode(uint);
Jav::cstringw toString();
Jav::cstring getError() { return error; }
private:
Jav::cstringw buildEnvironment();
Jav::cstringw getCombinedEnvVar(const std::wstring &name);\
Jav::cstringw towstring(const char *s);
private:
PROCESS_INFORMATION info = {};
Jav::rFile m_read_end;
Jav::rFile m_error_end;
Jav::wFile m_write_end;
OVERLAPPED async_struct;
uint flags = 0;
uint waitTime = INFINITE;
Builder *builder;
Jav::Error error = "";
};
Jav::cstringw getEnvVarW(const wchar_t *name);
Jav::cstringw findFileExtExeW(const wchar_t *ext);
Jav::cstringw findFileNameExeW(const wchar_t *fname);
void setEnvironmentStringsW(const wchar_t *env);
///________________///___________
template<class ...ARGS>
void ProcessA::addArgEx(const char *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << first;
addArgEx(rest...);
}
inline void ProcessA::addArgEx(const char *s)
{
builder->cmd << ' ' << s;
}
template<class ...ARGS>
void ProcessW::addArgEx(const wchar_t *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << first;
addArgEx(rest...);
}
template<class ...ARGS>
void ProcessW::addArgEx(const char *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << towstring(first);
addArgEx(rest...);
}
inline void ProcessW::addArgEx(const wchar_t *s)
{
builder->cmd << ' ' << s;
}
inline void ProcessW::addArgEx(const char *s)
{
builder->cmd << ' ' << towstring(s);
}
}}
#endif // JAV_WIN32_PROCESS_HPP
src/Jav/win32/Process.cpp
#include <Jav/error/error.h>
#include <Jav/string.h>
#include <Jav/win32/Process.h>
#include <Jav/win32/debug.h>
#include <Jav/win32/registry.h>
#include <shlwapi.h>
namespace Jav { namespace win32 {
ProcessA::ProcessA(const char *cmd,const char *current_dir,const char *app_name)
: builder(new Builder())
{
if(cmd) builder->cmd << cmd;
builder->app_name = app_name;
builder->current_dir = current_dir;
}
ProcessA::~ProcessA()
{
stop();
delete builder;
}
bool ProcessA::launch()
{
Jav::rFile child_read_end;
Jav::wFile child_error_end;
Jav::wFile child_write_end;
if(!Jav::createPipe(m_read_end,child_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
//Jav::createPipe(m_error_end,child_error_end);
if(!Jav::createPipe(child_read_end,m_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
builder->si.dwFlags |= STARTF_USESTDHANDLES;
builder->si.hStdInput = (HANDLE)child_read_end;
builder->si.hStdOutput = (HANDLE)child_write_end;
builder->si.hStdError = (HANDLE)child_write_end;
auto env = buildEnvironment();
auto cmd = builder->cmd.str();
if(flags & ADD_PARENT_ENV)
{
auto parent_env = GetEnvironmentStringsA();
for(auto &elem : builder->env_list)
{
auto val = getCombinedEnvVar(elem.first);
SetEnvironmentVariableA(elem.first.c_str(),val);
}
auto child_env = GetEnvironmentStringsA();
if( !CreateProcessA(builder->app_name,&cmd[0],NULL,NULL,true,0,child_env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
setEnvironmentStringsA(parent_env);
FreeEnvironmentStringsA(child_env);
FreeEnvironmentStringsA(parent_env);
}
else
{
if( !CreateProcessA(builder->app_name,&cmd[0],NULL,NULL,true,0,env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
}
if( !(flags & DEBUG) )
{
delete builder;
builder = NULL;
}
/*
if( WaitForInputIdle(info.hProcess,INFINITE) == WAIT_FAILED)
{
error = Jav::DefferedError("Error: Failed to wait for input idle\n");
return false;
}
if( WaitForSingleObject(info.hProcess,INFINITE) == WAIT_FAILED)
{
error = Jav::InternalError("InternalError: Failed to wait for object\n");
return false;
}
*/
return true;
}
int ProcessA::wait(uint time)
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
int ProcessA::stop()
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
if(flags & WAIT) WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
void ProcessA::pause()
{
SuspendThread(info.hThread);
}
void ProcessA::resume()
{
ResumeThread(info.hThread);
}
bool ProcessA::isOpen()
{
if(!info.hProcess) { return false; }
/*
DWORD exitCode;
if(!GetExitCodeProcess(info.hProcess,&exitCode))
throw Jav::InternalError("InternalError: Failed to GetExitCodeProcess\n");
return exitCode == STILL_ACTIVE;
*/
switch(WaitForSingleObject(info.hProcess,0))
{
case WAIT_TIMEOUT: return true;
case WAIT_FAILED: throw Jav::Error("Error: Failed to wait for object\n");
case WAIT_ABANDONED: throw Jav::Error("Error: wait abondoned\n");
case WAIT_OBJECT_0: rep("WAIT_OBJECT_0"); return false;
default: throw Jav::Error("Error: Invalid Error Code, %s\n",__func__);
}
}
size_t ProcessA::getMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_read_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessA::getErrorMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_error_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessA::read(void *buf,size_t sz)
{
return m_read_end.read(buf,sz);
}
size_t ProcessA::readError(void *buf,size_t sz)
{
return m_error_end.read(buf,sz);
}
size_t ProcessA::write(const void *data,size_t sz)
{
return m_write_end.write(data,sz);
}
size_t ProcessA::write(const char *data)
{
return m_write_end.write(data);
}
void ProcessA::setAppName(const char *name)
{
builder->app_name = name;
}
void ProcessA::setCurrentDir(const char *dir)
{
builder->current_dir = dir;
}
void ProcessA::addArg(const char *arg)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << arg;
}
void ProcessA::addQuotedArg(const char *arg)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << '\"' << arg << '\"';
}
void ProcessA::addPathArg(const char *parent_dir,const char *child_name)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << '\"'<< parent_dir << '\\' << child_name << '\"';
}
void ProcessA::addEnv(const char *var,const char *val)
{
auto &stream = builder->env_list[var];
llong pos = stream.tellp();
pos == 0 ? stream << val : stream << ';' << val;
}
/*
void ProcessA::addEnv(const char *var,const char *val)
{
auto &stream = builder->env_list[var];
llong pos = stream.tellp();
if(pos == 0) stream << '\"' << val << '\"';
else
{
stream.seekp(pos-1);
stream << ';' << val << '\"';
}
}
*/
bool ProcessA::addParentEnv()
{
flags |= ADD_PARENT_ENV;
}
/*
bool ProcessA::addParentEnv()
{
LPCH parent_env = GetEnvironmentStringsA();
if(parent_env == NULL)
{
error = Jav::Error("Error: GetEnvironmentStrings failed\n");
FreeEnvironmentStringsA(parent_env);
return false;
}
for(auto it = Jav::MultiStringIterator(parent_env); it.has(); it.next())
{
auto var_end = Jav::nextPosOf('=',it.get()).it;
if(!var_end)
{
error = Jav::Error("Error: Invalid EnvironmentString\n");
FreeEnvironmentStringsA(parent_env);
return false;
}
Jav::cstring var(it.get(),var_end++);
addEnv(var,var_end);
}
FreeEnvironmentStringsA(parent_env);
return true;
}
*/
void ProcessA::setConsoleTitle(const char *title)
{
builder->si.lpTitle = (char*)title;
}
void ProcessA::setConsoleWidth(int w)
{
builder->si.dwFlags |= STARTF_USECOUNTCHARS;
builder->si.dwXCountChars = w;
}
void ProcessA::setConsoleHeight(int h)
{
builder->si.dwFlags |= STARTF_USECOUNTCHARS;
builder->si.dwYCountChars = h;
}
void ProcessA::setConsoleTextAndFillColor(int color)
{
builder->si.dwFlags |= STARTF_USEFILLATTRIBUTE;
builder->si.dwFillAttribute = color;
}
void ProcessA::setWindowVisibility(int visibility)
{
builder->si.dwFlags |= STARTF_USESHOWWINDOW;
builder->si.wShowWindow = visibility;
}
void ProcessA::setWindowXpos(int x)
{
builder->si.dwFlags |= STARTF_USEPOSITION;
builder->si.dwX = x;
}
void ProcessA::setWindowYpos(int y)
{
builder->si.dwFlags |= STARTF_USEPOSITION;
builder->si.dwY = y;
}
void ProcessA::setWindowWidth(int w)
{
builder->si.dwFlags |= STARTF_USESIZE;
builder->si.dwXSize = w;
}
void ProcessA::setWindowHeight(int h)
{
builder->si.dwFlags |= STARTF_USESIZE;
builder->si.dwYSize = h;
}
void ProcessA::setWaitTime(uint time)
{
waitTime = time;
}
void ProcessA::setParentMode(uint mode)
{
flags = mode;
}
void ProcessA::addParentMode(uint mode)
{
Jav::bitOn(flags,mode);
}
void ProcessA::removeParentMode(uint mode)
{
Jav::bitOff(flags,mode);
}
Jav::cstring ProcessA::toString()
{
if(!builder) return "NOTHING TO DEBUG";
std::ostringstream s;
auto env = buildEnvironment();
s << "AppName: " << builder->app_name << '\n';
if(env) s << "Env: " << Jav::MultiString{env} << '\n';
else s << "Env: " << "NO ENVIRONMENT" << '\n';
s << "CurrentDir: " << builder->current_dir << '\n';
s << "cmd: " << builder->cmd.str() << '\n';
return s.str().c_str();
}
Jav::cstring ProcessA::buildEnvironment()
{
if(!builder->env_list.size()) return NULL;
std::string env;
for(auto &elem : builder->env_list)
{
env += elem.first + '=' + elem.second.str() + '\0';
}
env += '\0';
return Jav::cstring(&env[0],&env[env.size()]);
}
Jav::cstring ProcessA::getCombinedEnvVar(const std::string &name)
{
Jav::cstring parent_val;
std::string child_val;
auto elem = builder->env_list.find(name);
if(elem != builder->env_list.end()) child_val = elem->second.str();
SetLastError(0);
auto sz = GetEnvironmentVariableA(name.c_str(),NULL,0);
if(sz == 0)
{
parent_val = GetLastError() ? NULL : "";
return child_val.empty() ? parent_val : Jav::cstring(child_val.c_str());
}
GetEnvironmentVariableA(name.c_str(),parent_val,sz);
if(child_val.empty()) return parent_val;
Jav::cstring val( strlen(parent_val + child_val.size() + 2) );
sprintf(val,"%s;%s",parent_val.str(),child_val.c_str());
return val;
}
bool ProcessA::Builder::ICompare::operator()(const std::string &l,const std::string &r)const
{
return boost::ilexicographical_compare<std::string,std::string>(l,r);
}
Jav::cstring getEnvVarA(const char *name)
{
SetLastError(0);
auto sz = GetEnvironmentVariableA(name,NULL,0);
if(sz == 0) return GetLastError() ? NULL : "";
Jav::cstring val(sz);
GetEnvironmentVariableA(name,val,sz);
return val;
}
Jav::cstring FindFileNameExe(const char *fname)
{
Jav::cstring exe_name(MAX_PATH);
auto e = (int)FindExecutableA(fname,NULL,exe_name);
if(e <= 32) throw Jav::Error("Error: unable to find association\n");
return exe_name;
}
/*
Jav::cstring FindFileExtExe(const char *ext)
{
Jav::cstring exe_name;
Jav::win32::RegistryKeyReader key(".cpp",HKEY_CLASSES_ROOT);
if(!key || !key.getString(NULL,exe_name))
throw Jav::DefferedError("Error: No program Associated with file");
return exe_name;
}
*/
/* Note uses AssocQueryString which doesn't seem to be compatible on winxp*/
Jav::cstring FindFileExtExe(const char *ext)
{
DWORD sz=0;
auto hr = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCSTR_EXECUTABLE,ext,NULL,NULL,&sz);
if( hr != S_FALSE)
throw Jav::InternalError("Error Failed to obtain extension association name length\n");
Jav::cstring exe_name(sz);
hr = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCSTR_EXECUTABLE,ext,NULL,exe_name,&sz);
if( hr != S_OK)
throw Jav::InternalError("Error Failed to obtain extension association\n");
return exe_name;
}
void setEnvironmentStringsA(const char *env)
{
if(env == NULL) throw Jav::Error("Error: environment string is null\n");
for(auto it = Jav::MultiStringIterator(env); it.has(); )
{
auto begin = it.get();
auto end = it.next('\0');
auto var_end = Jav::nextPosOf('=',{begin,end});
if(!var_end) throw Jav::Error("Error: Invalid EnvironmentString\n");
Jav::cstring var(begin,var_end.it++);
SetEnvironmentVariableA(var,var_end);
}
}
ProcessW::ProcessW(const wchar_t *cmd,const wchar_t *current_dir,const wchar_t *app_name)
: builder(new Builder())
{
if(cmd) builder->cmd << cmd;
builder->app_name = app_name;
builder->current_dir = current_dir;
}
ProcessW::~ProcessW()
{
stop();
delete builder;
}
bool ProcessW::launch()
{
Jav::rFile child_read_end;
Jav::wFile child_error_end;
Jav::wFile child_write_end;
if(!Jav::createPipe(m_read_end,child_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
//Jav::createPipe(m_error_end,child_error_end);
if(!Jav::createPipe(child_read_end,m_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
builder->si.dwFlags |= STARTF_USESTDHANDLES;
builder->si.hStdInput = (HANDLE)child_read_end;
builder->si.hStdOutput = (HANDLE)child_write_end;
builder->si.hStdError = (HANDLE)child_write_end;
auto env = buildEnvironment();
auto cmd = builder->cmd.str();
if(flags & ADD_PARENT_ENV)
{
auto parent_env = GetEnvironmentStringsW();
for(auto &elem : builder->env_list)
{
auto val = getCombinedEnvVar(elem.first);
SetEnvironmentVariableW(elem.first.c_str(),val);
}
auto child_env = GetEnvironmentStringsW();
if( !CreateProcessW(builder->app_name,&cmd[0],NULL,NULL,false,0,child_env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
setEnvironmentStringsW(parent_env);
FreeEnvironmentStringsW(child_env);
FreeEnvironmentStringsW(parent_env);
}
else
{
if( !CreateProcessW(builder->app_name,&cmd[0],NULL,NULL,false,0,env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
}
if( !(flags & DEBUG) )
{
delete builder;
builder = NULL;
}
return true;
}
int ProcessW::wait(uint time)
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
int ProcessW::stop()
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
if(flags & WAIT) WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
void ProcessW::pause()
{
SuspendThread(info.hThread);
}
void ProcessW::resume()
{
ResumeThread(info.hThread);
}
bool ProcessW::isOpen()
{
if(!info.hProcess) return false;
DWORD exitCode;
if(!GetExitCodeProcess(info.hProcess,&exitCode))
throw Jav::InternalError("InternalError: Failed to GetExitCodeProcess\n");
return exitCode == STILL_ACTIVE;
}
size_t ProcessW::getMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_read_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessW::getErrorMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_error_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessW::read(void *buf,size_t sz)
{
return m_read_end.read(buf,sz);
}
...
...
...