Open an offline chart in MQL4 using [user32.dll] and [kernel32.dll] Win-API calls - winapi

I have the following code to open an offline chart in metatrader 4.
But all I could get it to do is open the offline file list. I am not
very familiar with windows programming so can someone tell me what am I doing wrong ?
#import "user32.dll"
int PostMessageA( int hWnd, int Msg, int wParam, int lParam );
int SendMessageA( int hWnd, int Msg, int wParam, int lParam );
int GetAncestor( int hWnd, int gaFlags );
int GetLastActivePopup( int hWnd );
int GetDlgItem( int hDlg, int nIDDlgItem );
#import
#import "kernel32.dll"
int FindFirstFileA( string Path, int& Answer[] );
bool FindNextFileA( int handle, int& Answer[] );
bool FindClose( int handle );
#import
#define WM_COMMAND 0x0111
#define WM_KEYDOWN 0x0100
#define VK_DOWN 0x28
#define BM_CLICK 0x00F5
#define GA_ROOT 2
#define PAUSE 100
string BuffToString( int& Buffer[] )
{
string Str = "";
int Pos = 11;
while ( Pos < 75 ) {
while ( Buffer[Pos] != 0 ) {
Str = Str + CharToStr( Buffer[Pos] & 0xFF );
Buffer[Pos] /= 0x100;
}
Pos++;
}
return( Str );
}
int GetChartPos( string FileName )
{
int Buffer[79];
int Pos = 0;
int handle = FindFirstFileA( TerminalPath() + "\history\\" + AccountServer() + "\\*.hst", Buffer );
if ( BuffToString( Buffer ) != FileName ) {
Pos++;
while ( FindNextFileA( handle, Buffer ) ) {
if ( BuffToString( Buffer ) == FileName )
break;
Pos++;
}
}
if ( handle > 0 )
FindClose( handle );
return( Pos );
}
int OpenOfflineList()
{
int hwnd = WindowHandle( Symbol(), Period() );
hwnd = GetAncestor( hwnd, GA_ROOT );
SendMessageA( hwnd, WM_COMMAND, 33053, 0 );
Sleep( PAUSE );
hwnd = GetLastActivePopup( hwnd );
return( hwnd );
}
void OpenOfflineChartbyNum( int ChartPos )
{
int hwnd1 = OpenOfflineList();
int hwnd2 = GetDlgItem( hwnd1, 1 );
hwnd1 = GetDlgItem( hwnd1, 0x487 );
while ( ChartPos >= 0 ) {
SendMessageA( hwnd1, WM_KEYDOWN, VK_DOWN, 0 );
ChartPos--;
}
Sleep( PAUSE );
SendMessageA( hwnd2, BM_CLICK, 0, 0 );
return;
}
void OpenOfflineChart( string Symb, int period )
{
OpenOfflineChartbyNum( GetChartPos( Symb + period + ".hst" ) );
return;
}
int init()
{
OpenOfflineChart( "AUDUSD", 120 );
return;
}

