Use Visual Studio exception in C - visual-studio

I use С language in project. Eventually, I began to notice that most of the code is check. In the project the return type of the function bool. Because we are working with the media and if function read or write return false we must break programm,
Some time ago I learned that in Visual Studio has built-in exception (Use Visual Studio 2008)
__try
__finally
In the project, we use the log. All functions use log.
We decided to combine the Log and exception. Use preprocessor
#define try do \
{ \
Int32_T __FUNCTION__##ReturnValue = -1; \
LOG_BEGIN(); \
__try \
{
#define finally } \
__finally \
{
#define end_try } \
if( __FUNCTION__##ReturnValue != -1 ) \
{ \
return LOG_END_WITH(__FUNCTION__##ReturnValue); \
} \
else \
{ \
LOG_END(); \
return; \
} \
}while(0)
#define raise(e) RaiseException( e, EXCEPTION_NONCONTINUABLE, 0, NULL )
#define return_false __FUNCTION__##ReturnValue = 0; \
__leave
#define return_true ##__FUNCTION__##ReturnValue = 1; \
__leave
#define return_void ##__FUNCTION__##ReturnValue = -1; \
__leave
Example of use. Function return the bool value
Bool_T Node_Insert( IN OUT Tree_T * tree, IN Text_T fullPath, ... )
{
Text_T itemName = NULL;
int * delimiterPos = NULL;
try
{
// if tree is empty we should add ROOT_PATH
//
if( Tree_IsEmpty( IN *tree ) )
{
if( failed Tree_CreateRoot( IN OUT *tree, IN datafield ) )
{
LOG_ERROR( "Node_Insert FAILURE --> Tree_CreateRoot failed" );
return_false;
}
}
if( Text_Length( IN fullPath ) > 1 )
{
...
// Allocate memory for Text
//
item = Text_New();
// Allocate for delimeterPos array
//
delimiterPos = NEW(n, sizeof(elem));
...
while( i < nDelimiter )
{
...
...
...
}
}
else if ( Text_Length( IN fullPath ) == 1 )
{
// we have already added ROOT
//
return_true;
}else{
LOG_ERROR( "Node_Insert FAILURE --> Path length is not correct" );
return_false;
}
}
// In the end we need free allocated memory
//
finally
{
Text_Delete( IN OUT &itemName );
free( delimiterPos );
}
// in end_try preprocessor we write log and return true or false
//
end_try;
}
Example of use. Method that don't return value
void Ifo_ReadBufferHeader( IN Uint8_T * buffer, ... IN OUT Int32_T * position )
{
Text_T fullPath = NULL;
try
{
// getting buffer size
//
Uint32_T size = Buffer_GetSize( IN buffer );
// getting mode
//
Uint8_T writeMode = Buffer_GetWritingMode( IN buffer );
if( Ifo_IsAddMode( IN static_cast(DataIfoMode_T, writeMode) ) )
{
Uint32_T pos = 1 + Ifo_GetLabelLength() + 23;
// getting path length
//
fullPath = Text_New( IN "", IN mp );
Buffer_GetNameField( IN buffer, OUT &fullPath, IN OUT &pos );
*parentFolderPath = fullPath;
*position = pos;
} else if( Ifo_IsRenameMode( IN static_cast(DataIfoMode_T, writeMode ) )
{
*position = 1 + Ifo_GetLabelLength() + 23;
}
*bufferSize = size;
*mode = static_cast(DataIfoMode_T, writeMode);
// return_void preprocessor that use
//
return_void;
}
finally
{
// After run the main code you must free allocated memory
//
Text_Delete( IN OUT &fullPath );
}
end_try;
}
My question is. How do I design what would it would return a value? That is, the function will return a value, such as calculate buffer CRC return as a value. How do I do this by using these macros.
int GetCrcBuffer( IN Uint32_T buffer, IN Tree_T tree )
{
Text_T itemName = NULL;
int crc = 0;
try
{
...
...
itemName = Text_New();
if( crc != 12 )
{
return_value(crc); // ???? return value of crc and delete text??? how???
}
...
...
}
finally
{
Text_Delete( IN OUT itemName );
}
end_try;
}
How to make it run finally block and return value crc??
How write the preprocessor return_value() ???

Your finally block will properly delete itemname, so you don't have to worry about that part. The only trick is that your return value cannot be -1 (that is: -1 cannot be a legitimate return value) as that signals error in end_try. Then just like with raise(e), you can specify a parameter to the macro:
#define return_value(v) ##__FUNCTION__##ReturnValue = (v); \
__leave
If you want to write a function that can return nnon-integral types, you can do this trick: have the function actually return an int, signalling success/failure, and in the case of success, pass the actual "return value" (the non-integral type one) back via a pointer parameter:
#define return_value2(v) *ret_val=(v); \
##__FUNCTION__##ReturnValue = 1; \
__leave
int GetMyType( IN Uint32_T buffer, IN Tree_T tree, MyType* ret_val )
{
...
}
Note: the value ret_val points to is valid only if the function did not return -1

Related

How do I allocate memory for LabVIEW array of clusters using DSSetHandleSize() without a memory exception

I am able to reliably use DSSetHandleSize() with arrays of LabVIEW strings. When I try the same with arrays of clusters, it always crashes upon return from the DLL when increasing the handle size.
The code is:
typedef struct {
uint8_t depth;
LStrHandle name;
LStrHandle child;
} TreeElem;
typedef struct {
int32_t dimSize;
TreeElem Element[1];
} **TreeHdl;
MSEXPORT void BuildTreeCtl(int32_t depth, uint32_t xmlNodePtr, TreeHdl TreeCluster)
{
xmlNode *cur_node, *chile_node;
for (cur_node = (xmlNode *) xmlNodePtr; cur_node; cur_node = cur_node->next) {
if (cur_node->type == XML_ELEMENT_NODE) {
DSSetHandleSize(TreeCluster, sizeof(int32) + (sizeof(TreeElem) * ((*TreeCluster)->dimSize + 1)));
int idx = (*TreeCluster)->dimSize++;
if (cur_node->name != NULL) {
LV_STR_NEW(((*TreeCluster)->Element)[idx].name, (char*) cur_node->name);}
// check for CDATA
for (chile_node = cur_node->children; chile_node; chile_node = chile_node->next) {
if (chile_node->type == XML_CDATA_SECTION_NODE) {
if (chile_node->content != NULL) {
LV_STR_NEW(((*TreeCluster)->Element)[idx].child, (char*) chile_node->content);
}
}
}
BuildTreeCtl(depth + 1, (uint32_t) cur_node->children, TreeCluster);
}
}
}
Call DSSetHSzClr instead of DSSetHandleSize.
It turns out DSSetHandleSize() (for at least LabVIEW 2013 and earlier) does not initialize new memory to zero when increasing array size. Also, it's likely to allocate a new block of memory with a different handle than when going in - it does reliably transfer existing data to the new memory allocation, but whatever exists in the new memory block will cause LabVIEW to crash on return, certainly because LabVIEW is using junk pointers.
The code should be:
MSEXPORT void BuildTreeCtl(int32_t depth, uint32_t xmlNodePtr, TreeHdl TreeCluster)
{
xmlNode *cur_node, *chile_node;
for (cur_node = (xmlNode *) xmlNodePtr; cur_node; cur_node = cur_node->next) {
if (cur_node->type == XML_ELEMENT_NODE) {
DSSetHandleSize(TreeCluster, sizeof(int32) + (sizeof(TreeElem) * ((*TreeCluster)->dimSize + 1)));
int idx = (*TreeCluster)->dimSize++;
#if 0 // brute force
(((*TreeCluster)->Element)[idx]).depth = depth;
(((*TreeCluster)->Element)[idx]).name = NULL;
(((*TreeCluster)->Element)[idx]).child = NULL;
#elif 0 // generic
uint8_t *Ptr = (uint8_t *) &(((*TreeCluster)->Element)[idx]);
for (int j=0; j<sizeof(TreeElem); j++) {*(Ptr + j) = 0;}
#else
uint8_t *Ptr = (uint8_t *) &(((*TreeCluster)->Element)[idx]);
memset(Ptr, 0, sizeof(TreeElem));
#endif
if (cur_node->name != NULL) {
LV_STR_NEW(((*TreeCluster)->Element)[idx].name, (char*) cur_node->name);}
// check for CDATA
for (chile_node = cur_node->children; chile_node; chile_node = chile_node->next) {
if (chile_node->type == XML_CDATA_SECTION_NODE) {
if (chile_node->content != NULL) {
LV_STR_NEW(((*TreeCluster)->Element)[idx].child, (char*) chile_node->content);
}
}
}
BuildTreeCtl(depth + 1, (uint32_t) cur_node->children, TreeCluster);
}
}
}

Resolve managed and native stack trace - which API to use?

This is continuation to my previous question - phase 2 so to say.
First question was here: Fast capture stack trace on windows / 64-bit / mixed mode
Now I have resolved a huge amount of stack traces and now wondering how to resolve symbol information of managed stack frames.
For native C++ side it's relatively simple -
First you specify which process from where to take symbols:
HANDLE g_hProcess = GetCurrentProcess();
Where you can replace process in run-time using code snipet like this:
g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_processId);
b = (g_hProcess != NULL );
if( !b )
errInfo.AppendFormat(_T("Process id '%08X' is not running anymore."), g_processId );
else
InitSymbolLoad();
And initialize symbol loading:
void InitSymbolLoad()
{
SymInitialize(g_hProcess, NULL, TRUE);
DWORD dwFlags = SymGetOptions();
SymSetOptions(SymGetOptions() | SYMOPT_DEFERRED_LOADS | SYMOPT_NO_IMAGE_SEARCH);
}
And after that resolve native symbol , somehow like this:
extern HANDLE g_hProcess;
void StackFrame::Resolve()
{
struct {
union
{
SYMBOL_INFO symbol;
char buf[sizeof(SYMBOL_INFO) + 1024];
}u;
}ImageSymbol = { 0 };
HANDLE hProcess = g_hProcess;
DWORD64 offsetFromSymbol = 0;
ImageSymbol.u.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
ImageSymbol.u.symbol.Name[0] = 0;
ImageSymbol.u.symbol.MaxNameLen = sizeof(ImageSymbol) - sizeof(SYMBOL_INFO);
SYMBOL_INFO* pSymInfo = &ImageSymbol.u.symbol;
// Get file / line of source code.
IMAGEHLP_LINE64 lineStr = { 0 };
lineStr.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
function.clear();
if( SymGetLineFromAddr64(hProcess, (DWORD64)ip, (DWORD*)&offsetFromSymbol, &lineStr) )
{
function = lineStr.FileName;
function += "(";
function += std::to_string((_ULonglong) lineStr.LineNumber).c_str();
function += "): ";
}
// Successor of SymGetSymFromAddr64.
if( SymFromAddr(hProcess, (DWORD64)ip, &offsetFromSymbol, pSymInfo) )
function += ImageSymbol.u.symbol.Name;
}
This looks like working.
But now also managed stack frames.
There are two interfaces which I've located:
IDebugClient / GetNameByOffset
Mentioned in:
http://www.codeproject.com/Articles/371137/A-Mixed-Mode-Stackwalk-with-the-IDebugClient-Inter
(*) (Includes sample code)
http://blog.steveniemitz.com/building-a-mixed-mode-stack-walker-part-1/
Used by:
https://github.com/okigan/CrashInsight (Code not touched for 4 years)
Mixed mode stackwalk article provides good example.
IXCLRDATAProcess / GetRuntimeNameByAddress
Mentioned also in two links above.
Used by process hacker (GPL license, C style)
Implementation seems to reside in here:
https://github.com/dotnet/coreclr/blob/master/src/debug/daccess/daccess.cpp
(Based on commits this code is quite alive)
ICorProfiler / ???
Mentioned at the end of (*) article.
Approach 1 seems to be quite old fashioned, also article (*) mentions some problems around it.
Approach 3 will probably require in-depth analysis of profiling API's.
There is also one mention I have found about these API's - in here:
https://naughter.wordpress.com/2015/05/24/changes-in-the-windows-10-sdk-compared-to-windows-8-1-part-two/
· cor.h, cordebug.h/idl, CorError.h, CorHdr.h, corhlpr.h,
corprof.h/idl, corpub.h/idl & corsym.h/idl: All of these header files
have been removed. They are all the native mode COM interface to .NET.
This sentence I don't fully understand. Are those interfaces dead or replaced or what happened to them ?
So I guess based on my brief analysis approach 2 is only good / alive API interface which is worth of using ? Have you came across any problems related to those api's.
After walking through huge amount of code samples and interfaces, I've understood that there aren't any simple to use API interface. Code and API's developed for native C++ works only with native C++, and code and API's developed for managed code works only with managed code.
There is additionally problem of resolving stack trace afterwards might not work. You see - developer can generate code dynamically on fly using Jit engine / IL Generator, and dispose it as well - so after you have "void*" / instruction address - you should resolve symbolic information right away, not afterwards. But I'll leave this for time being, will assume that developer is not too fancy coder and not generating and disposing new code all the times, and FreeLibrary will not be called without need. (May be I can address this later on if I'll hook FreeLibrary / Jit components.)
Resolving function name was quite trivial, through IXCLRDataProcess with little bit of magic and luck - I was able to get function names, however - I want to expand it deeper - into exact source code path and source code line where code were executing, and this turned to be quite complex functionality to reach.
Finally I've hit upon source code where such thing were performed - and it was done here:
https://github.com/dotnet/coreclr/blob/master/src/ToolBox/SOS/Strike/util.cpp
GetLineByOffset is function name in that file.
I've analyzed, retuned and made my own solution from that source code, which I'm now attaching here now:
Updated code can be found from here:
https://sourceforge.net/projects/diagnostic/
But here is just a snapshot of same code taken at some point of time:
ResolveStackM.h:
#pragma once
#include <afx.h>
#pragma warning (disable: 4091) //dbghelp.h(1544): warning C4091: 'typedef ': ignored on left of '' when no variable is declared
#include <cor.h> //xclrdata.h requires this
#include "xclrdata.h" //IXCLRDataProcess
#include <atlbase.h> //CComPtr
#include <afxstr.h> //CString
#include <crosscomp.h> //TCONTEXT
#include <Dbgeng.h> //IDebugClient
#pragma warning (default: 4091)
class ResoveStackM
{
public:
ResoveStackM();
~ResoveStackM();
void Close(void);
bool InitSymbolResolver(HANDLE hProcess, CString& lastError);
bool GetMethodName(void* ip, CStringA& methodName);
bool GetManagedFileLineInfo(void* ip, CStringA& lineInfo);
HMODULE mscordacwks_dll;
CComPtr<IXCLRDataProcess> clrDataProcess;
CComPtr<ICLRDataTarget> target;
CComPtr<IDebugClient> debugClient;
CComQIPtr<IDebugControl> debugControl;
CComQIPtr<IDebugSymbols> debugSymbols;
CComQIPtr<IDebugSymbols3> debugSymbols3;
};
//
// Typically applications don't need more than one instance of this. If you do, use your own copies.
//
extern ResoveStackM g_managedStackResolver;
ResolveStackM.cpp:
#include "ResolveStackM.h"
#include <Psapi.h> //EnumProcessModules
#include <string> //to_string
#pragma comment( lib, "dbgeng.lib" )
class CLRDataTarget : public ICLRDataTarget
{
public:
ULONG refCount;
bool bIsWow64;
HANDLE hProcess;
CLRDataTarget( HANDLE _hProcess, bool _bIsWow64 ) :
refCount(1),
bIsWow64(_bIsWow64),
hProcess(_hProcess)
{
}
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, PVOID* ppvObject)
{
if ( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, __uuidof(ICLRDataTarget)) )
{
AddRef();
*ppvObject = this;
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef( void)
{
return ++refCount;
}
ULONG STDMETHODCALLTYPE Release( void)
{
refCount--;
if( refCount == 0 )
delete this;
return refCount;
}
virtual HRESULT STDMETHODCALLTYPE GetMachineType( ULONG32 *machineType )
{
#ifdef _WIN64
if (!bIsWow64)
*machineType = IMAGE_FILE_MACHINE_AMD64;
else
*machineType = IMAGE_FILE_MACHINE_I386;
#else
*machineType = IMAGE_FILE_MACHINE_I386;
#endif
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetPointerSize( ULONG32* pointerSize )
{
#ifdef _WIN64
if (!bIsWow64)
#endif
*pointerSize = sizeof(PVOID);
#ifdef _WIN64
else
*pointerSize = sizeof(ULONG);
#endif
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetImageBase( LPCWSTR imagePath, CLRDATA_ADDRESS *baseAddress )
{
HMODULE dlls[1024] = { 0 };
DWORD nItems = 0;
wchar_t path[ MAX_PATH ];
DWORD whatToList = LIST_MODULES_ALL;
if( bIsWow64 )
whatToList = LIST_MODULES_32BIT;
if( !EnumProcessModulesEx( hProcess, dlls, sizeof(dlls), &nItems, whatToList ) )
{
DWORD err = GetLastError();
return HRESULT_FROM_WIN32(err);
}
nItems /= sizeof(HMODULE);
for( unsigned int i = 0; i < nItems; i++ )
{
path[0] = 0;
if( GetModuleFileNameEx(hProcess, dlls[i], path, sizeof(path) / sizeof(path[0])) )
{
wchar_t* pDll = wcsrchr( path, L'\\');
if (pDll) pDll++;
if (_wcsicmp(imagePath, path) == 0 || _wcsicmp(imagePath, pDll) == 0)
{
*baseAddress = (CLRDATA_ADDRESS) dlls[i];
return S_OK;
}
}
}
return E_FAIL;
}
virtual HRESULT STDMETHODCALLTYPE ReadVirtual( CLRDATA_ADDRESS address, BYTE *buffer, ULONG32 bytesRequested, ULONG32 *bytesRead )
{
SIZE_T readed;
if( !ReadProcessMemory(hProcess, (void*)address, buffer, bytesRequested, &readed) )
return HRESULT_FROM_WIN32( GetLastError() );
*bytesRead = (ULONG32) readed;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE WriteVirtual( CLRDATA_ADDRESS address, BYTE *buffer, ULONG32 bytesRequested, ULONG32 *bytesWritten )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetTLSValue( ULONG32 threadID, ULONG32 index, CLRDATA_ADDRESS *value )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE SetTLSValue( ULONG32 threadID, ULONG32 index, CLRDATA_ADDRESS value )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetCurrentThreadID( ULONG32 *threadID )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetThreadContext( ULONG32 threadID, ULONG32 contextFlags, ULONG32 contextSize, BYTE *context )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE SetThreadContext( ULONG32 threadID, ULONG32 contextSize, BYTE *context)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Request( ULONG32 reqCode, ULONG32 inBufferSize, BYTE *inBuffer, ULONG32 outBufferSize, BYTE *outBuffer)
{
return E_NOTIMPL;
}
}; //CLRDataTarget
ResoveStackM::ResoveStackM() :
mscordacwks_dll(0)
{
}
ResoveStackM::~ResoveStackM()
{
Close();
}
void ResoveStackM::Close( void )
{
clrDataProcess.Release();
target.Release();
debugClient.Release();
if( mscordacwks_dll != 0 )
{
FreeLibrary(mscordacwks_dll);
mscordacwks_dll = 0;
}
}
bool ResoveStackM::InitSymbolResolver(HANDLE hProcess, CString& lastError)
{
wchar_t path[ MAX_PATH ] = { 0 };
// According to process hacker - mscoree.dll must be loaded before loading mscordacwks.dll.
// It's enough if base application is managed.
if( GetWindowsDirectoryW(path, sizeof(path)/sizeof(wchar_t) ) == 0 )
return false; //Unlikely to fail.
#ifdef _WIN64
wcscat(path, L"\\Microsoft.NET\\Framework64\\v4.0.30319\\mscordacwks.dll");
#else
wcscat(path, L"\\Microsoft.NET\\Framework\\v4.0.30319\\mscordacwks.dll");
#endif
mscordacwks_dll = LoadLibraryW(path);
PFN_CLRDataCreateInstance pCLRCreateInstance = 0;
if( mscordacwks_dll != 0 )
pCLRCreateInstance = (PFN_CLRDataCreateInstance) GetProcAddress(mscordacwks_dll, "CLRDataCreateInstance");
if( mscordacwks_dll == 0 || pCLRCreateInstance == 0)
{
lastError.Format(L"Required dll mscordacwks.dll from .NET4 installation was not found (%s)", path);
Close();
return false;
}
BOOL isWow64 = FALSE;
IsWow64Process(hProcess, &isWow64);
target.Attach( new CLRDataTarget(hProcess, isWow64 != FALSE) );
HRESULT hr = pCLRCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&clrDataProcess );
if( FAILED(hr) )
{
lastError.Format(L"Failed to initialize mscordacwks.dll for symbol resolving (%08X)", hr);
Close();
return false;
}
hr = DebugCreate(__uuidof(IDebugClient), (void**)&debugClient);
if (FAILED(hr))
{
lastError.Format(_T("Could retrieve symbolic debug information using dbgeng.dll (Error code: 0x%08X)"), hr);
return false;
}
DWORD processId = GetProcessId(hProcess);
const ULONG64 LOCAL_SERVER = 0;
int flags = DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND;
hr = debugClient->AttachProcess(LOCAL_SERVER, processId, flags);
if (hr != S_OK)
{
lastError.Format(_T("Could attach to process 0x%X (Error code: 0x%08X)"), processId, hr);
Close();
return false;
}
debugControl = debugClient;
hr = debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
if ((hr = debugControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) != S_OK)
{
return false;
}
debugSymbols3 = debugClient;
debugSymbols = debugClient;
// if debugSymbols3 == NULL - GetManagedFileLineInfo will not work
return true;
} //Init
struct ImageInfo
{
ULONG64 modBase;
};
// Based on a native offset, passed in the first argument this function
// identifies the corresponding source file name and line number.
bool ResoveStackM::GetManagedFileLineInfo( void* ip, CStringA& lineInfo )
{
ULONG lineN = 0;
char path[MAX_PATH];
ULONG64 dispacement = 0;
CComPtr<IXCLRDataMethodInstance> method;
if (!debugSymbols || !debugSymbols3)
return false;
// Get managed method by address
CLRDATA_ENUM methEnum;
HRESULT hr = clrDataProcess->StartEnumMethodInstancesByAddress((ULONG64)ip, NULL, &methEnum);
if( hr == S_OK )
{
hr = clrDataProcess->EnumMethodInstanceByAddress(&methEnum, &method);
clrDataProcess->EndEnumMethodInstancesByAddress(methEnum);
}
if (!method)
goto lDefaultFallback;
ULONG32 ilOffsets = 0;
hr = method->GetILOffsetsByAddress((CLRDATA_ADDRESS)ip, 1, NULL, &ilOffsets);
switch( (long)ilOffsets )
{
case CLRDATA_IL_OFFSET_NO_MAPPING:
goto lDefaultFallback;
case CLRDATA_IL_OFFSET_PROLOG:
// Treat all of the prologue as part of the first source line.
ilOffsets = 0;
break;
case CLRDATA_IL_OFFSET_EPILOG:
{
// Back up until we find the last real IL offset.
CLRDATA_IL_ADDRESS_MAP mapLocal[16];
CLRDATA_IL_ADDRESS_MAP* map = mapLocal;
ULONG32 count = _countof(mapLocal);
ULONG32 needed = 0;
for( ; ; )
{
hr = method->GetILAddressMap(count, &needed, map);
if ( needed <= count || map != mapLocal)
break;
map = new CLRDATA_IL_ADDRESS_MAP[ needed ];
}
ULONG32 highestOffset = 0;
for (unsigned i = 0; i < needed; i++)
{
long l = (long) map[i].ilOffset;
if (l == CLRDATA_IL_OFFSET_NO_MAPPING || l == CLRDATA_IL_OFFSET_PROLOG || l == CLRDATA_IL_OFFSET_EPILOG )
continue;
if (map[i].ilOffset > highestOffset )
highestOffset = map[i].ilOffset;
} //for
if( map != mapLocal )
delete[] map;
ilOffsets = highestOffset;
}
break;
} //switch
mdMethodDef methodToken;
void* moduleBase = 0;
{
CComPtr<IXCLRDataModule> module;
hr = method->GetTokenAndScope(&methodToken, &module);
if( !module )
goto lDefaultFallback;
//
// Retrieve ImageInfo associated with the IXCLRDataModule instance passed in. First look for NGENed module, second for IL modules.
//
for (int extentType = CLRDATA_MODULE_PREJIT_FILE; extentType >= CLRDATA_MODULE_PE_FILE; extentType--)
{
CLRDATA_ENUM enumExtents;
if (module->StartEnumExtents(&enumExtents) != S_OK )
continue;
CLRDATA_MODULE_EXTENT extent;
while (module->EnumExtent(&enumExtents, &extent) == S_OK)
{
if (extentType != extent.type )
continue;
ULONG startIndex = 0;
ULONG64 modBase = 0;
hr = debugSymbols->GetModuleByOffset((ULONG64) extent.base, 0, &startIndex, &modBase);
if( FAILED(hr) )
continue;
moduleBase = (void*)modBase;
if (moduleBase )
break;
}
module->EndEnumExtents(enumExtents);
if( moduleBase != 0 )
break;
} //for
} //module scope
DEBUG_MODULE_AND_ID id;
DEBUG_SYMBOL_ENTRY symInfo;
hr = debugSymbols3->GetSymbolEntryByToken((ULONG64)moduleBase, methodToken, &id);
if( FAILED(hr) )
goto lDefaultFallback;
hr = debugSymbols3->GetSymbolEntryInformation(&id, &symInfo);
if (FAILED(hr))
goto lDefaultFallback;
char* IlOffset = (char*)symInfo.Offset + ilOffsets;
//
// Source maps for managed code can end up with special 0xFEEFEE markers that
// indicate don't-stop points. Try and filter those out.
//
for (ULONG SkipCount = 64; SkipCount > 0; SkipCount--)
{
hr = debugSymbols3->GetLineByOffset((ULONG64)IlOffset, &lineN, path, sizeof(path), NULL, &dispacement );
if( FAILED( hr ) )
break;
if (lineN == 0xfeefee)
IlOffset++;
else
goto lCollectInfoAndReturn;
}
if( !FAILED(hr) )
// Fall into the regular translation as a last-ditch effort.
ip = IlOffset;
lDefaultFallback:
hr = debugSymbols3->GetLineByOffset((ULONG64) ip, &lineN, path, sizeof(path), NULL, &dispacement);
if( FAILED(hr) )
return false;
lCollectInfoAndReturn:
lineInfo += path;
lineInfo += "(";
lineInfo += std::to_string((_ULonglong) lineN).c_str();
lineInfo += "): ";
return true;
}
bool ResoveStackM::GetMethodName(void* ip, CStringA& symbol)
{
symbol.Empty();
GetManagedFileLineInfo(ip, symbol);
USES_CONVERSION;
CLRDATA_ADDRESS displacement = 0;
ULONG32 len = 0;
wchar_t name[1024];
if (!clrDataProcess )
return false;
HRESULT hr = clrDataProcess->GetRuntimeNameByAddress( (CLRDATA_ADDRESS)ip, 0, sizeof(name) / sizeof(name[0]), &len, name, &displacement );
if( FAILED( hr ) )
return false;
name[ len ] = 0;
symbol += W2A(name);
return true;
} //GetMethodName
ResoveStackM g_managedStackResolver;
So far tested only with some smaller piece of code, only 64-bit (doubt that 32-bit works at all - I don't have call stack determination yet for it).
It's possible that this code contains bugs, but I'll try to haunt them down and fix them.
I harvested so much code that please mark this answer as useful. :-)
Here is an answer from Jan Kotas on this:
From: Jan Kotas <jkotas#microsoft.com>
To: Tarmo Pikaro <tapika#yahoo.com>
Sent: Tuesday, January 12, 2016 5:09 AM
Subject: RE: Fast capture stack trace on windows 64 bit / mixed mode...
Your solution based on IXCLRDATAProcess sounds good to me.
PerfView (https://www.microsoft.com/en-us/download/details.aspx?id=28567) –
that does what you are trying to build as well as a lot of other stuff – is
using IXCLRDATA* as well. You may be interested in
https://github.com/Microsoft/clrmd . It is set of managed wrappers for
IXCLRDATA* that are easier to use than the COM interfaces.
What I have briefly tried out - this requires Visual Studio 2015 / C# 6.0.
Also this technique is unusable. Like .net StackTrace / StackFrame are resolving call stack and symbol information right away - and I need to resolve symbol information afterwards (after stack trace capturing).
Alternative 1 / IDebugClient / GetNameByOffset is not usable for managed stack trace, it can be used for native code only - as for native call stack I have demo code snipet above already. Not sure whether IDebugClient provides something more than SymGetLineFromAddr64 / SymFromAddr does not provide - not sure.

Blank Results while backtesting

I am new to the world of MQL4-code.
I come from a C++ background and I am trying to learn MQL4 language & conventions.
I am writing a simple Expert Advisor (my first ever).It compiles but, when I am trying to test it, it ends with no results. I attach code to better illustrate what I am trying to do:
//+------------------------------------------------------------------+
//| MyFirstExpert.mq4 |
//| Leonardo |
//| http://investinmarkets.altervista.org |
//+------------------------------------------------------------------+
#property copyright "Leonardo "
#property link "http://investinmarkets.altervista.org"
#property version "1.00"
#property strict
input int BarCount = 3;
int Ticket = 0;
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
int BarCountTemp = BarCount + 1;
double bars[];
ArrayResize( bars, BarCountTemp );
for ( int i = 0; i < BarCountTemp; i++ ) {
bars[i] = Close[i + 1];
}
int i = 0;
bool is_p;
do
{
if ( bars[i] > bars[i+1] && i < BarCountTemp ) is_p = true;
else is_p = false;
i++;
}
while ( is_p );
if ( is_p == true && Ticket == 0 ) {
Ticket = OrderSend(_Symbol,OP_SELL,0.1,Bid,0,0,0,"Sell Order Custom",110);
Alert("Sell order opened to match found.");
Comment("Sell order opened #"+Ticket+".");
}
if ( Ticket != 0 ) {
bool select = OrderSelect(Ticket,SELECT_BY_TICKET);
if ( Close[1] > Close[2] ) {
bool close = OrderClose(Ticket,OrderLots(),Ask,0,clrGreen);
Alert("Sell order closed.");
Comment("Sell order closed #"+Ticket+".");
Ticket = 0;
}
}
}
//+------------------------------------------------------------------+
I want to simply count bars (input by user) and then perform a check: if e.g. 3 bars are all positive then open a sell order (just this case for the moment). If opened, the next bar check if still positive, if not close the trade.
I am getting always blank results.
Thank you in advance!
Welcome to the MQL4-world, Leonardo
let's review the syntax:
for ( int i = 0; i < BarCountTemp; i++ ) {
bars[i] = Close[i + 1];
}
int i = 0;
bool is_p;
do
{
if ( bars[i] > bars[i+1] && i < BarCountTemp ) is_p = true;
else is_p = false;
i++;
}
while ( is_p );
could be merged / simplified into a single loop/break construct:
bool is_p = True; // FYI: FALSE if not initialised
// WARNING: "New"-MQL4 has changed variable visibility-scope to be limited just to the innermost syntax-construct and variables easily "cease" exist outside that syntax-construct boundary ... for(){bool is_p ...visible...} ...invisible...
for ( int i = 0; // .SET
i < BarCountTemp; // .TEST: [**]
i++ ) { // .INC
if ( Close[i+1] > Close[i+2] // avoid TimeSeries' replica(s)
// && i < BarCountTemp // ALWAYS TRUE [^**]
) continue; // ---------------------------- LOOP-^
else {
is_p = False;
break; // ---------------------------- EXIT-v
}
Next: got at least one Comment() remark on top of the chart window?
int Ticket = EMPTY; // Rather initialise as = EMPTY;
if ( is_p == True
&& Ticket == EMPTY // un-ambiguous meaning
) {
Ticket = OrderSend( _Symbol, // .SYM
OP_SELL, // .OP
0.1, // .LOTs check sizing, MarketInfo()
Bid, // .PRICE
0, // .SLIPPAGE
0, // .SL
0, // .TP
"Sell Order Custom",// .COMMENT
110 // .MAGNUM
);
if ( Ticket == EMPTY ){ // EXC. HANDLER
...
}
else {
Alert( "Sell order opened to match found." ); // .NOP if isTesting()
Comment( "Sell order opened #" + Ticket + "." ); // .GUI is visible????
}
}
Finally: include Exception handlers for cases, where error may appear
if ( Ticket != EMPTY // TEST 1st,
&& Close[1] > Close[2] // TEST 2nd, prevent dbPool-ops, if not True
) {
bool select = OrderSelect( Ticket, SELECT_BY_TICKET );
if (!select ){ // EXC. HANDLER
...
}
bool close = OrderClose( Ticket,
OrderLots(),
Ask,
0,
clrGreen
);
if (!close ){ // EXC. HANDLER
...
}
Alert( "Sell order closed." );
Comment( "Sell order closed #" + Ticket + "." );
Ticket = EMPTY; // .SET EMPTY
}
}

How to return nullptr from a lambda function?

I have a small lambda function which shall find and return a QTreeWidgetItem. But if it does not find the given item, then it shall return a nullptr. But if I try to compile it then it gives me an error.
The function:
auto takeTopLevelItem = []( QTreeWidget* aTreeWidget, const QString& aText )
{
const int count = aTreeWidget->topLevelItemCount();
for ( int index = 0; index < count; ++index )
{
auto item = aTreeWidget->topLevelItem( index );
if ( item->text( 0 ) == aText )
{
return aTreeWidget->takeTopLevelItem( index );
}
}
return nullptr; // This causes a compilation error.
};
The error:
Error 1 error C3487: 'nullptr': all return expressions in a lambda must have the same type: previously it was 'QTreeWidgetItem *' cpp 251
I changed the mentioned line with this and now it compiles:
return (QTreeWidgetItem*)( nullptr );
but I would like to avoid this syntax. How can I solve this ?
I use Visual Studio 2012.
You can add an explicit return type annotation:
auto takeTopLevelItem = []( ... ) -> QTreeWidgetItem*
{
// ...
}
That way nullptr will be converted to your pointer type properly. You're getting that error because the lambda assumes no conversions should be made, and treats nullptr_t as a legitimate alternative return type.
As a side note, consider using (std::)optional instead. The nullability of pointers can be used to represent a missing return, but it doesn't mean it necessarily should be.
If you just want to avoid the syntax, rather than the casting, you could it like this:
static_cast<QTreeWidgetItem*>(nullptr);
I made a small example, on how Bartek's and mine's answer really work:
#include <iostream>
class A {
int a;
};
auto bla = [] (A* obj, bool flag) -> A* {
if(flag)
return obj;
return nullptr;
// return static_cast<A*>(nullptr);
};
int main() {
A obj;
A* ptr = &obj;
bool flag = false;
if( bla(ptr, flag) == nullptr)
std::cout << "ok\n";
return 0;
}
I had this very same issue with some Smart Pointers, so I found I could do this to avoid the issue:
auto myFunc= [](MyClass* class)
{
MyPointer* pointer = nullptr;
if( class && class->isValid() )
{
pointer = class->getPointerInstance()
}
return pointer;
}
Similarly, for shared pointer, just repleace MyPointer* by std::shared_ptr<MyPointer>.
So your code would looks like:
auto takeTopLevelItem = []( QTreeWidget* aTreeWidget, const QString& aText )
{
QTreeWidgetItem* item = nullptr;
const int count = aTreeWidget->topLevelItemCount();
for ( int index = 0; index < count; ++index )
{
auto item = aTreeWidget->topLevelItem( index );
if ( item->text( 0 ) == aText )
{
item = aTreeWidget->takeTopLevelItem( index );
break;
}
}
return item;
};

Resolve Windows device path to drive letter

How do you resolve an NT style device path, e.g. \Device\CdRom0, to its logical drive letter, e.g. G:\ ?
Edit: A Volume Name isn't the same as a Device Path so unfortunately GetVolumePathNamesForVolumeName() won't work.
Hopefully the following piece of code will give you enough to solve this - after you've initialised it, you just need to iterate through the collection to find your match. You may want to convert everything to upper/lower case before you insert into the collection to help with lookup performance.
typedef basic_string<TCHAR> tstring;
typedef map<tstring, tstring> HardDiskCollection;
void Initialise( HardDiskCollection &_hardDiskCollection )
{
TCHAR tszLinkName[MAX_PATH] = { 0 };
TCHAR tszDevName[MAX_PATH] = { 0 };
TCHAR tcDrive = 0;
_tcscpy_s( tszLinkName, MAX_PATH, _T("a:") );
for ( tcDrive = _T('a'); tcDrive < _T('z'); ++tcDrive )
{
tszLinkName[0] = tcDrive;
if ( QueryDosDevice( tszLinkName, tszDevName, MAX_PATH ) )
{
_hardDiskCollection.insert( pair<tstring, tstring>( tszLinkName, tszDevName ) );
}
}
}
Maybe you could use GetVolumeNameForMountPoint and iterate through all mount points A:\ through Z:\, breaking when you find a match?
http://msdn.microsoft.com/en-us/library/aa364994(VS.85).aspx
(I haven't tried this)
Following function does the job using C only
BOOL GetWin32FileName(const TCHAR* pszNativeFileName, TCHAR *pszWin32FileName)
{
BOOL bFound = FALSE;
// Translate path with device name to drive letters.
TCHAR szTemp[MAX_PATH];
szTemp[0] = '\0';
if (GetLogicalDriveStrings(MAX_PATH - 1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
TCHAR* p = szTemp;
do
{
// Copy the drive letter to the template string
*szDrive = *p;
// Look up each device name
if (QueryDosDevice(szDrive, szName, MAX_PATH))
{
size_t uNameLen = _tcslen(szName);
if (uNameLen < MAX_PATH)
{
bFound = _tcsnicmp(pszNativeFileName, szName, uNameLen) == 0
&& *(pszNativeFileName + uNameLen) == _T('\\');
if (bFound)
{
// Replace device path with DOS path
StringCchPrintf(pszWin32FileName,
MAX_PATH,
TEXT("%s%s"),
szDrive,
pszNativeFileName + uNameLen);
}
}
}
// Go to the next NULL character.
while (*p++);
} while (!bFound && *p);
}
return(bFound);
}
You can lookup all volumes' name to match a device name and get drive letter.Here is a sample:
int DeviceNameToVolumePathName(WCHAR *filepath) {
WCHAR fileDevName[MAX_PATH];
WCHAR devName[MAX_PATH];
WCHAR fileName[MAX_PATH];
HANDLE FindHandle = INVALID_HANDLE_VALUE;
WCHAR VolumeName[MAX_PATH];
DWORD Error = ERROR_SUCCESS;
size_t Index = 0;
DWORD CharCount = MAX_PATH + 1;
int index = 0;
// \Device\HarddiskVolume1\windows,locate \windows.
for (int i = 0; i < lstrlenW(filepath); i++) {
if (!memcmp(&filepath[i], L"\\", 2)) {
index++;
if (index == 3) {
index = i;
break;
}
}
}
filepath[index] = L'\0';
memcpy(fileDevName, filepath, (index + 1) * sizeof(WCHAR));
FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));
if (FindHandle == INVALID_HANDLE_VALUE)
{
Error = GetLastError();
wprintf(L"FindFirstVolumeW failed with error code %d\n", Error);
return FALSE;
}
for (;;)
{
// Skip the \\?\ prefix and remove the trailing backslash.
Index = wcslen(VolumeName) - 1;
if (VolumeName[0] != L'\\' ||
VolumeName[1] != L'\\' ||
VolumeName[2] != L'?' ||
VolumeName[3] != L'\\' ||
VolumeName[Index] != L'\\')
{
Error = ERROR_BAD_PATHNAME;
wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);
break;
}
VolumeName[Index] = L'\0';
CharCount = QueryDosDeviceW(&VolumeName[4], devName, 100);
if (CharCount == 0)
{
Error = GetLastError();
wprintf(L"QueryDosDeviceW failed with error code %d\n", Error);
break;
}
if (!lstrcmpW(devName, filepath)) {
VolumeName[Index] = L'\\';
Error = GetVolumePathNamesForVolumeNameW(VolumeName, fileName, CharCount, &CharCount);
if (!Error) {
Error = GetLastError();
wprintf(L"GetVolumePathNamesForVolumeNameW failed with error code %d\n", Error);
break;
}
// concat drive letter to path
lstrcatW(fileName, &filepath[index + 1]);
lstrcpyW(filepath, fileName);
Error = ERROR_SUCCESS;
break;
}
Error = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));
if (!Error)
{
Error = GetLastError();
if (Error != ERROR_NO_MORE_FILES)
{
wprintf(L"FindNextVolumeW failed with error code %d\n", Error);
break;
}
//
// Finished iterating
// through all the volumes.
Error = ERROR_BAD_PATHNAME;
break;
}
}
FindVolumeClose(FindHandle);
if (Error != ERROR_SUCCESS)
return FALSE;
return TRUE;
}
If you want to resolve it in driver,you can check this link for reference.
Here is refactored version of the solution.
I replaced TChAR with wchar_t because afaik it's not a good idea to use it in most projects.
std::map<std::wstring, std::wstring> GetDosPathDevicePathMap()
{
// It's not really related to MAX_PATH, but I guess it should be enough.
// Though the docs say "The first null-terminated string stored into the buffer is the current mapping for the device.
// The other null-terminated strings represent undeleted prior mappings for the device."
wchar_t devicePath[MAX_PATH] = { 0 };
std::map<std::wstring, std::wstring> result;
std::wstring dosPath = L"A:";
for (wchar_t letter = L'A'; letter <= L'Z'; ++letter)
{
dosPath[0] = letter;
if (QueryDosDeviceW(dosPath.c_str(), devicePath, MAX_PATH)) // may want to properly handle errors instead ... e.g. check ERROR_INSUFFICIENT_BUFFER
{
result[dosPath] = devicePath;
}
}
return result;
}

Resources