Related
I cannot seem to figure out why its doing this...
unsigned long drawing::font;
auto drawing::setup_fonts() -> void
{
drawing::font = g_surface->CreateFont_();
g_surface->SetFontGlyphSet(drawing::font, "Small Fonts", 8, 400, 0, 0, sdk::FONTFLAG_OUTLINE);
}
auto drawing::render_text(int x, int y, Color clr, const char* fmt, ...) -> void
{
va_list va_alist;
char buffer[1024] = { '\0' };
wchar_t str[1024] = { '\0' };
va_start(va_alist, fmt);
vsprintf(buffer, fmt, va_alist);
va_end(va_alist);
swprintf(str, 1024, L"%s", buffer);
g_surface->DrawSetTextPos(x, y);
g_surface->DrawSetTextFont(drawing::font);
g_surface->DrawSetTextColor(clr);
g_surface->DrawPrintText(str, wcslen(str));
}
it is giving me this error in both functions, second one is at the closing bracket and first error is this line
drawing::font = g_surface->CreateFont_();
namespace drawing def
namespace drawing
{
auto setup_fonts() -> void;
auto render_text(int x, int y, Color clr, const char* fmt, ...) -> void;
extern unsigned long font;
}
g_surface def
sdk::ISurface* g_surface;
extern sdk::ISurface* g_surface;
g_surface = get_interface<sdk::ISurface>("vguimatsurface.dll", VGUIMAT_SURFACE_INTERFACE_VERSION);
template <class T>
auto get_interface(const char* module, const char* name) -> T*
{
return reinterpret_cast<T*>(platform::get_interface(module, name));
}
and ISurface class. its telling me to add more info because of how big this class is so i removed most of it
class ISurface
{
public:
...
virtual vgui::HFont CreateFont_() = 0;
virtual bool SetFontGlyphSet(vgui::HFont font, const char* windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin = 0, int nRangeMax = 0) = 0;
...
};
in debugger I checked if g_surface was nullptr and its not.
I have imported a DLL into my program, and there are a few functions as follows:
typedef int (*FEEDQ_CALLBACK) ( long nSD, long nQtype, long nNumber ) ;
typedef int (*CALLDISP_CALLBACK) ( AODFeedDispositionBase* CallDisposition );
typedef int (*SERVICE_STATUS_CALLBACK) ( long nSD, long nStatus ) ;
typedef int (*AUTH_SERVICE_CALLBACK) ( long nSD, char* sSDesc, long nSType ) ;
typedef int (*INBOUND_ABANDON_CALLBACK) ( long nSD, DCCProInboundDisposition* inbounCall ) ;
typedef int (*SESSION_STATUS_CALLBACK) ( long lTypeId, long lStatus ) ;
extern "C"
{
//Methods
AODFEEDAPI short Initialize(const char * sTenantName, const char * sUserID, const char * sPassword, AUTH_SERVICE_CALLBACK pAuthSrvFunc, SERVICE_STATUS_CALLBACK pSrvStatFunc, FEEDQ_CALLBACK pFeedQFunc, CALLDISP_CALLBACK pCallDispFunc, INBOUND_ABANDON_CALLBACK pInboundCallFunc, SESSION_STATUS_CALLBACK pSessionStatFunc = 0);
AODFEEDAPI short Close();
AODFEEDAPI short StopService( long nServiceID, long nQueueAction );
AODFEEDAPI short StartService(long nServiceID);
}
I want to call the Initialize() function in the DLL. I'm confused as to how I should pass the parameters in the callback function.
You don't pass parameters to the callbacks. The callbacks are functions you define in your own code and then give to the DLL so it can call them when needed, passing parameters to you. For example:
int MyFeedQCallback( long nSD, long nQtype, long nNumber )
{
// do something...
}
int MyCallDispCallback( AODFeedDispositionBase* CallDisposition )
{
// do something...
}
int MyServiceStatusCallback( long nSD, long nStatus )
{
// do something...
}
int MyAuthServiceCallback( long nSD, char* sSDesc, long nSType )
{
// do something...
}
int MyInboundAbandonCallback( long nSD, DCCProInboundDisposition* inbounCall )
{
// do something...
}
int MySessionStatusCallback( long lTypeId, long lStatus )
{
// do something...
}
int main()
{
...
short ret = Initialize("tenant name", "user ID", "password", &AuthServiceCallback, &MyServiceStatusCallback, &MyFeedQCallback, &MyCallDispCallback, &MyInboundAbandonCallback, &MySessionStatusCallback);
...
long serviceID = ...;
StartService(serviceID);
...
StopService(serviceID, queueAction);
...
Close();
return 0;
}
I would like to create a program that will run in background as a final product. For debug purpose I want it to display a console.
I learned that there is a ShowWindow( hWnd, SW_HIDE ); function, but if I use it in a 'standard' main function the console window still pops up for a moment. I was trying to work it out like this (yes I know it's crappy):
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <tchar.h>
#define DEBUG
//#undef DEBUG
#ifndef DEBUG
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd = GetConsoleWindow();
ShowWindow( hWnd, SW_HIDE );
while(1);
return 0;
}
#else
#pragma comment(linker, "/SUBSYSTEM:CONSOLE")
int main(int argc, int **argv)
{
HWND hWnd = GetConsoleWindow();
while(1);
return 0;
}
#endif
Here I managed to prevent the window form popping up, but I can't pass parameters to the program.
I believe there is a much better solution for this. Can you share?
PS
I don't want to use .NET.
This is an answer to the first part of the question, "Here I managed to prevent the window form popping up", i.e. how to set the Windows subsystem for an application in Visual C++.
I will answer the second part of the question, about command line arguments, separately.
// How to create a Windows GUI or console subsystem app with a standard `main`.
#ifndef _MSC_VER
# error Hey, this is Visual C++ specific source code!
#endif
// Better set this in the project settings, so that it's more easily configured.
#ifdef NDEBUG
# pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
#else
# pragma comment( linker, "/subsystem:console" )
#endif
#undef UNICODE
#define UNICODE
#undef NOMINMAX
#define NOMINAX
#undef STRICT
#define STRICT
#include <windows.h>
int main()
{
MessageBox( 0, L"Hi!", L"This is the app!", MB_SETFOREGROUND );
}
The NDEBUG standard C++ macro is designed for suppressing the effect of standard assert, so it’s not required to be globally meaningful. However, in practice it is globally meaningful. And then it provides a bit of portability compared to using a Visual C++ macro such as DEBUG.
Anyway, in order to make it easier to configure the subsystem, unless you want to enforce that debug builds should be console and release builds should be GUI, then I recommend doing this in the project settings rather than via a #pragma (note also that e.g. the g++ compiler does not support linker pragmas, so using the project settings is more portable).
If you want you can check the subsystem programmatically instead of just by inspection (i.e. instead of noting whether the above program produces a console or not):
// How to create a Windows GUI or console subsystem app with a standard `main`.
#ifndef _MSC_VER
# error Hey, this is Visual C++ specific source code!
#endif
// Better set this in the project settings, so that it's more easily configured.
#ifdef NDEBUG
# pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
#else
# pragma comment( linker, "/subsystem:console" )
#endif
#undef UNICODE
#define UNICODE
#undef NOMINMAX
#define NOMINAX
#undef STRICT
#define STRICT
#include <windows.h>
#include <assert.h> // assert
#include <string> // std::wstring
#include <sstream> // std::wostringstream
using namespace std;
template< class Type >
wstring stringFrom( Type const& v )
{
wostringstream stream;
stream << v;
return stream.str();
}
class S
{
private:
wstring s_;
public:
template< class Type >
S& operator<<( Type const& v )
{
s_ += stringFrom( v );
return *this;
}
operator wstring const& () const { return s_; }
operator wchar_t const* () const { return s_.c_str(); }
};
IMAGE_NT_HEADERS const& imageHeaderRef()
{
HMODULE const hInstance =
GetModuleHandle( nullptr );
IMAGE_DOS_HEADER const* const pImageHeader =
reinterpret_cast< IMAGE_DOS_HEADER const* >( hInstance );
assert( pImageHeader->e_magic == IMAGE_DOS_SIGNATURE ); // "MZ"
IMAGE_NT_HEADERS const* const pNTHeaders = reinterpret_cast<IMAGE_NT_HEADERS const*>(
reinterpret_cast< char const* >( pImageHeader ) + pImageHeader->e_lfanew
);
assert( pNTHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC ); // "PE"
return *pNTHeaders;
}
int main()
{
IMAGE_NT_HEADERS const& imageHeader = imageHeaderRef();
WORD const subsystem = imageHeader.OptionalHeader.Subsystem;
MessageBox(
0,
S() << L"Subsystem " << subsystem << L" "
<< (0?0
: subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI? L"GUI"
: subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI? L"Console"
: L"Other"),
L"Subsystem info:",
MB_SETFOREGROUND );
}
You can still pass parameters to a regular, non-console Win32 program: they just show up in the single lpCmdLine string, all globbed together into one big command line. You can use CommandLineToArgvW to parse that into separate arguments, but do note that that function is only available in the Unicode flavor. For example:
int wmain(int argc, wchar_t **argv)
{
// Common main function (Unicode args)
}
#ifndef DEBUG
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Forward invocation to wmain
int argc;
LPWSTR *argv = CommandLineToArgvW(pCmdLine, &argc);
int status = wmain(argc, argv);
LocalFree(argv);
return status;
}
#endif
I'd also recommend using your project settings to set the executable's subsystem (console or Windows) depending on the configuration instead of using a #pragma to do it.
This is an answer to the second part of the question, “but I can't pass parameters to the program”, i.e. how to obtain the command line arguments in a Visual C++ Windows app.
The simplest but also most limited way is to use the arguments of a standard C++ main,
int main( int argc, char* argv[] )
{
// Whatever, e.g.
vector<string> const args( argv, argv + argc );
}
The C++ standard strongly suggests that those arguments should be encoded with some multi-byte character set such as UTF-8,
C++11 §3.6.1/2:
“If argc is nonzero these arguments shall be supplied in argv[0]
through argv[argc-1] as pointers to the initial characters of null-terminated multibyte strings (NTMBSs) (17.5.2.1.4.2) and argv[0] shall be the pointer to the initial character of a NTMBS that represents the name used to invoke the program or "".”
However, at the time of the first C++ standard, in 1998, neither the *nix world convention, nor the Windows convention, was to do this. Instead the convention was to pass the arguments with some locale-specific character encoding. The Linux world almost immediately started a migration towards UTF-8, while Windows did not, so that still as of 2012 in Windows the standard main arguments are not sufficient to pass e.g. arbitrary filenames…
Happily, in Windows the command line that’s passed to the process, and that is available via the GetCommandLine API function, is UTF-16 encoded, which means that any filename (and indeed any text) can be passed.
On the third hand, the API function that provides a standard parsing of the command line, CommandLineToArgvW, has at least one sillybug, and maybe more… And presumably the non-standard Visual C++ Unicode C++ startup function wmain has arguments provided by that function. So for best results, until that’s been fixed one should use some proper home-brewed command line parsing, e.g. as illustrated in the program below (I just picked an ad-hoc “personal tool” program I made last week, it’s similar to the Windows 2000 Resource Kit’s timethis):
// A program to measure the execution time of another program.
// Based vaguely on Jeffrey Richter's "timep" program in
// the 2nd edition of "Win32 System Programming".
//
// Author: Alf P. Steinbach, 2012. License: Boost license 1.0.
#undef UNICODE
#define UNICODE
#undef STRICT
#define STRICT
#undef NOMINMAX
#define NOMINMAX
#include <windows.h>
#include <shlwapi.h> // PathGetCharType
#include <assert.h> // assert
#include <functional> // std::function
#include <iomanip> // set::setfill, std::setw
#include <iostream> // std::wcout, std::endl
#include <sstream> // std::wostringstream
#include <stddef.h> // ptrdiff_t
#include <stdexcept> // std::runtime_error, std::exception
#include <stdint.h> // int64_t
#include <string> // std::string
#include <type_traits> // std::is_fundamental
#include <utility> // std::move
using namespace std;
#if !defined( CPP_STATIC_ASSERT )
# define CPP_STATIC_ASSERT( e ) static_assert( e, #e )
#endif
#if !defined( CPP_NORETURN )
# define CPP_NORETURN [[noreturn]]
#endif
// MSVC workaround: "#define CPP_NORETURN __declspec( noreturn )"
// clang workaround: "#define CPP_NORETURN __attribute__(( noreturn ))"
namespace cpp {
namespace detail {
template< class Destination, class Source >
class ImplicitCast
{
public:
static Destination value( Source const v )
{
return static_cast<Destination>( v );
}
};
template< class Source >
class ImplicitCast< bool, Source >
{
public:
static bool value( Source const v )
{
return !!v; // Shuts up Visual C++ sillywarning about performance.
}
};
};
template< class Destination, class Source >
Destination implicitCast( Source const v )
{
CPP_STATIC_ASSERT( is_fundamental< Destination >::value );
CPP_STATIC_ASSERT( is_fundamental< Source >::value );
return detail::ImplicitCast< Destination, Source >::value( v );
}
typedef ptrdiff_t Size;
inline bool hopefully( bool const c ) { return c; }
inline CPP_NORETURN bool throwX( string const& s )
{
throw runtime_error( s );
}
inline CPP_NORETURN bool throwX( string const& s, exception const& reasonX )
{
throwX( s + "\n!Because - " + reasonX.what() );
}
class ScopeGuard
{
private:
function<void()> cleanup_;
ScopeGuard( ScopeGuard const& ); // No such.
ScopeGuard& operator=( ScopeGuard const& ); // No such.
public:
~ScopeGuard() { cleanup_(); }
ScopeGuard( function<void()> const cleanup )
: cleanup_( cleanup )
{}
};
class SubstringRef
{
private:
wchar_t const* start_;
wchar_t const* end_;
public:
Size length() const { return end_ - start_; }
wchar_t const* start() const { return start_; }
wchar_t const* end() const { return end_; }
SubstringRef( wchar_t const* start, wchar_t const* end )
: start_( start )
, end_( end )
{}
};
inline void skipWhitespace( wchar_t const*& p )
{
while( *p != L'\0' && iswspace( *p ) ) { ++p; }
}
inline wchar_t const* theAfterWhitespacePart( wchar_t const* p )
{
skipWhitespace( p );
return p;
}
inline void invert( bool& b ) { b = !b; }
} // namespace cpp
namespace winapi {
using cpp::hopefully;
using cpp::invert;
using cpp::Size;
using cpp::skipWhitespace;
using cpp::SubstringRef;
using cpp::theAfterWhitespacePart;
using cpp::throwX;
namespace raw {
typedef DWORD DWord;
typedef FILETIME FileTime;
typedef HANDLE Handle;
typedef PROCESS_INFORMATION ProcessInformation;
typedef SYSTEMTIME SystemTime;
typedef WORD Word;
} // namespace raw
// The following logic is mainly a workaround for a bug in CommandLineToArgvW.
// See [http://preview.tinyurl.com/CommandLineToArgvWBug].
inline SubstringRef nextArgumentIn( wchar_t const* const commandLine )
{
wchar_t const* p = commandLine;
skipWhitespace( p );
wchar_t const* const start = p;
bool isInQuotedPart = false;
while( *p != L'\0' && (isInQuotedPart || !iswspace( *p ) ) )
{
if( *p == L'\"' ) { invert( isInQuotedPart ); }
++p;
}
return SubstringRef( start, p );
}
// This corresponds essentially to the argument of wWinMain(...).
inline wchar_t const* commandLineArgPart()
{
SubstringRef const programSpec = nextArgumentIn( GetCommandLine() );
return theAfterWhitespacePart( programSpec.end() );
}
class ProcessInfo
{
private:
raw::ProcessInformation info_;
ProcessInfo( ProcessInfo const& ); // No such.
ProcessInfo& operator=( ProcessInfo const& ); // No such.
public:
raw::ProcessInformation& raw() { return info_; }
raw::Handle handle() const { return info_.hProcess; }
~ProcessInfo()
{
::CloseHandle( info_.hThread );
::CloseHandle( info_.hProcess );
}
ProcessInfo(): info_() {}
ProcessInfo( ProcessInfo&& other )
: info_( move( other.info_ ) )
{
other.info_ = raw::ProcessInformation(); // Zero.
}
};
inline ProcessInfo createProcess( wchar_t const commandLine[] )
{
STARTUPINFO startupInfo = { sizeof( startupInfo ) };
ProcessInfo processInfo;
wstring mutableCommandLine( commandLine );
mutableCommandLine += L'\0';
GetStartupInfo( &startupInfo );
bool const creationSucceeded = !!CreateProcess (
nullptr, // LPCTSTR lpApplicationName,
&mutableCommandLine[0], // LPTSTR lpCommandLine,
nullptr, // LPSECURITY_ATTRIBUTES lpProcessAttributes,
nullptr, // LPSECURITY_ATTRIBUTES lpThreadAttributes,
true, // BOOL bInheritHandles,
NORMAL_PRIORITY_CLASS, // DWORD dwCreationFlags,
nullptr, // LPVOID lpEnvironment,
nullptr, // LPCTSTR lpCurrentDirectory,
&startupInfo, // LPSTARTUPINFO lpStartupInfo,
&processInfo.raw() // LPPROCESS_INFORMATION lpProcessInformation
);
hopefully( creationSucceeded )
|| throwX( "winapi::createProcess: CreateProcess failed" );
return processInfo;
}
inline raw::Handle dup(
raw::Handle const h,
raw::DWord const desiredAccess,
bool inheritable = false
)
{
raw::Handle result = 0;
bool const wasDuplicated = !!DuplicateHandle(
GetCurrentProcess(), h,
GetCurrentProcess(), &result,
desiredAccess,
inheritable,
0 // options
);
hopefully( wasDuplicated )
|| throwX( "winapi::dup: DuplicateHandle failed" );
assert( result != 0 );
return result;
}
inline int64_t mSecsFromRelative( raw::FileTime const t )
{
ULARGE_INTEGER asLargeInt;
asLargeInt.u.HighPart = t.dwHighDateTime;
asLargeInt.u.LowPart = t.dwLowDateTime;
return asLargeInt.QuadPart/10000;
}
SubstringRef filenamePart( SubstringRef const& path )
{
wchar_t const* p = path.end();
while( p != path.start() && PathGetCharType( *p ) != GCT_SEPARATOR )
{
--p;
}
if( PathGetCharType( *p ) == GCT_SEPARATOR ) { ++p; }
return SubstringRef( p, path.end() );
}
} // namespace winapi
winapi::ProcessInfo createProcess( wchar_t const commandLine[], char const errMsg[] )
{
try{ return winapi::createProcess( commandLine ); }
catch( exception const& x ) { cpp::throwX( errMsg, x ); }
}
winapi::raw::Handle run( wchar_t const commandLine[] )
{
namespace raw = winapi::raw;
using cpp::hopefully;
using cpp::throwX;
using winapi::dup;
using winapi::ProcessInfo;
static char const* const createErrMsg = "Failed to create process";
ProcessInfo const process = createProcess( commandLine, createErrMsg );
// Early handle duplication ensures that one has the required rights.
raw::Handle const accessibleHandle =
dup( process.handle(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE );
raw::DWord const waitResult = WaitForSingleObject( process.handle(), INFINITE );
hopefully( waitResult == WAIT_OBJECT_0 )
|| throwX( "Failed waiting for process termination." );
return accessibleHandle;
}
class Interval
{
private:
int hours_;
int minutes_;
int seconds_;
int milliseconds_;
public:
int msecs() const { return milliseconds_; }
int seconds() const { return seconds_; }
int minutes() const { return minutes_; }
int hours() const { return hours_; }
Interval( int msecs, int seconds = 0, int minutes = 0, int hours = 0 )
: milliseconds_( msecs )
, seconds_( seconds )
, minutes_( minutes )
, hours_( hours )
{
assert( unsigned( hours ) < 24 );
assert( unsigned( minutes ) < 60 );
assert( unsigned( seconds ) < 60 );
assert( unsigned( msecs ) < 1000 );
}
static Interval fromMSecs( int msecs )
{
int const totalSeconds = msecs / 1000;
int const totalMinutes = totalSeconds / 60;
int const totalHours = totalMinutes / 24;
return Interval(
msecs % 1000, totalSeconds % 60, totalMinutes %60, totalHours
);
}
};
wostream& operator<<( wostream& stream, Interval const& t )
{
wostringstream formatter;
formatter << setfill( L'0' );
formatter
<< setw( 2 ) << t.hours() << ":"
<< setw( 2 ) << t.minutes() << ":"
<< setw( 2 ) << t.seconds() << "."
<< setw( 3 ) << t.msecs();
return (stream << formatter.str());
}
string narrowStringFrom( cpp::SubstringRef const& s )
{
return string( s.start(), s.end() ); // Non-ANSI characters => garbage.
}
void cppMain()
{
namespace raw = winapi::raw;
using cpp::hopefully;
using cpp::implicitCast;
using cpp::ScopeGuard;
using cpp::SubstringRef;
using cpp::throwX;
using winapi::commandLineArgPart;
using winapi::filenamePart;
using winapi::mSecsFromRelative;
using winapi::nextArgumentIn;
SubstringRef const programSpec = nextArgumentIn( GetCommandLine() );
SubstringRef const programName = filenamePart( programSpec );
wchar_t const* const otherCommandLine = commandLineArgPart();
hopefully( nextArgumentIn( otherCommandLine ).length() > 0 )
|| throwX( "Usage: " + narrowStringFrom( programName ) + " command" );
raw::DWord const startMSecs = GetTickCount();
raw::Handle const finishedProcess = run( otherCommandLine );
raw::DWord const endMSecs = GetTickCount();
raw::DWord const realElapsedMSecs = endMSecs - startMSecs;
ScopeGuard const closingHandle( [=]() { CloseHandle( finishedProcess ); } );
Interval const realElapsedTime = Interval::fromMSecs( realElapsedMSecs );
static char const* const commandLineLabel = "Command line: ";
static char const* const rElapsedTimeLabel = "External elapsed time: ";
static char const* const pElapsedTimeLabel = "In-process elapsed time: ";
static char const* const kernelTimeLabel = "In-process kernel time: ";
static char const* const userTimeLabel = "In-process user time: ";
wclog << endl;
wclog << commandLineLabel << "[" << otherCommandLine << "]" << endl;
wclog << rElapsedTimeLabel << realElapsedTime << endl;
raw::FileTime creationTime;
raw::FileTime exitTime;
raw::FileTime kernelTime;
raw::FileTime userTime;
bool const timesWereObtained = !!GetProcessTimes(
finishedProcess, &creationTime, &exitTime, &kernelTime, &userTime
);
hopefully( timesWereObtained )
|| throwX( "cppMain: GetProcessTimes failed" );
int const elapsedTimeMSecs= implicitCast<int>(
mSecsFromRelative( exitTime ) - mSecsFromRelative( creationTime )
);
int const kernelTimeMSecs = implicitCast<int>( mSecsFromRelative( kernelTime ) );
int const userTimeMSecs = implicitCast<int>( mSecsFromRelative( userTime ) );
wclog << pElapsedTimeLabel << Interval::fromMSecs( elapsedTimeMSecs ) << endl;
wclog << kernelTimeLabel << Interval::fromMSecs( kernelTimeMSecs ) << endl;
wclog << userTimeLabel << Interval::fromMSecs( userTimeMSecs ) << endl;
}
int main()
{
try
{
cppMain();
return EXIT_SUCCESS;
}
catch( exception const& x )
{
wcerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
Rather than changing your subsystem mode depending on the build type, consider using AllocConsole to explicitly create a console for your process.
I want to use class member functions as callbacks, I don't use libsigc, because it's slow.
In ATL, we can use member function for C-style callback(http://www.codeproject.com/KB/cpp/SoloGenericCallBack.aspx), so can we implement c++ thunk in linux?
The code below will crash:
#include <assert.h>
#include <stdio.h>
#include <sys/mman.h>
typedef char BYTE;
typedef int DWORD;
typedef int* DWORD_PTR;
typedef int* INT_PTR;
typedef bool BOOL;
typedef unsigned long ULONG;
typedef unsigned long* ULONG_PTR;
#define PtrToUlong( p ) ((ULONG)(ULONG_PTR) (p) )
#define __stdcall __attribute__((__stdcall__))
//#pragma pack( push, 1 )
struct MemFunToStdCallThunk
{
BYTE m_mov;
DWORD m_this;
BYTE m_pushEax;
BYTE m_jmp;
DWORD m_relproc;
void Init( DWORD_PTR proc, void* pThis )
{
printf("proc=%x\n", proc);
m_mov = 0xB8; // mov eax
m_this = PtrToUlong(pThis);
m_pushEax = 0xc3;// push eax
m_jmp = 0xe9; //jmp
m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(MemFunToStdCallThunk)));
printf("m_relproc = %x\n", m_relproc);
mprotect(this, sizeof(MemFunToStdCallThunk), PROT_READ|PROT_WRITE|PROT_EXEC);
}
void* GetCodeAddress()
{
return this;
}
}__attribute__ ((packed));
//#pragma pack( pop )
template< typename TDst, typename TSrc >
TDst UnionCastType( TSrc src )
{
union
{
struct
{
int* pfn; //function,index
long delta; // offset,
}funcPtr;
TSrc uSrc;
}uMedia;
uMedia.uSrc = src;
return uMedia.funcPtr.pfn;
}
typedef int ( __stdcall *StdCallFun)(int, int);
class CTestClass
{
public:
int m_nBase;
MemFunToStdCallThunk m_thunk;
int memFun( int m, int n )
{
int nSun = m_nBase + m + n;
printf("m=%d,n=%d,nSun=%d\n", m, n, nSun);
return 1234;
}
public:
CTestClass()
{
m_nBase = 10;
}
void Test()
{
printf("%x\n", &CTestClass::memFun);
m_thunk.Init(UnionCastType<DWORD_PTR>(&CTestClass::memFun), this );
StdCallFun fun = (StdCallFun)m_thunk.GetCodeAddress();
assert( fun != NULL );
int ret = fun( 9, 3 );
printf("ret = %x\n", ret);
}
};
int main()
{
CTestClass test;
test.Test();
return 0;
}
EDIT:
Thanks to user786653, I get the right answer:
#include <assert.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
typedef char BYTE;
typedef int DWORD;
typedef int* DWORD_PTR;
typedef int* INT_PTR;
typedef bool BOOL;
typedef unsigned long ULONG;
typedef unsigned long* ULONG_PTR;
#define PtrToUlong(p) ((ULONG)(ULONG_PTR) (p) )
#define __stdcall __attribute__((__stdcall__))
struct MemFunToStdCallThunk
{
BYTE m_repairStack[10];
DWORD m_mov;
DWORD m_this;
BYTE m_jmp;
DWORD m_relproc;
void Init( DWORD_PTR proc, void* pThis )
{
printf("proc=%p\n", proc);
m_repairStack[0] = 0x83; //sub esp, 0x4
m_repairStack[1] = 0xec;
m_repairStack[2] = 0x04;
m_repairStack[3] = 0x8b; //mov eax,[esp + 0x4]
m_repairStack[4] = 0x44;
m_repairStack[5] = 0x24;
m_repairStack[6] = 0x04;
m_repairStack[7] = 0x89;//mov [esp], eax
m_repairStack[8] = 0x04;
m_repairStack[9] = 0x24;
m_mov = 0x042444C7; // mov dword ptr [esp+0x4],
m_this = PtrToUlong(pThis);
m_jmp = 0xe9; //jmp
m_relproc = (DWORD)proc - ((DWORD)this+sizeof(MemFunToStdCallThunk));
printf("m_relproc = %d\n", m_relproc);
//long page_size = sysconf(_SC_PAGE_SIZE);
//mprotect((void*)(PtrToUlong(this) & -page_size), 2*page_size, PROT_READ|PROT_WRITE|PROT_EXEC);
}
void* GetCodeAddress()
{
return this;
}
}__attribute__ ((packed));
template< typename TDst, typename TSrc >
TDst UnionCastType( TSrc src )
{
union
{
struct
{
int* pfn; //function or index
long delta; // offset
}funcPtr;
TSrc uSrc;
}uMedia;
uMedia.uSrc = src;
return uMedia.funcPtr.pfn;
}
typedef int ( __stdcall *StdCallFun)(int, int);
class CTestClass
{
public:
int m_nBase;
MemFunToStdCallThunk m_thunk;
int memFun( int m, int n )
{
printf("this=%p\n", this);
int nSun = m_nBase + m + n;
printf("m=%d,n=%d,nSun=%d\n", m, n, nSun);
return nSun;
}
public:
CTestClass()
{
m_nBase = 10;
}
void Test()
{
int (CTestClass::*abc)(int, int);
printf("sizeof(MemFunToStdCallThunk)=%d,sizeof(abc)=%d\n", sizeof(MemFunToStdCallThunk), sizeof(abc));
printf("memFun=%p\n", &CTestClass::memFun);
m_thunk.Init(UnionCastType<DWORD_PTR>(&CTestClass::memFun), this );
StdCallFun fun = (StdCallFun)m_thunk.GetCodeAddress();
assert( fun != NULL );
int ret = memFun(2, 3);
printf("ret 1= %d\n", ret);
ret = fun( 9, 3 );
printf("ret 2= %d\n", ret);
}
};
int main()
{
CTestClass test;
test.Test();
return 0;
}
Yes, but I wouldn't recommend it. It will (obviously) make your code a lot less portable and you're potentially opening a security hole if you're not careful.
You will need to make the code executable with mprotect(2). Something like mprotect(&thunk_struct, sizeof(struct _CallBackProcThunk), PROT_READ|PROT_WRITE|PROT_EXEC).
Also the normal GCC syntax for structure packing is struct S { /* ... */ } __attribute__ ((packed)) though newer versions might support the #pragma pack syntax.
You will probably also want to substitute DWORD with uint32_t from stdint.h and BYTE with uint8_t (or just stick a typedef in there).
EDIT:
From the man page on mprotect "[..]addr must be aligned to a page boundary". You should check the return value. Try doing something like this instead:
long page_size = sysconf(_SC_PAGE_SIZE);
uintptr_t addr = ((uintptr_t)this) & -page_size;
if (mprotect((void*)addr, 2*page_size, PROT_READ|PROT_WRITE|PROT_EXEC)) {
perror("mprotect");
/* handle error */
}
The following calculation is wrong:
DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(MemFunToStdCallThunk)))
It's doing its calculations on int*'s.
(DWORD)proc - ((DWORD)this+sizeof(MemFunToStdCallThunk)
should be sufficient here.
A very ugly (non-portable etc. etc.), but small and self-contained example follows:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
struct thunk {
uint32_t mov;
uint32_t this_ptr;
uint8_t jmp;
uint32_t rel;
} __attribute__((packed));
class Test {
public:
virtual int foo(void) {
printf("foo! %p\n", (void*)this);
return 42;
}
};
int main()
{
Test test;
printf("%d\n", test.foo());
thunk t;
t.mov = 0x042444C7;
t.this_ptr = (uint32_t)&test;
t.jmp = 0xe9;
t.rel = ((uint32_t)(void*)&Test::foo) - ((uint32_t)&t + sizeof(thunk));
uint32_t addr = (uint32_t)&t;
long page_size = sysconf(_SC_PAGE_SIZE);
if (mprotect((void*)(addr & -page_size), 2*page_size, PROT_READ|PROT_WRITE|PROT_EXEC)) {
perror("mprotect");
return 1;
}
union {
void* p;
int (*foo)(int);
} u;
u.p = &t;
printf("%d\n", u.foo(0));
return 0;
}
A reasonable approach is something like this:
struct Foo {
void doit();
};
extern "C" {
void callback(void *handle) {
reinterpret_cast<Foo*>(handle)->doit();
}
}
The assembly of callback looks like this here (x64):
callback:
jmpq _ZN3Foo4doitEv
You can't pass pointer-to-member pointers to C callbacks directly, but there are portable tricks (i.e. not restricted to one target OS) that work very well.
The easiest way to do that is just to use a wrapper non-member function whose only purpose is to call your member function.
void wrapper()
{
object->callWhatever();
}
You can pass wrapper() as a function pointer.
See also for example Cast member function for create_pthread() call for how to handle cases where you get a void* parameter with the callback and want to use that to store (directly or not) a reference/pointer to the object you want to operate on.
I want to use class member functions as callbacks, I don't use libsigc, because it's slow. In ATL, we can use member function for C-style callback(http://www.codeproject.com/KB/cpp/SoloGenericCallBack.aspx), so can we implement c++ thunk in linux?
You probably can. However, there is no need to.
Most asynchronous APIs allow to pass a void* argument when registering for an asynchronous event. When the event gets reported this void* is reported as well and can be used to call a member function of an object. (Vague language because APIs like epoll_wait() don't actually call you back, where as pthread_create() does.).
I'm attempting to debug a stack overwrite (no pun intended) corruption problem with some c/c++ code in Visual Studio 2008.
When I compile the solution in win32 debug mode, I'm able to run the debugger and see a class get instantiated.
In the constructor call, we initialize some fixed length char[] member variables using memset.
If I use printf to print the memory location of the member variable, I get a value that is exactly 14 bytes past what the VS2008 watch/local var window reports as the memory location.
How does VS calculate what it thinks the memory address is of a variable?
Edit: I have compiled with Run Time Check options, and what I see is "Run-time Check Failure #2 - stack around variable 'varName' was corrupted.
Edit 3: Here is the abbreviated source
Header file:
//////////////////////////////////////////////////////////////////////
#include "commsock.h"
#include "buffer.h"
#include "BufferedReader.h"
#define AUTHTYPE_PASSKEY 1
#define AUTHTYPE_PAGER 2
#define AUTHTYPE_PASSWORD 3
#define AUTHTYPE_RADIUS 4
#define AUTHTYPE_INFOCARD_RO 5
#define AUTHTYPE_INFOCARD_CR 6
#define AUTHSTATE_NOT_LOGGED_IN 0
#define AUTHSTATE_IDENTIFIED 1
#define AUTHSTATE_AUTHENTICATED 2
#define AUTHSTATE_MAX_FAILS 32
#define AUTHSTATE_NO_LOG 65536
class PRClientSession {
public:
PRClientSession();
virtual ~PRClientSession();
void Reset();
BOOL BeginAuth(LPCSTR szUserID, LPCSTR szClientIP, LPCSTR szOtherUserID="");
BOOL CompleteAuth(LPCSTR szResponse);
BOOL Logoff();
BOOL DetachAsync();
BOOL Detach();
BOOL Attach(const char *authstr=NULL);
BOOL Connected(){return m_Socket.Connected();};
BOOL EncryptText(char *pPassword, char *pOut, char *pKey);
BOOL EncryptText(char *pPassword, char *pOut);
BOOL DecryptText(char *pPassword, char *pOut, char *pKey);
BOOL DecryptText(char *pPassword, char *pOut);
LPCSTR Error(LPCSTR szErr=NULL){if (szErr)strncpy_s(m_szError,szErr,sizeof(m_szError));return m_szError;};
LPCSTR Challenge(LPCSTR szChal=NULL){if (szChal)strncpy_s(m_szChallenge,szChal,sizeof(m_szChallenge));return m_szChallenge;};
LPCSTR UserID(LPCSTR szUID=NULL){if (szUID)strncpy_s(m_szUserID,szUID,sizeof(m_szUserID));return m_szUserID;};
LPCSTR SessionID(LPCSTR szSID=NULL){if (szSID)strncpy_s(m_szSessionID,szSID,sizeof(m_szSessionID));return m_szSessionID;};
LPCSTR SessionLogID(LPCSTR szSLID=NULL){if (szSLID)strncpy_s(m_szSessionLogID,szSLID,sizeof(m_szSessionLogID));return m_szSessionLogID;};
LPCSTR SessionStateID(LPCSTR szSSID=NULL){if (szSSID)strncpy_s(m_szSessionStateID,szSSID,sizeof(m_szSessionStateID));return m_szSessionStateID;};
int AuthType(int iType=-1){if (iType != -1)m_iAuthType = iType;return m_iAuthType;};
int SendRequest(LPCSTR szType, ...);
int Write(char *szBuf, int iLen=-1);
int Read(char *szBuf, int iLen, int iTimeout=-1);
BOOL ReadResponse(int iBlock=FALSE);
int ReadResponseTimeout(int iBlock) ;
BOOL GetField(LPCSTR szField, char *szBuf, int iLen, int total=-1);
BOOL GetField(LPCSTR szField, int &iBuf );
char *GetData(){return m_pData;};
int GetDataSize(){return m_iDataSize;}
char *GetMessage(){return m_pMessage;};
SOCKET getSocket(){return m_Socket.getSocket();}
private:
BOOL LoadConfig();
BOOL GetMsgField(LPCSTR szIn, LPCSTR szSrch, char *szBuf, int iLen, int total=-1);
int ReadMessage( char *buf, int len, const char *terminator = NULL );
public:
bool sendCommand(char* szCommand, char *szArglist);
LPCSTR ReplyMessage(){ return m_szReplyMessage; }
int IsRadiusChallenge(){ return m_iIsRadiusChallenge; }
static char PRIISMS_USER_TAG[];
static char DEST_USER_TAG[];
static char RADIUS_USER_TAG[];
static char RADIUS_PASSWORD_TAG[];
static char GENERATE_LOGIN[];
static char VALIDATE_LOGIN[];
static char PR_RADIUS_GENERATED[];
private:
BOOL doConnect();
// Response reader vars...
char *m_pMessage;
char *m_pData;
Buffer m_Buf;
BOOL m_bLoaded;
BufferedReader m_reader;
Buffer m_ReqBuf;
CCommSocket m_Socket;
char m_szServer[128];
int m_iServerPort;
char m_szError[128];
int m_iAuthState;
int m_iDataSize;
int m_iAuthType;
char m_szChallenge[1024];
char m_szUserID[64];
char m_szSessionID[64];
char m_szSessionLogID[64];
char m_szSessionStateID[64];
char m_szSessionSecret[16];
long m_RequestID;
int m_iIsRadiusChallenge;
char m_szReplyMessage[1024];
};
and the source code with constructor...
#include "stdafx.h"
#include "PRclntsn.h"
#include "iondes.h"
#include "prsystemparameters.h"
#include "prsessionlog.h"
#include "util.h"
#include "PRClntSn.h"
#include <string>
using namespace std;
#define LoadConfigFailedMsg "Unable to retrieve database configuration entries."
#ifndef AUTH_TYPE_RADIUS
#define AUTH_TYPE_RADIUS 4
#endif
//------------------------------------------------------------------------------------
// initialize static members
char PRClientSession::DEST_USER_TAG[] = "DEST_USER=";
char PRClientSession::PRIISMS_USER_TAG[] = "PRIISMS_USER=";
char PRClientSession::RADIUS_USER_TAG[] = "RADIUS_USER=";
char PRClientSession::RADIUS_PASSWORD_TAG[] = "RADIUS_PASSWORD=";
char PRClientSession::GENERATE_LOGIN[] = "GENERATE_LOGIN";
char PRClientSession::VALIDATE_LOGIN[] = "VALIDATE_LOGIN";
char PRClientSession::PR_RADIUS_GENERATED[] = "PR_RADIUS_GENERATED";
PRClientSession::PRClientSession()
{
Reset();
}
void PRClientSession::Reset()
{
//LOG4CXX_TRACE(mainlogger, CLASS_METHOD_TAG);
printf("m_szServer mem location: %p", (void *)&m_szServer);
memset(m_szServer, 0, sizeof(m_szServer));
m_iServerPort = 0;
memset(m_szError, 0, sizeof(m_szError));
m_iAuthState = 0;
m_iDataSize = 0;
m_iAuthType = 0;
memset(m_szChallenge, 0, sizeof(m_szChallenge));
memset(m_szUserID, 0, sizeof(m_szUserID));
memset(m_szSessionID, 0, sizeof(m_szSessionID));
memset(m_szSessionLogID, 0, sizeof(m_szSessionLogID));
memset(m_szSessionStateID, 0, sizeof(m_szSessionStateID));
memset(m_szSessionSecret, 0, sizeof(m_szSessionSecret) );
// memset(m_szReadBuf, 0, sizeof(m_szReadBuf));
m_RequestID = 0;
m_iIsRadiusChallenge = 0;
memset(m_szReplyMessage, 0, sizeof(m_szReplyMessage));
m_reader.setComm(&m_Socket);
}
Output of printf:
m_szServer mem location: 01427308
Visual Studio 2008 Locals window
m`_szServer 0x014272fa "" char [128]`
It's off by 14... and when the code actually runs the very first memset, it does so starting with address 7308, not 72fa. It basically tramples contiguous memory regions (and thus, variables)
The debugger inserts extra space. This space is set to a pre-defined value, and if it's changed, the debugger knows that something has gone wrong. It's normal for compilers and debuggers to insert space that serves no apparent purpose.
If you have used a fixed-size char[], that immediately signals to me as a problem, since there's no explicit encapsulation. You could use a custom array type (such as boost::array) to write your own buffer overrun detection code. If you throw at this time, you will get a stack trace.
This is (probably) not going to fix your bug, but I would try to avoid C-style arrays if you can.
Unless you have some compelling reason to use the fixed-length arrays, replace them with std::vector which will be filled with 0x00 by default.
so instead of
const size_t MYCLASS::BUFLEN(16);
class myClass {
public:
myClass()
{
memset(buffer, 0, BUFLEN);
}
private:
static const size_t BUFLEN;
char buffer[BUFLEN];
};
you have
const size_t MYCLASS::BUFLEN(16);
class myClass {
public:
myClass() : buffer(BUFLEN)
{
memset(buffer, 0, BUFLEN);
}
private:
static const size_t BUFLEN;
std::vector<char> buffer;
};