I'm not an expert in WinApi and the question is obviously old, but it is still relevant. So the problem is that you use FindFirstFileA(), which uses ANSI strings, but after 600 release MT4 uses Unicode, so you need to use FindFirstFileW() instead. Also, instead of SendMessage() you should use PostMessage() (pls don't ask me why). So here is the working code:
#import "user32.dll"
int PostMessageA( int hWnd, int Msg, int wParam, int lParam );
int SendMessageA( int hWnd, int Msg, int wParam, int lParam );
int GetAncestor( int hWnd, int gaFlags );
int GetLastActivePopup( int hWnd );
int GetDlgItem( int hDlg, int nIDDlgItem );
#import
#import "kernel32.dll"
int FindFirstFileW( string Path, ushort &Answer[] );
bool FindNextFileW( int handle, ushort &Answer[] );
bool FindClose( int handle );
#import
#define WM_COMMAND 0x0111
#define WM_KEYDOWN 0x0100
#define VK_DOWN 0x28
#define BM_CLICK 0x00F5
#define GA_ROOT 2
#define PAUSE 100
string BuffToString( int& Buffer[] )
{
string Str = "";
int Pos = 11;
while ( Pos < 75 ) {
while ( Buffer[Pos] != 0 ) {
Str = Str + CharToStr( Buffer[Pos] & 0xFF );
Buffer[Pos] /= 0x100;
}
Pos++;
}
return( Str );
}
int GetChartPos( string FileName )
{
ushort Buffer[300];
int Pos=-1;
string path = TerminalInfoString( TERMINAL_DATA_PATH ) + "\\history\\" + AccountInfoString( ACCOUNT_SERVER ) + "\\*.hst";
int handle = FindFirstFileW( path, Buffer );
string name = ShortArrayToString( Buffer, 22, 152 );
Pos++;
if(name!=FileName)
{
ArrayInitialize(Buffer,0);
while(FindNextFileW(handle,Buffer))
{
name=ShortArrayToString(Buffer,22,152);
Pos++;
if(name==FileName)
{
break;
}
ArrayInitialize(Buffer,0);
}
}
if(handle>0)
FindClose(handle);
return(Pos);
}
int OpenOfflineList()
{
int hwnd = WindowHandle( Symbol(), Period() );
hwnd = GetAncestor( hwnd, GA_ROOT );
PostMessageA( hwnd, WM_COMMAND, 33053, 0 );
Sleep( PAUSE );
hwnd = GetLastActivePopup( hwnd );
return( hwnd );
}
void OpenOfflineChartbyNum( int ChartPos )
{
int hwnd1 = OpenOfflineList();
int hwnd2 = GetDlgItem( hwnd1, 1 );
hwnd1 = GetDlgItem( hwnd1, 0x487 );
while ( ChartPos >= 0 ) {
PostMessageA( hwnd1, WM_KEYDOWN, VK_DOWN, 0 );
ChartPos--;
}
Sleep( PAUSE );
PostMessageA( hwnd2, BM_CLICK, 0, 0 );
return;
}
void OpenOfflineChart( string Symb, int period )
{
OpenOfflineChartbyNum( GetChartPos( Symb + period + ".hst" ) );
return;
}
int init()
{
OpenOfflineChart( "AUDUSD", 120 );
return;
}
But now ( Build 970+ ) it is much simpler to go with ChartOpen( "AUDUSD", 2 ); So, if you don't use custom names for symbols, you can replace all that with just one line of code.

Related

WaitForMultipleObjects starving out

When I call WaitForMultipleObjects on a set of objects and another thread is waiting for a single object of that set with WaitForSingleObject, this thread is preferred and the thread calling WaitForMultipleObjects never gets CPU-time.
Look at this source:
#include <windows.h>
#include <cstdio>
#include <unordered_map>
HANDLE hEvt,
hSema;
bool volatile fReleased;
DWORD WINAPI LockAndReleaseThread( LPVOID lpvThreadParam );
int main()
{
int const NTHREADS = 10;
HANDLE ahWait[2];
ahWait[0] = ::hEvt = CreateEvent( NULL, FALSE, TRUE, NULL );
ahWait[1] = ::hSema = CreateSemaphore( NULL, 0, 1, NULL );
fReleased = false;
for( int i = 0; i < NTHREADS; i++ )
CreateThread( NULL, 0, LockAndReleaseThread, NULL, 0, NULL );
for( ; ; )
WaitForMultipleObjects( 2, ahWait, TRUE, INFINITE ),
std::printf( "main thread is holding lock and received signal\n" ),
::fReleased = false,
SetEvent( ::hEvt );
return 0;
}
char GetID();
DWORD WINAPI LockAndReleaseThread( LPVOID lpvThreadParam )
{
for( ; ; )
{
WaitForSingleObject( ::hEvt, INFINITE );
std::printf( "spawned thread with id %c is holding lock\n", (char)GetID() );
if( !::fReleased )
ReleaseSemaphore( ::hSema, 1, NULL ),
::fReleased = true;
Sleep( 1000 );
SetEvent( ::hEvt );
}
return 0;
}
char GetID()
{
static std::unordered_map<DWORD, char> mapTIDsToIDs;
static char nextId = 'A';
DWORD dwThreadId;
if( mapTIDsToIDs.find( dwThreadId = GetCurrentThreadId() ) == mapTIDsToIDs.end() )
return mapTIDsToIDs[dwThreadId] = nextId++;
else
return mapTIDsToIDs[dwThreadId];
}
In this code, the main thread never gets CPU-time if NTHREADS is >= 2.
I don't expect WaitForSingleObject or WaitForMultipleObjects to be completely fair, but this looks to me like a conceptual flaw.
If I replace
WaitForMultipleObjects( 2, ahWait, TRUE, INFINITE ),
with
WaitForSingleObject( ::hSema, INFINITE ),
WaitForSingleObject( ::hEvt, INFINITE ),
the main-thread gets CPU-time.
[EDIT1]
The bug hasn't been fixed even with Windows 10.
And doing WaitForMultipleObjects instead of WaitForSingleObject doesn't change the situation.
And the spawned threads get CPU-time in a round-robin-fashion and it doesn't matter if there is a Sleep or not and if I'm doing WaitforSingleObject or WaitForMultipleObjects.
The tip with using a critical section here doesn't help me.
I've developed a condition-variable where this constellation can happen if multiple threads are locking the condvar and one is waiting for the condvar to be singalled:
#include <windows.h>
#include <cassert>
#include <exception>
#include <intrin.h>
#if !defined(NDEBUG)
#define ONDEBUG(expr) (expr)
#else
#define ONDEBUG(expr) ((void)0)
#endif
inline
DWORD FastGetCurrentThreadId()
{
#if defined(_M_IX86)
return __readfsdword( 0x24 );
#elif defined(_M_AMD64)
return *(DWORD *)(__readgsqword( 0x30 ) + 0x48);
#endif
}
class Exception : public std::exception
{
};
class ResourceException : public Exception
{
};
template <typename TYPE>
inline
TYPE AutoThrowResourceException( TYPE t )
{
if( !t )
throw ResourceException();
return t;
}
class XHANDLE
{
public:
XHANDLE( HANDLE h = NULL );
~XHANDLE();
void CloseHandle();
public:
HANDLE h;
};
inline
XHANDLE::XHANDLE( HANDLE h )
{
this->h = h;
}
inline
XHANDLE::~XHANDLE()
{
BOOL fClosed;
if( h && h != INVALID_HANDLE_VALUE )
fClosed = ::CloseHandle( h ),
assert(fClosed);
}
inline
void XHANDLE::CloseHandle()
{
::CloseHandle( h );
h = NULL;
}
class CondVar
{
public:
CondVar();
~CondVar();
void Enter();
void Wait();
void Release();
void ReleaseAll();
void Leave();
private:
LONGLONG volatile m_llOwnersAndWaiters;
DWORD volatile m_dwRecursionCount;
DWORD volatile m_dwOwningThreadId;
XHANDLE m_xhEvtEnter,
m_xhSemRelease;
private:
static
DWORD Owners( LONGLONG llOwnersAndWaiters )
{
return (DWORD)llOwnersAndWaiters;
}
static
DWORD Waiters( LONGLONG llOwnersAndWaiters )
{
return (DWORD)((DWORDLONG)llOwnersAndWaiters >> 32);
}
};
CondVar::CondVar() :
m_xhEvtEnter( AutoThrowResourceException( ::CreateEvent( NULL, FALSE, FALSE, NULL ) ) ),
m_xhSemRelease( AutoThrowResourceException( ::CreateSemaphore( NULL, 0, 0x7FFFFFFF, NULL ) ) )
{
m_llOwnersAndWaiters = 0;
m_dwRecursionCount = 0;
m_dwOwningThreadId = 0;
}
CondVar::~CondVar()
{
}
void CondVar::Enter()
{
if( m_dwOwningThreadId == FastGetCurrentThreadId() )
{
m_dwRecursionCount++;
return;
}
LONGLONG llOwnersAndWaiters = ::InterlockedIncrement64( &m_llOwnersAndWaiters );
assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ));
if( (Owners( llOwnersAndWaiters ) - Waiters( llOwnersAndWaiters )) > 1 )
for( ; ::WaitForSingleObject( m_xhEvtEnter.h, INFINITE ) != WAIT_OBJECT_0; assert(false) );
ONDEBUG(llOwnersAndWaiters = m_llOwnersAndWaiters);
assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == 0);
m_dwOwningThreadId = FastGetCurrentThreadId();
}
void CondVar::Wait()
{
LONGLONG llOwnersAndWaiters;
DWORD dwSavedRecusionCount;
ONDEBUG(llOwnersAndWaiters = m_llOwnersAndWaiters);
assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == FastGetCurrentThreadId());
m_dwOwningThreadId = 0;
dwSavedRecusionCount = m_dwRecursionCount;
m_dwRecursionCount = 0;
llOwnersAndWaiters = ::InterlockedAdd64( &m_llOwnersAndWaiters, 0x100000000 );
if( Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) )
for( ; !::SetEvent( m_xhEvtEnter.h ); assert(false) );
HANDLE ahWait[2] = { m_xhEvtEnter.h, m_xhSemRelease.h };
for( ; ::WaitForMultipleObjects( 2, ahWait, TRUE, INFINITE ) != WAIT_OBJECT_0; assert(false) );
ONDEBUG(llOwnersAndWaiters = m_llOwnersAndWaiters);
assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == 0);
m_dwOwningThreadId = FastGetCurrentThreadId();
m_dwRecursionCount = dwSavedRecusionCount;
}
void CondVar::Release()
{
LONGLONG llOwnersAndWaiters,
llOwnersAndWaitersPrevOrChanged;
for( llOwnersAndWaiters = m_llOwnersAndWaiters; Waiters( llOwnersAndWaiters ); llOwnersAndWaiters = llOwnersAndWaitersPrevOrChanged )
{
assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == FastGetCurrentThreadId());
if( (llOwnersAndWaitersPrevOrChanged = ::InterlockedCompareExchange64( &m_llOwnersAndWaiters, llOwnersAndWaiters - 0x100000000, llOwnersAndWaiters )) == llOwnersAndWaiters )
{
for (; !::ReleaseSemaphore( m_xhSemRelease.h, 1, NULL ); assert( false ));
break;
}
}
}
void CondVar::ReleaseAll()
{
LONGLONG llOwnersAndWaiters,
llOwnersAndWaitersPrevOrChanged;
for( llOwnersAndWaiters = m_llOwnersAndWaiters; Waiters( llOwnersAndWaiters ); llOwnersAndWaiters = llOwnersAndWaitersPrevOrChanged )
{
assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == FastGetCurrentThreadId());
if( (llOwnersAndWaitersPrevOrChanged = ::InterlockedCompareExchange64( &m_llOwnersAndWaiters, llOwnersAndWaiters & 0x0FFFFFFFF, llOwnersAndWaiters )) == llOwnersAndWaiters )
{
for (; !::ReleaseSemaphore( m_xhSemRelease.h, (LONG)Waiters( llOwnersAndWaiters ), NULL ); assert( false ));
break;
}
}
}
void CondVar::Leave()
{
LONGLONG llOwnersAndWaiters;
LONG lRecursionCount;
ONDEBUG(llOwnersAndWaiters = m_llOwnersAndWaiters);
assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == FastGetCurrentThreadId());
if( (lRecursionCount = m_dwRecursionCount) != 0 )
{
m_dwRecursionCount = lRecursionCount - 1;
return;
}
m_dwOwningThreadId = 0;
llOwnersAndWaiters = ::InterlockedDecrement64( &m_llOwnersAndWaiters );
if( Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) )
for( ; !::SetEvent( m_xhEvtEnter.h ); assert(false) );
}
/* ---------------------------- unit-test ---------------------------- */
#include <cstdio>
#include <cstdlib>
#include <deque>
#include <map>
DWORD WINAPI ThreadFunc( LPVOID lpvThreadParam );
using namespace std;
struct ThreadResult
{
DWORD dwThreadId;
DWORD dwCounter;
};
CondVar cv;
deque<ThreadResult> dqtr;
bool volatile fStop = false;
int main()
{
int const NTHREADS = 16;
HANDLE ahThreads[NTHREADS];
int i;
std::map<DWORD, DWORD> mTRs;
for( i = 0; i < NTHREADS; i++ )
ahThreads[i] = CreateThread( NULL, 0, ThreadFunc, NULL, 0, NULL );
for( i = 0; i < 10000; i++ )
{
ThreadResult tr;
::cv.Enter();
if( ::dqtr.empty() )
::cv.Wait();
tr = ::dqtr.front();
::dqtr.pop_front();
::cv.Leave();
printf( "Thread: %08X - Number: %d\n", (unsigned)tr.dwThreadId, (unsigned)tr.dwCounter );
if( mTRs.find( tr.dwThreadId ) == mTRs.end() )
mTRs[tr.dwThreadId] = tr.dwCounter;
else
assert((mTRs[tr.dwThreadId] + 1) == tr.dwCounter),
mTRs[tr.dwThreadId] = tr.dwCounter;
}
for( ::fStop = true, i = 0; i < NTHREADS; i++ )
WaitForSingleObject( ahThreads[i], INFINITE );
return 0;
}
DWORD WINAPI ThreadFunc( LPVOID lpvThreadParam )
{
ThreadResult tr;
tr.dwThreadId = FastGetCurrentThreadId();
tr.dwCounter = 0;
for( ; !::fStop; tr.dwCounter++ )
{
::cv.Enter();
::dqtr.push_back( tr );
::cv.Release();
Sleep( 100 );
::cv.Leave();
}
return 0;
}
I can reproduce your problem, and it is bizarre.
For what it's worth, I found that I could work around it by using WaitForMultipleObjects() exclusively, and including a per-thread dummy object, i.e.,
DWORD WINAPI LockAndReleaseThread( LPVOID lpvThreadParam )
{
HANDLE ahWait[2];
ahWait[0] = ::hEvt;
ahWait[1] = CreateEvent(NULL, TRUE, TRUE, NULL);
for( ; ; )
{
WaitForMultipleObjects(2, ahWait, TRUE, INFINITE );
std::printf( "spawned thread with id %c is holding lock\n", (char)GetID() );
if( !::fReleased )
ReleaseSemaphore( ::hSema, 1, NULL ),
::fReleased = true;
Sleep( 1000 );
SetEvent( ::hEvt );
}
return 0;
}
This does not seem to work if the child threads share a single dummy object, unless the parent thread is also waiting on that object. So while it seems to work in this case, it is likely to be a fragile approach.
Personally at this point I'd be looking for another solution, but in your scenario I wouldn't really know where to start. (Off the top of my head, I'm thinking a linked list of threads and perhaps some lock-free queues.) On the other hand, since you're reading the thread ID directly from the undocumented process environment block, reliability and forwards compatibility clearly isn't a major issue for you, so perhaps this workaround will be sufficient. :-)
You should also note that Windows does have its own implementation of condition variables. I'm not sure why you're rolling your own here.

Ruby implementation Win32API get mouse scroll/wheel input

I'm trying to use win32api on user32.dll to get mouse wheel input.
I tried the following:
#state = Win32API.new('user32','DefWindowProc',['i'],'i')
p #state.call(0x0800)
But it keeps returning 0 no matter what I do with my mouse wheel.
I thought something is wrong with my mouse, so I tried to do:
#state = Win32API.new('user32','GetKeyState',['L'],'L')
p #state.call(0x01) #left key
p #state.call(0x02) #right key
It worked, so I'm not sure what's wrong with the mouse wheel.
Regards
Edit 2/20/2014:
I tried to do the following:
GetActiveWindow = Win32API.new('user32','GetForegroundWindow','','i')
DefWindowProc = Win32API.new('user32','DefWindowProc','iiii','i')
DefWindowProc.call(GetActiveWindow.call,
Then, I got stuck with the UINT / Message (2nd argument).
Edit 2/20/2014:
I found the WM_MOUSEWHEEL message
Latest revision:
GetActiveWindow = Win32API.new('user32','GetForegroundWindow','','i')
DefWindowProc = Win32API.new('user32','DefWindowProc','iiii','i')
#WM_MOUSEWHEEL message 0x020A
DefWindowProc.call(GetActiveWindow.call, 0x020A,
Now I just need to figure out the 3rd and 4th argument.
As I don't know Ruby, I will give here a solution in C/C++ using only standard Win32 APIs. If you can call Win32 APIs from Ruby, that will work.
Note that the solution involves "callback" and "pointer". I know that's possible in Python (for example, with the ctypes module) and hope that same thing is possible with Ruby.
Register a Window Class and create a Window from that class, the window will be a Message-Only Window, so it will be invisible, with no GUI.
Use the RegisterRawInputDevices API to ask for raw events from mouse devices.
Set up a message loop, with the standard GetMessage/DispatchMessage combo.
Process the sent WM_INPUT message in your Window Procedure
4.1. Allocate memory for the raw datas
4.2. Retrieve the raw datas
4.3. Filter for mouse event and wheel datas
4.4. Process (I just print the wheel delta)
4.5. Free allocated memory.
Below, full source code. Build with VS2012 Express on Windows 7.
#include <Windows.h>
#include <stdio.h>
LRESULT CALLBACK MyWindowProc( HWND, UINT, WPARAM, LPARAM );
int main( void ) {
WNDCLASS WndClass;
memset( &WndClass, 0, sizeof( WndClass ) );
WndClass.hInstance = GetModuleHandle( NULL );
WndClass.lpszClassName = L"MyRawInputClass";
WndClass.lpfnWndProc = MyWindowProc;
RegisterClass( &WndClass );
HWND hWnd = CreateWindow( WndClass.lpszClassName, NULL, 0, 0, 0, 0, 0,
HWND_MESSAGE, 0, WndClass.hInstance, 0 );
RAWINPUTDEVICE RawInputDevice;
RawInputDevice.usUsagePage = 0x01; // Generic Desktop Controls
RawInputDevice.usUsage = 0x02; // Mouse
RawInputDevice.dwFlags = RIDEV_INPUTSINK;
RawInputDevice.hwndTarget = hWnd;
BOOL bWin32Success = RegisterRawInputDevices( &RawInputDevice, 1,
static_cast<UINT>( sizeof( RAWINPUTHEADER ) ) );
BOOL bRet;
MSG msg;
while( ( bRet = GetMessage( &msg, hWnd, 0, 0 ) ) != 0 ) {
if (bRet != -1) {
DispatchMessage(&msg);
}
}
// NO GUI, UNREACHABLE
DestroyWindow( hWnd );
UnregisterClass( WndClass.lpszClassName, WndClass.hInstance );
return 0;
}
LRESULT CALLBACK MyWindowProc( HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam ) {
switch ( uiMsg ) {
case WM_INPUT: {
UINT dwSize;
HRAWINPUT hRawInput = reinterpret_cast<HRAWINPUT>( lParam );
UINT uiRetCode = GetRawInputData( hRawInput, RID_INPUT, NULL, &dwSize,
static_cast<UINT>( sizeof( RAWINPUTHEADER ) ) );
if ( uiRetCode != 0xffffffff ) {
LPBYTE lpb = new BYTE[ dwSize ];
uiRetCode = GetRawInputData( hRawInput, RID_INPUT, lpb, &dwSize,
static_cast<UINT>( sizeof( RAWINPUTHEADER ) ) );
if ( uiRetCode > 0 ) {
RAWINPUT* praw = reinterpret_cast<RAWINPUT*>( lpb );
if ( praw->header.dwType == RIM_TYPEMOUSE ) {
if ( praw->data.mouse.usButtonFlags & RI_MOUSE_WHEEL ) {
signed int siDelta = static_cast<SHORT>( praw->data.mouse.usButtonData );
printf( "WHEEL EVENT: Delta = %d\n", siDelta );
}
}
}
delete[] lpb;
}
break;
} // WM_INPUT
default:
return DefWindowProc( hWnd, uiMsg, wParam, lParam );
}
return 0;
}

Win32 save text files from tabbed interface

I'm using MS visual c++ 2010 express to write a notepad program. I need to be able to save files from my tabbed interface ( each edit control is stored into a vector ). I can't seem to figure out how to save, depending on which edit control is visible to the user ( active tab ). Everything I've tried so far either doesn't save the contents or only saves from the first edit control. When the user adds a new file to the tabbed interface, it added that edit control to the back of the vector. This is my latest attempt:
std::vector<HWND>vect;
BOOL SaveTextFileFromEdit( HWND hEdit, LPCTSTR pszFileName )
{
HANDLE hFile;
BOOL bSuccess = FALSE;
hFile = CreateFile( pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile != INVALID_HANDLE_VALUE )
{
DWORD dwTextLength = GetWindowTextLength( hEdit );
if( dwTextLegth > 0 )
{
DWORD dwBufferSize = dwTextLength + 1;
LPSTR pszText = ( LPSTR )GlobalAlloc( GPTR, dwBufferSize );
if( pszText != NULL )
{
if( GetWindowText( hEdit, pszText, dwBufferSize ) )
{
DWORD dwWritten;
if( WriteFile( hFile, pszText, dwTextLength, &dwWritten, NULL ) )
bSuccess = TRUE;
}
GlobalFree( pszText );
}
}
CloseHandle( hFile );
}
return bSuccess;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
case WM_COMMAND:
switch( LOWORD( wParam ) )
{
case ID_FILE_SAVEAS:
{
OPENFILENAME ofn;
char szFileName[ MAX_PATH ] = "";
ZeroMemory( &ofn, sizeof( ofn ) );
ofn.lStructSize = sizeof( ofn );
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrDefExt = "txt";
ofn.flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
if( GetSaveFileName( &ofn ) )
{
HWND hEdit, hTabs;
hTabs = GetDlgItem( hwnd, IDC_MAIN_TAB );
int curTab = TabCtrl_GetCurSel( hTabs );
hEdit = GetDlgItem( hTabs, IDC_MAIN_EDIT );
// This is what i did have: hEdit = GetDlgItem( vect[ curTab ], IDC_MAIN_EDIT );
// Which wasn't saving anything
if( SaveTextFileFromEdit( hEdit, szFileName ) )
{
// EVERYTHING IS GOOD
}
}
}
break;
}
break;
}
return 0;
}
If the vector has each edit control's HWND then you don't need GetDlgItem at all: You already have the HWND that it would return.
hEdit = vect[ curTab ];

Find out if UNC path is pointing to local machine

Is there any simple way to tell if UNC path points to a local machine.
I found the following question SO
Is there any WIN32 API that will do the same?
#include <windows.h>
#include <WinSock.h>
#include <string>
#include <algorithm>
#pragma comment(lib, "wsock32.lib")
using namespace std;
std::wstring ExtractHostName( const std::wstring &share )
{
if (share.size() < 3 )
return L"";
size_t pos = share.find( L"\\", 2 );
wstring server = ( pos != string::npos ) ? share.substr( 2, pos - 2 ) : share.substr( 2 );
transform( server.begin(),server.end(), server.begin(), tolower);
return server;
}
bool IsIP( const std::wstring &server )
{
size_t invalid = server.find_first_not_of( L"0123456789." );
bool fIsIP = ( invalid == string::npos );
return fIsIP;
}
bool IsLocalIP( const std::wstring &ipToCheck )
{
if ( ipToCheck == L"127.0.0.1" )
return true;
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 1, 1 );
if ( WSAStartup( wVersionRequested, &wsaData ) != 0 )
return false;
bool fIsLocal = false;
char hostName[255];
if( gethostname ( hostName, sizeof(hostName)) == 0 )
{
PHOSTENT hostinfo;
if(( hostinfo = gethostbyname(hostName)) != NULL )
{
for (int i = 0; hostinfo->h_addr_list[i]; i++)
{
char *ip = inet_ntoa(*( struct in_addr *)hostinfo->h_addr_list[i]);
wchar_t wcIP[100]={0};
::MultiByteToWideChar(CP_ACP, 0, ip, -1, wcIP, _countof(wcIP));
if (ipToCheck == wcIP)
{
fIsLocal = true;
break;
}
}
}
}
WSACleanup();
return fIsLocal;
}
bool IsLocalHost( const std::wstring &server )
{
if (server == L"localhost")
return true;
bool fIsLocalHost = false;
wchar_t buffer[MAX_PATH]={0};
DWORD dwSize = _countof(buffer);
BOOL fRet = GetComputerName( buffer, &dwSize );
transform( buffer, buffer + dwSize, buffer, tolower);
fIsLocalHost = ( server == buffer );
return fIsLocalHost;
}
bool ShareIsLocal( const std::wstring &share )
{
wstring server = ExtractHostName( share );
bool fIsIp = IsIP( server );
if ( fIsIp )
return IsLocalIP( server );
else
return IsLocalHost( server );
}

WM_COMMAND message not being received by the Windows Procedure

I am creating a system tray program with a shortcut/context menu, but I can't seem to receive WM_COMMAND messages in the Windows Procedure. It simply doesn't send when I click the menu item, and I've been checking for ages whether I have set up the menu correctly.
Here is my code:
#include <Windows.h>
#include <stdio.h>
#include "resource.h"
#define WM_TRAYICON (WM_USER + 0x0001) //a custom message for the notification icon
HWND hwnd; //window handle
HINSTANCE hinst; //module handle
WNDCLASSEX wnd; //window class
MSG msg; //event message or notification
NOTIFYICONDATA nid; //notification icon object
HMENU cmenu;
MENUITEMINFO menuitem1;
MENUITEMINFO menuitem2;
CURSORINFO cursor;
LRESULT CALLBACK MainWProc ( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_COMMAND:
printf("asfd\r\n");
break;
case WM_CREATE:
printf("just created\r\n");
break;
case WM_TRAYICON:
switch( LOWORD(lParam) )
{
case WM_CONTEXTMENU:
GetCursorInfo( &cursor );
//printf("xPos: %d\r\nyPos = %d\r\n\r\n", xPos, yPos );
TrackPopupMenuEx( cmenu, TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_NOANIMATION | TPM_HORIZONTAL | TPM_VERTICAL, cursor.ptScreenPos.x, cursor.ptScreenPos.y, hwnd, NULL );
//DestroyMenu(
break;
}
break;
case WM_INITMENU:
printf("open menu\r\n");
break;
case WM_DESTROY:
//clean things up
Shell_NotifyIcon( NIM_DELETE, &nid );
break;
default:
break;
}
return DefWindowProc( hwnd, uMsg, wParam, lParam );
}
void main()
{
int result;
hinst = GetModuleHandle( NULL );
cursor.cbSize = sizeof( cursor );
memset( &wnd, 0, sizeof( wnd ) );
wnd.cbSize = sizeof( wnd );
wnd.lpszClassName = "MainWClass";
wnd.lpfnWndProc = MainWProc;
wnd.hInstance = hinst;
result = RegisterClassEx( &wnd );
hwnd = CreateWindowEx
(
0, //extended styles
wnd.lpszClassName, //class name
"Main Window", //window name
WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL | WS_MINIMIZEBOX, //style tags
CW_USEDEFAULT, //horizontal position
CW_USEDEFAULT, //vertical position
CW_USEDEFAULT, //width
CW_USEDEFAULT, //height
(HWND) NULL, //parent window
(HMENU) NULL, //class menu
(HINSTANCE) wnd.hInstance, //some HINSTANCE pointer
NULL //Create Window Data?
);
if( !hwnd )
{
printf("CreateWindowEx failed: %d\n", GetLastError() );
Sleep( INFINITE );
}
nid.cbSize = sizeof( nid );
nid.hWnd = hwnd;
nid.uID = 1;
nid.uVersion = NOTIFYICON_VERSION_4;
nid.uCallbackMessage = WM_TRAYICON;
nid.hIcon = LoadIcon( hinst, MAKEINTRESOURCE( IDI_ICON1 ) );
strcpy( nid.szTip, "My Tooltip!" );
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_SHOWTIP;
cmenu = CreatePopupMenu();
menuitem1.cbSize = sizeof( menuitem1 );
menuitem1.fMask = MIIM_TYPE;
menuitem1.fType = MFT_STRING;
menuitem1.hSubMenu = NULL;
//menuitem1.cch = ;
menuitem1.dwTypeData = "Open a world of wonder!";
InsertMenuItem( cmenu, 0, true, &menuitem1 );
if( ! Shell_NotifyIcon( NIM_ADD, &nid ) )
{
printf("Shell_NotifyIcon( NIM_ADD, &nid ) failed.\r\n");
Sleep( INFINITE );
}
if( ! Shell_NotifyIcon( NIM_SETVERSION, &nid ) )
{
printf("Shell_NotifyIcon( NIM_SETVERSION, &nid ) failed.\r\n");
Sleep( INFINITE );
}
UpdateWindow( hwnd );
for( ; ; )
{
if( GetMessage(&msg, hwnd, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
Um, you passed the TPM_RETURNCMD flag which means "don't post a WM_COMMAND message. Just return the value you would have posted."

Resources