Wednesday, 03 June 2020
Monday, 04 May 2020
C++ Task Parallel Library (TPL)
Příspěvek nejen pro ty, kteří se od března do června nedočkají kvůli COVIDu C++ kurzu. Kdyby se někdo nudil, nebo mu lezly na mozek ty neustále se opakující zprávy o Babišovi, rouškách a další variace na mem "všichni tady chcípnem, když ne na COVID, tak na sucho", dovolím si upozornit na jednu poměrně čerstvou (neoptimalizovaná alfa) asynchronní švestičku ze své zahrádky. Švestičky i optimisticky naznačují roční období, kdy se snad uvidíme, jestliže macecha příroda nereleasne implementaci zmutované specifikace COVID++.
RStein.AsyncCpp používající coroutine z C++ 20 je knihovna, ve které se rychle zorientuje každý, kdo zná Task Parallel Library (.NET, C#).
V knihovně najdete nejen:
Task (tedy něco jako std:future) - tásky jsou narozdíl od knihovny cppcoro, kterou asi znáte, 'hot' - tedy přes TaskFactory je Task rovnou nastartován a naschedulován k vyřízení.
Task má API, které čekáte. A narozdíl od std::future má metodu ContinueWith (then).
Task se dá "co_awaitovat", protože podporuje concept "Awaiter". A můžete ho samozřejmě použít jako návratovou hodnotu z coroutine metody ("coroutine promise type").
Dále jsou v knihovně jednoduché metody pro vytvoření dokončeného Tasku z předpřipravené hodnoty (TaskFromResult), z výjimky (TaskFromException), nebo lze vrátit Task ve stavu Canceled (TaskFromCanceled).
Jednoduché DataFlow. ("flat", "fork-join" a a další typy).
Kombinátory pro Task:
WaitAll.
WaitAny.
TaskCompletionSouce - std:: promise bez těch otravných věcí, které určitě znáte sami.
Funkcionální kompozice tasku.
Fbind (alias bind, SelectMany, flatMap, mapMany)
Fmap (map, Select)
Asynchronní primitivy.
AsyncSemaphore.
Kooperativní storno pomocí tříd CancellationTokenSource a a CancellationToken.
AsyncProducerConsumerCollection.
Více zde:
https://github.com/renestein/Rstein.AsyncCpp
-------------------------------------------------------------------------------
Dear friends/followers,
maybe the result of my experimentation with coroutines may be interesting for someone else.
The RStein.AsyncCpp library (early unoptimized alpha) is a set of types that should be familiar for anyone who knows the Task Parallel Library (TPL) for .NET (C#). In addition, this library contains simple DataFlow, functional combinators for the Task class, useful async primitives (AsyncSemaphore, AsyncProducerConsumerCollection, CancellationToken, CancellationTokenSource ...).
The library is my playground for testing coroutine support in C++.
The library supports compilation in the VS 2019. Support for other compilers is planned.
More info.
https://github.com/renestein/Rstein.AsyncCpp
Monday, 04 May 2020 09:24:33 (Central Europe Standard Time, UTC+01:00)
C++ | Nativní kód
Monday, 24 May 2010
Omezení pro argumenty šablony (template) v C++ napodobující “where“ omezení pro generické argumenty v C#
Tento článek je hlavně reakce na stížnost, kterou měl kolega-vývojář z firmy, kde vývojáři použivají C++ i C#. Stížnost byla zaměřena na to, že na rozdíl od C# není možné v C++ zkontrolovat v době kompilace, zda argument předaný do šablony implementuje vyžadované rozhraní, nebo je potomkem námi vyžadované třídy.
Zdůrazním hned v úvodu, že v článku mluvím o “klasickém” C++ a ne o jeho .Net dialektu C++/CLI, který podporuje šablony i generiku.
Příkladem v C# může být následující třída Collection, která vyžaduje, aby za generický parametr T byla dosazena třída podporující rozhraní IBusinessObject. Podrobněji jsem podobnou kolekci už v éře př. lq. (čteme před Linqem) popisoval třeba zde.
public class Collection<T> : where T : IBusinessObject
{
}
Šablony v C++ jsou i přes povrchní syntaktickou podobnost zcela odlišnou jazykovou konstrukcí při srovnání s generikou v C#, a protože jsou “uzavřené šablonové typy” generovány již v době kompilace, kompilátor sám zajistí, že typ předaný do šablony podporuje všechny námi vyžadované metody, vlastnosti a operátory. Přesto existují situace, kdy chceme garantovat, že do naší “šablonové” kolekce jsou vkládány jen objekty podporující stejné rozhraní, nebo vyžadujeme, aby vkládané objekty byly povinně potomky nějaké třídy. Nestačí nám tedy, když argument šablony podporuje metodu Commit, která má stejnou signaturu jako metoda Commit z třídy Transaction, ale chceme mít již v době kompilace ověřeno, že předaná instance je potomkem zřídy Transaction. Důvod? Třeba to, že metoda Commit z třídy Transaction má v sobě vysokoúrovňový scénář, na který spoléháte (vzor Template method, jehož název nemá nic společného s šablonami ani generikou ).
Pro novou verzi jazyka C++0x bylo plánováno, že různá omezení parametru šablony bude možné vyjádřit pomocí tzv. “Concepts”. Bohužel Concepts ani v C++0x nakonec nebudou a my si musíme vystačit s výrazovými prostředky současné verze jazyka C++. S jednou výjimkou - v článku použiju i jednu elegantní konstrukci z C++0x (implementována ve Visual Studio 2010), statickou verifikaci pomocí klíčového slova static_assert, ale ukážu, jak snadno můžeme static_assert nahradit.
Mějme běžnou šablonu třídy Collection. V našem případě jde jen o dostatečný draft třídy Collection s konstruktorem, destruktorem a prázdnou implementací metod Add a Remove.
template
<typename T>
class Collection
{
public:
~Collection(void)
{
}
Collection(void)
{
}
void Add(T& t)
{
}
void Remove(T& t)
{
}
};
Za generický parametr T lze nyní substituovat “cokoli”, ale my chceme omezit typ T pouze na instance podporující rozhraní IBusinessObject.
class IBusinessObject
{
public:
IBusinessObject(void);
virtual void CommitChanges() = 0;
virtual void RollbackChanges() = 0;
virtual ~IBusinessObject(void);
};
V C++ samozřejmě za rozhraní považujeme třídu, jejíž všechny metody jsou abstraktní, a proto převod dvou omezení ze C# ( potomek třídy, implementor rozhraní (realizace) ) se nám v C++ redukuje na nalezení ekvivalentu omezení “parametr šablony musí být potomkem námi vyžadované třídy”.
K vyjádření takového omezení si zavedeme pomocnou šablonovou třídu IsDerivedTest:
#pragma once
template
<typename Base, typename Derived>
class TestIsDerived
{
private:
struct Is_Derived_Helper
{
int dummy;
};
struct Not_Derived_Helper
{
int dummy;
int dummy2;
};
private:
TestIsDerived(void);
~TestIsDerived(void);
static Is_Derived_Helper Test(Base* base);
static Not_Derived_Helper Test(...);
public:
enum TestResult
{
IsDerivedResult = ((sizeof(Test(static_cast<Derived*>(0))) == (sizeof(Is_Derived_Helper))))
};
};
Co třída TestIsDerived umí?
Dva parametry šablony TestIsDerived mají výmluvné názvy – Base (bázová třída) a Derived (třída, u níž chceme zkontrolovat, zda je z Base odvozena)
Tuto třídu nechceme nikdy instanciovat, proto jsou její konstruktor a destruktor privátní. U privátních struktur Is_Derived_Helper a Not_Derived_Helper je důležitá jen jedna věc – musí mít odlišnou velikost (sizeof(Is_Derived_Helper) != sizeof(Not_Derived_Helper ), a proto první obsahuje jednu "dummy" proměnnou typu int a druhá struktura dvě “dummy” proměnné typu int.
Při kontrole zda parametr šablony Derived je odvozen z parametru šablony Base postupujeme takto:
- Máme dvě privátní statické metody nazvané Test. Tyto funkce jsou jen deklarovány, jejich definici nepotřebujeme, protože nebudou nikdy zavolány. Důležité je jen to, že jedna metoda Test vrací Is_Derived_Helper a druhá metoda Test vrací Not_Derived_Helper - připomňme si, že jde o struktury mající různou velikost.
- Metoda Test, která vrací strukturu Is_Derived_Helper, přijímá jako argument pointer na parametr Base, a může tedy přijmout i pointer na Derived, pokud Derived je potomkem Base. Druhá metoda Test (Test(…)) bude vyvolána vždy, když se nepodaří Derived* převést na Base*.
- Do hodnoty IsDerivedResult v enumeraci TestResult uložíme výsledek výrazu ((sizeof(Test(static_cast<Derived*>(0))) == (sizeof(Is_Derived_Helper)))) - “voláme” metodu Test tak, že přes operátor static_cast přetypujeme konstantu 0 na pointer na Derived . Ač může vypadat přetypování 0 na Derived* jako “zvěrstvo” za účelem rychlého poslání programu do
řiti říše binárního nebytí, slovo voláme je v předchozí větě záměrně v uvozovkách, protože jak jsem již psal, k žádnému volání metody Test nikdy nedojde. V době kompilace ale kompilátor zjistí, jakou variantu metody Test by zavolal pro Derived* a my z velikosti “potenciální a nikdy skutečně nevrácené” návratové hodnoty zvolené metody Test jsme schopni poznat, jestli je Derived potomkem Base. Pokud je Derived potomkem Base, bude zvolena varianta Test(Base* base), která vrací Is_Derived_Helper, a podmínka ((sizeof(Test(static_cast<Derived*>(0))) == (sizeof(Is_Derived_Helper) bude pravdivá - uložíme do IsDerivedResult hodnotu 1. Pokud Derived není potomkem Base, bude zvolena varianta metody Test(…), která vrací Not_Derived_Helper, a podmínka ((sizeof(Test(static_cast<Derived*>(0))) == (sizeof(Is_Derived_Helper) bude nepravdivá (uložíme do IsDerivedResult hodnotu 0). Znovu zdůrazňuji, že k vyhodnocení podmínky dojde již v době kompilace.
Scéna je připravena, pozveme hlavní aktéry:
Mějme dvě třídy. Třída Invoice je potomkem třídy IBusinessObject a třída NotBusinessObject (nomen omen :) ) překvapivě není.:)
class NotBusinessObject
{
public:
NotBusinessObject(void);
virtual ~NotBusinessObject(void);
};
#pragma once
#include "IBusinessObject.h"
class Invoice : public IBusinessObject
{
public:
virtual void CommitChanges();
virtual void RollbackChanges();
Invoice(void);
~Invoice(void);
};
#include "StdAfx.h"
#include <iostream>
#include "Invoice.h"
using namespace std;
Invoice::Invoice(void)
{
}
Invoice::~Invoice(void)
{
}
void Invoice::CommitChanges()
{
cout << "Commit";
}
void Invoice::RollbackChanges()
{
cout << "Rollback";
}
Naše třída Collection má přijímat jen instance potomků třídy IBusinessObject.
#pragma once
#include "TestIsDerived.h"
template
<typename T>
class Collection
{
public:
~Collection(void)
{
static_assert(TestIsDerived<IBusinessObject, T>::IsDerivedResult, "Invalid base class, IBusinessObject required");
}
Collection(void)
{
}
void Add(T& t)
{
}
void Remove(T& t)
{
}
};
Do destruktoru jsme vložili statickou kontrolu, kdy námi vytvořené pomocné třídě TestIsDerived substituujeme za parametr šablony Base třídu IBusinessObject a za parametr Derived přímo parametr šablony třídy Collection, který musí být vždy potomkem IBusinessObject. Do destruktoru jsme kód vložili proto, aby bylo garantováno, že při instanciaci šablony Collection ke statické kontrole vždy v době kompilace dojde – kód bychom mohli vložit i do konstruktoru, ale budeme-li mít konstruktorů více, museli bychom kontrolu duplikovat v každém konstruktoru. Statická kontrola v destruktoru spoléhá na to, že vytvořený konkrétní objekt Collection<T> je v aplikaci zlikvidován - buď jde o likvidaci automatické (lokální) proměnné, nebo vy sami použijete operátor delete atd. Jestliže by destruktor objektu v aplikaci nikdy nebyl volán, kompilátor nebude destruktor v šablonové třídě při rozvíjení šablony instanciovat, stejně jako nikdy neinstanciuje další nepoužité metody v šabloně.
A použití:
Tento kód bez problémů projde. Invoice je potomkem IBusinessObject.
// TemplateConstraint_Console.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "Collection.h"
#include "Invoice.h"
#include "NotBusinessObject.h"
int _tmain(int argc, _TCHAR* argv[])
{
Collection<Invoice> col1;
}
Tento kód nahlásí chybu. NotBusinessObject není potomkem třídy IBusinessObject.
// TemplateConstraint_Console.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "Collection.h"
#include "Invoice.h"
#include "NotBusinessObject.h"
int _tmain(int argc, _TCHAR* argv[])
{
Collection<NotBusinessObject> col1;
}
Ve Visual Studiu 2010 dostanu v době kompilace tuto chybu:
Error 1 error C2338: Invalid base class, IBusinessObject required c:\users\stein\documents\visual studio 2010\projects\templateconstraint_console\templateconstraint_console\collection.h 16 1 TemplateConstraint_Console
Tím je náš úkol splněn, ale jak jsem sliboval, ukážu nyní, i jak se můžeme obejít bez klíčového slova static_assert z C++0x.
Třída TestIsDerived zůstane beze změny, ale napíšeme další dvě šablony, ve kterých budeme reagovat na hodnotu IsDerivedResult z třídy TestIsDerived, což původně dělal přímo static_assert.
#pragma once
template
<int T>
struct Invalid_Base_Class
{
public:
Invalid_Base_Class(void)
{
}
~Invalid_Base_Class(void)
{
}
};
template<>
struct Invalid_Base_Class<0>
{
private:
Invalid_Base_Class(void);
~Invalid_Base_Class(void);
};
Šablona Invalid_Base_Class má jeden šablonový parametr typu int. Struktura není svým rozhaním zajímavá, podstatný je pro nás jen název struktury, abychom v době kompilace viděli chybovou hlášku “Špatná bázová třída”. První šablona Invalid_Base_Class je běžnou strukturou, která bude použita pro instanciaci všech hodnot typu int krome hodnoty 0. Pro hodnotu 0, o níž víme, že je uložena v hodnotě enumerace TestIsDerived::IsDerivedResult, jestliže DERIVED NENÍ potomkem BASE, je určena explicitní specializace Invalid_Base_Class<0>, která má ale privátní konstruktor, takže pokus o její vytvoření vždy již při kompilaci selže.
V hrubé podobě, tedy bez vlastního makra assert_is_derived apod., které skryje práci s našimi strukturami Invalid_base_Class a které by zde jen zamlžovalo průzračnost řešení, můžeme kontrolu na nutnost šablonového parametru T třídy Collection<T> dědit z třídy IBusinessObject přepsat takto:
#pragma once
#include "Invalid_Base_Class.h"
template
<typename T>
class Collection
{
public:
~Collection(void)
{
Invalid_Base_Class<TestIsDerived<IBusinessObject, T>::IsDerivedResult> invalid_base;
invalid_base;
}
Collection(void)
{
}
void Add(T& t)
{
}
void Remove(T& t)
{
}
};
V destruktoru se pokusíme instanciovat strukturu Invalid_Base_Class, které jako šablonový argument předáme hodnotu v IsDerivedResult.
Collection<Invoice> opět bez problémů projde, ale při pokusu vytvořit Collection<NotBusinessObject> dostanu při kompilaci tuto chybovou zprávu:
Error 1 error C2248: 'Invalid_Base_Class<0>::Invalid_Base_Class' : cannot access private member declared in class 'Invalid_Base_Class<0>' c:\users\stein\documents\visual studio 2010\projects\templateconstraint_console\templateconstraint_console\collection.h 14 1 TemplateConstraint_Console
Řešení by se dalo přisladit dalším syntaktickým cukrem, ale princip by měl být z článku zřejmý.
Triky s šablonami jsou úžasné a převod některých rysů generiky ze C# není zase tak problematický. Jen ta nonšalantní elegance, která je C# vlastní, v přihroublém, ale o to výkonnějším, C++ trochu chybí. :) Kdyby někoho triky se šablonami zaujaly, doporučuji ke studiu Alexendrescovu knihu Modern C++ Design: Generic Programming and Design Patterns Applied.
Monday, 24 May 2010 15:50:36 (Central Europe Standard Time, UTC+01:00)
C# | Nativní kód
Monday, 27 July 2009
Odchytnutí zprávy WM_KEYDOWN v dialogu – Windows Mobile
V jednom předchozím článku jsem slíbil, že na blog dám i kód, který umožní ve Windows dialogu zachytit všechny stisknuté klávesy. Jak možná víte, dialog ve Windows je běžné okno (Window) s třídou (class) WC_DIALOG. K dialogu je přiřazena speciální funkce WNDPROC, která zajišťuje výchozí zpracování zpráv zaslaných formuláři (např. přechod mezi prvky dialogu pomocí klávesy TAB) a volá vývojářem aplikace určenou obslužnou funkci dialogu (DLGPROC). Jedním z nepříjemných důsledků tohoto modelu chování pro dialogy je, že nejsme schopni v DLGPROC odchytit a zpracovat zprávu o stisknutí tlačítka na klávesnici (WM_KEYDOWN).
Nechceme-li reimplementovat všechny vychytávky dialogů v našem vlastním “okně” (Window) a současně chceme i v dialogu odchytit zprávu WM_KEYDOWN, musíme výchozí WNDPROC obslužnou funkci při vytváření dialogu nahradit naší vlastní “proxy” WNDPROC funkcí.
BOOL CALLBACK DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_INITDIALOG)
{
ChangeDialogWndProc(hWnd);
}
return true;
}
void ChangeDialogWndProc(HWND hwnd)
{
g_oldDlgdProc = (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC);
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)&DlgWindowsProc);
}
V naší obslužné proceduře dialogu DlgProc při inicializaci dialogu (zpráva WM_INITDIALOG) voláme funkci ChangeDialogWndProc, která zaregistruje naší “proxy” WINDPROC funkci pomocí API SetWindowLong. Ještě předtím si uložíme do proměnné g_oldDlgdProc pointer na předchozí WNDPROC funkci, která je návratovou hodnotou API funkce GetWindowLong, když jí ve druhém argumentu předáme konstantu GWL_WNDPROC.
V naší “proxy” funkci WNDPROC odchytneme všechny potřebné zprávy a když chceme zachovat výchozí chování dialogu, předáme zprávu ke zpracování v předchozím kroku uložené výchozí Windows proceduře pro dialogy.
RESULT CALLBACK DlgWindowsProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_KEYDOWN)
{
//Do something with key
int key = (int) wParam;
}
return g_oldDlgdProc(hWnd, message, wParam, lParam);
}
Následuje jednoduchý příklad založený na standardní šabloně WM projektu.
// HookDialog.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "HookDialog.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE g_hInst; // current instance
HWND g_hWndMenuBar; // menu bar handle
WNDPROC g_oldDlgdProc;
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE, LPTSTR);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void ChangeDialogWndProc(HWND hwnd);
BOOL CALLBACK DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK DlgWindowsProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_POCKETPC_PORTRAIT), NULL, &DlgProc);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HOOKDIALOG));
wc.hCursor = 0;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = szWindowClass;
return RegisterClass(&wc);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
TCHAR szTitle[MAX_LOADSTRING]; // title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // main window class name
g_hInst = hInstance; // Store instance handle in our global variable
// SHInitExtraControls should be called once during your application's initialization to initialize any
// of the device specific controls such as CAPEDIT and SIPPREF.
SHInitExtraControls();
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_HOOKDIALOG, szWindowClass, MAX_LOADSTRING);
//If it is already running, then focus on the window, and exit
hWnd = FindWindow(szWindowClass, szTitle);
if (hWnd)
{
// set focus to foremost child window
// The "| 0x00000001" is used to bring any owned windows to the foreground and
// activate them.
SetForegroundWindow((HWND)((ULONG) hWnd | 0x00000001));
return 0;
}
if (!MyRegisterClass(hInstance, szWindowClass))
{
return FALSE;
}
hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_OK:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_CREATE:
SHMENUBARINFO mbi;
memset(&mbi, 0, sizeof(SHMENUBARINFO));
mbi.cbSize = sizeof(SHMENUBARINFO);
mbi.hwndParent = hWnd;
mbi.nToolBarId = IDR_MENU;
mbi.hInstRes = g_hInst;
if (!SHCreateMenuBar(&mbi))
{
g_hWndMenuBar = NULL;
}
else
{
g_hWndMenuBar = mbi.hwndMB;
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
CommandBar_Destroy(g_hWndMenuBar);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
BOOL CALLBACK DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_INITDIALOG)
{
ChangeDialogWndProc(hWnd);
}
return true;
}
LRESULT CALLBACK DlgWindowsProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_KEYDOWN)
{
//Do something with key
int key = (int) wParam;
}
return g_oldDlgdProc(hWnd, message, wParam, lParam);
}
void ChangeDialogWndProc(HWND hwnd)
{
g_oldDlgdProc = (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC);
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)&DlgWindowsProc);
}
Monday, 27 July 2009 11:29:07 (Central Europe Standard Time, UTC+01:00)
Compact .Net Framework | Nativní kód
Tuesday, 14 April 2009
Windows Mobile formulář přes celý displej - v nativním kódu
Na MSDN fórech jsem si všiml, že se vícekrát objevil dotaz, jak v nativním kódu vytvořit okno přes celou obrazovku, které se bude chovat jako formulář v Compact .Net Framework aplikaci při nastavení vlastnosti WindowState = Maximized.
API SHFullScreen sice přepne okno do celoobrazovkového režimu, ale při zobrazení SIPu se opět objeví taskbar. Při skrytí SIPu se okno vrátí do celoobrazovkového režimu. První, co mě napadlo, je skrýt samotný taskbar. Idea dobrá, mohli jsme mít jednoduché řešení, ale autoři Windows Mobile jako již tradičně řekli ne.
Po několika pokusech jsem zjistil, že jediné použitelné řešení představuje změna pozice a velikosti formuláře vždy, když odchytím zprávu WINDOWPOSCHANGED. To celé je korunováno opakovaným voláním SHFullScreen. Na řešení v Compact .Net Frameworku jsem se nedíval, abych si nekazil radost z vyřešeného úkolu, takže netuším, zda autoři CNF používají ještě nějaký další trik.
Níže naleznete příklad, který je založen na výchozí šabloně Smart Device Windows API projektu. Zajímavé části jsou zvýrazněny tučně. Tento postup lze samozřejmě jednoduše použít ve WTL nebo MFC.
Tady se ještě zeptám:
1) Jsou alespoń pro někoho z Vás tyto tipy/FAQ zajímavé? Já sám miluji přecházení mezi nativním a “managed” kódem, ale asi nemá smysl, abych tyto tipy psal na blog, jestliže o nativní kód (již) nikdo nestojí.Potom by stačílo, abych je nechal utopeny ve fóru o mobilních zařízeních, kde poslouží podobným individuím jako jsem já. V zásobě mám například často kladený dotaz, jak ve Windows Mobile dialogu zachytit WM_KEY zprávy. I když sám si stále programování v (Compact) .Net frameworku bez dobré znalosti nativního kódu nedovedu představit - což je v roce 2009 možná tristní a nečekaná zpráva.
2) A obecnější dotaz – zajímají někoho z vás tipy pro Windows Mobile/Compact .Net Framework? Pro mě, jak asi tušíte, je programování pro WIN Mobile zařízení potěšení, a proto se podobné tipy objevují i na blogu, který píšu hlavně pro zábavu. I když většinu času jsem nyní strávil vývojem v Silverlightu, WPF, WCF a léčením roztomilých neštovic na zpočátku krásné tváři Linq2SQL, což znamená, že se na blogu se objeví i další témata, která se budou točit kolem návrhu různých typů aplikací a jako bonus odhalíme nejčastější průšvihy spojené s anemickými modely (i model-view-viewmodely )aplikací.
// FullScreen.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "FullScreen.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE g_hInst; // current instance
HWND g_hWndMenuBar; // menu bar handle
RECT usedRect;
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE, LPTSTR);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
void MakeFullScreen();
void MakeFullScreen(HWND hWnd)
{
SetRect(&usedRect, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
LONG windowWidth = usedRect.right - usedRect.left;
LONG windowHeight = usedRect.bottom - usedRect.top;
MoveWindow(hWnd,
usedRect.left,
usedRect.top,
windowWidth,
windowHeight,
FALSE);
//SIPINFO info;
//info.cbSize = sizeof(info);
//ZeroMemory(&info, sizeof(info));
//info.rcVisibleDesktop = usedRect;
//
//if (!::SipSetInfo(&info))
//{
// int error = GetLastError();
// return FALSE;
//}
SHFullScreen(hWnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON);
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable;
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_FULLSCREEN));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FULLSCREEN));
wc.hCursor = 0;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = szWindowClass;
return RegisterClass(&wc);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
TCHAR szTitle[MAX_LOADSTRING]; // title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // main window class name
g_hInst = hInstance; // Store instance handle in our global variable
// SHInitExtraControls should be called once during your application's initialization to initialize any
// of the device specific controls such as CAPEDIT and SIPPREF.
SHInitExtraControls();
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_FULLSCREEN, szWindowClass, MAX_LOADSTRING);
//If it is already running, then focus on the window, and exit
hWnd = FindWindow(szWindowClass, szTitle);
if (hWnd)
{
// set focus to foremost child window
// The "| 0x00000001" is used to bring any owned windows to the foreground and
// activate them.
SetForegroundWindow((HWND)((ULONG) hWnd | 0x00000001));
return 0;
}
if (!MyRegisterClass(hInstance, szWindowClass))
{
return FALSE;
}
hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
// When the main window is created using CW_USEDEFAULT the height of the menubar (if one
// is created is not taken into account). So we resize the window after creating it
// if a menubar is present
if (g_hWndMenuBar)
{
RECT rc;
RECT rcMenuBar;
GetWindowRect(hWnd, &rc);
GetWindowRect(g_hWndMenuBar, &rcMenuBar);
rc.bottom -= (rcMenuBar.bottom - rcMenuBar.top);
MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, FALSE);
}
ShowWindow(hWnd, nCmdShow);
//MakeFullScreen(hWnd);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
LPWINDOWPOS pos;
static SHACTIVATEINFO s_sai;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_HELP_ABOUT:
DialogBox(g_hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, About);
break;
case IDM_OK:
SendMessage (hWnd, WM_CLOSE, 0, 0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_CREATE:
SHMENUBARINFO mbi;
memset(&mbi, 0, sizeof(SHMENUBARINFO));
mbi.cbSize = sizeof(SHMENUBARINFO);
mbi.hwndParent = hWnd;
mbi.nToolBarId = IDR_MENU;
mbi.hInstRes = g_hInst;
if (!SHCreateMenuBar(&mbi))
{
g_hWndMenuBar = NULL;
}
else
{
g_hWndMenuBar = mbi.hwndMB;
}
// Initialize the shell activate info structure
memset(&s_sai, 0, sizeof (s_sai));
s_sai.cbSize = sizeof (s_sai);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
CommandBar_Destroy(g_hWndMenuBar);
PostQuitMessage(0);
break;
case WM_WINDOWPOSCHANGED:
pos = (LPWINDOWPOS) lParam;
if ((pos->cx != usedRect.right - usedRect.left) ||
(pos->cy != usedRect.bottom - usedRect.top))
{
MakeFullScreen(hWnd);
}
break;
case WM_ACTIVATE:
// Notify shell of our activate message
SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE);
break;
case WM_SETTINGCHANGE:
SHHandleWMSettingChange(hWnd, wParam, lParam, &s_sai);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// Create a Done button and size it.
SHINITDLGINFO shidi;
shidi.dwMask = SHIDIM_FLAGS;
shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
shidi.hDlg = hDlg;
SHInitDialog(&shidi);
}
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
case WM_CLOSE:
EndDialog(hDlg, message);
return TRUE;
}
return (INT_PTR)FALSE;
}
Tuesday, 14 April 2009 15:04:43 (Central Europe Standard Time, UTC+01:00)
Compact .Net Framework | Nativní kód
Monday, 16 February 2009
GSM Net Monitor verze 0.9.0
Homepage aplikace.
Instalační cab.
Návod na rozchození lokalizace pozice pomocí BTS
Změny ve verzi 0.0.9.
- Vylepšena stabilita aplikace. Pro znalce: Přesně řečeno, při startu pluginu se žádným způsobem nepracuje s RIL knihovnou. Pokud by při inicializaci pluginu byly stále na některých zařízeních potíže, zbývá už jen načítat RIL funkce dynamicky přes LoadLibrary.
- V souboru se zachycenými BTS se na každém řádku (poslední údaj) vypisuje informace o síti (network id). (ukázka -26.01.2009;12:41:26;18208;43649;-1;23001;) Přidáno na žádost lovců BTS.
Důležité:
Před instalací nové verze vypněte v aplikaci sledování sítě. Nejlépe starou verzi také sami deaktivujte v nastavení Today obrazovky a odnistalujte ji přes applet Přidat-Odebrat programy.
Protože se jedná o AlFA preview, doporučuji před instalaci Net Monitoru mít v zařízení např. SPB Pocket Plus a v něm aktivovaný safe boot - jestliže by vám "vytuhlo" zařízení, nebudete muset dělat HR (Hard Reset), protože můžete při startu zařízení dočasně deaktivovat Today pluginy.
Program pro jistotu ani nezkoušejte na zařízení, kde je nahráno TouchFlo, nebo jiný agresivní Today plugin. Riskujete zatuhnutí zařízení a je zbytečné mi potom psát dojemné maily, pokud nejste schopni si předtím udělat zálohu PDA nebo alespoň mít funkční safe-boot.
Po upgradu budete muset pravděpodobně znovu zadat svůj registrovaný email a přístupový kód. Pokud jste jej zapomněli, jděte na stránku http://gsmadmin.renestein.net a zadejte znovu svůj email. Aplikace vám nabídne opětovné zaslání emailu s přístupovým kódem.
Jestliže máte zařízení s VGA displejem, v pluginu jsou malé ikony a plugin je vykreslován na malé ploše. Plugin může být vykreslen na větší ploše - přes kontexové menu zobrazte Nastavení pluginu a změňte na záložce Základní nastavení preferovanou výšku na obrazovce Dnes. Ikony ale stejně zůstanou malé, proto chystám plnohotnotnou VGA verzi, do té doby lze plugin plně ovládat přes kontextové menu.
Monday, 16 February 2009 12:52:47 (Central Europe Standard Time, UTC+01:00)
Mobilitky | Nativní kód | Net Monitor
Wednesday, 04 February 2009
Textbox nepodporující výběr textu a další specialitky
Někdy se hodí mít textbox, u kterého je skrytý "caret" (netuší někdo, jak se termín caret překládá - pouze kurzor?) a současně nepodporuje označování textu. Také můžete chtít, aby se textbox choval podobně jako při nastavení vlastnosti ReadOnly na true, ale bez "zašedlého" zobrazení textboxu, což je většinou nechtěný průvodní jev textových polí označených pouze pro čtení.
Kód je pro Compact .Net Framework, nic vám ale nebrání přenést jej na "velký" NF. Ke zpracování Windows zpráv je použita třída NativeWindow, která je součástí OpenNetCF frameworku. Opravdu jen tato podivná sekvence zpracování Windows zpráv byla v CNF ta pravá.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using OpenNETCF.Windows.Forms;
namespace HideCaret
{
public class NativeTxtWrapper : NativeWindow
{
[DllImport("CoreDll.dll")]
private static extern bool ShowCaret(IntPtr hWnd);
[DllImport("CoreDll.dll")]
private static extern bool HideCaret(IntPtr hWnd);
private TextBox m_txtBox;
private bool m_hasFullFocus;
private const int WM_LBUTTONDOWN = 0x201;
private const int WM_KEYDOWN = 0x0100;
private const int WM_MOUSEMOVE = 0x0200;
private const int WM_CHAR = 0x0102;
private const int WM_COMMAND = 0x0111;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_LBUTTONDBLCLK = 0x0203;
private const int WM_PAINT = 0x000F;
private const int WM_KILLFOCUS = 0x0008;
private const int WM_SETFOCUS = 0x0007;
public NativeTxtWrapper(TextBox txtBox)
{
init(txtBox);
m_hasFullFocus = false;
}
private void init(TextBox txtBox)
{
if (txtBox == null)
{
throw new ArgumentNullException("txtBox");
}
if (txtBox.Handle != IntPtr.Zero)
{
AssignHandle(txtBox.Handle);
}
txtBox.HandleCreated += ((sender, e) => AssignHandle(((Form)sender).Handle));
txtBox.HandleDestroyed += ((sender, e) => ReleaseHandle());
m_txtBox = txtBox;
}
protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message m)
{
if (m.Msg == WM_CHAR)
{
m_hasFullFocus = true;
}
if (m.Msg == WM_MOUSEMOVE || m.Msg == WM_LBUTTONDBLCLK)
{
return;
}
if (((m.Msg == WM_LBUTTONUP) ||
(m.Msg == WM_LBUTTONDOWN)) && m_hasFullFocus)
{
return;
}
if (m.Msg == WM_SETFOCUS)
{
base.WndProc(ref m);
HideCaret(m_txtBox.Handle);
return;
//m_hasFocus = false;
}
if (m.Msg == WM_KILLFOCUS)
{
m_hasFullFocus = false;
}
base.WndProc(ref m);
}
}
}
Jestliže chcete textbox pouze pro čtení, stačí nepředat ke zpracování bázové třídě (base.WndProc) zprávu WM_CHAR.
Objektem NativeTxtWrapper lze oddekorovat jakýkoli textbox, nebo můžete vytvořit potomka třídy Textbox, který před klienty třídy skryje použití objektu NativeTextWrapper
private void Form1_Load(object sender, EventArgs e)
{
m_wrapper = new NativeTxtWrapper(textBox1);
textBox1.Focus();
}
Podobnou třídu mám i pro nativní projekty psané v MFC, možná se někomu z vás bude hodit. Přepsat kód do Windows API z MFC je také trivální.
#pragma once
#include "afxwin.h"
class CTextBoxEx :
public CEdit
{
private:
bool m_hasFullFocus;
public:
CTextBoxEx(void);
~CTextBoxEx(void);
virtual LRESULT WindowProc(UINT message, WPARAM wparam, LPARAM lparam);
};
#include "StdAfx.h"
#include "TextBoxEx.h"
CTextBoxEx::CTextBoxEx(void) : m_hasFullFocus(false)
{
}
CTextBoxEx::~CTextBoxEx(void)
{
}
LRESULT CTextBoxEx::WindowProc(UINT message, WPARAM wparam, LPARAM lparam)
{
//Readonly textbox
if (message == WM_CHAR)
{
return 1;
}
//end Readonly textbox
POINT mousePoint;
GetCursorPos(&mousePoint);
ScreenToClient(&mousePoint);
RECT clientRect;
GetClientRect(&clientRect);
BOOL isMouseInRect = PtInRect(&clientRect, mousePoint);
if (((message == WM_MOUSEMOVE) || (message == WM_LBUTTONDBLCLK)) && isMouseInRect)
{
return 1;
}
if (((message == WM_LBUTTONUP) ||
(message== WM_LBUTTONDOWN)) && m_hasFullFocus && isMouseInRect)
{
return 1;
}
if (message == WM_SETFOCUS)
{
m_hasFullFocus = true;
HideCaret();
return 1;
}
if (message == WM_KILLFOCUS)
{
m_hasFullFocus = false;
}
return CEdit::WindowProc(message, wparam, lparam);
}
Wednesday, 04 February 2009 16:52:26 (Central Europe Standard Time, UTC+01:00)
.NET Framework | Compact .Net Framework | Nativní kód | Windows Forms
Wednesday, 30 July 2008
GSM Net Monitor verze 0.7.0. Alfa
Homepage aplikace.
Instalační cab.
Návod na rozchození lokalizace pozice pomocí BTS
Změny ve verzi 0.0.7.
- Odstraněna chyba, která se mohla vyskytnout při odesílání požadavku na server => Net Monitor vypisoval chybu 2 (špatné formátování).
Důležité:
Před instalací nové verze vypněte v aplikaci sledování sítě.
Protože se jedná o AlFA preview, doporučuji před instalaci Net Monitoru mít v zařízení např. SPB Pocket Plus a v něm aktivovaný safe boot - jestliže by vám "vytuhlo" zařízení, nebudete muset dělat HR (Hard Reset), protože můžete při startu zařízení dočasně deaktivovat Today pluginy.
Po upgradu budete muset pravděpodobně znovu zadat svůj registrovaný email a přístupový kód. Pokud jste jej zapomněli, jděte na stránku http://gsmadmin.renestein.net a zadejte znovu svůj email. Aplikace vám nabídne opětovné zaslání emailu.
Wednesday, 30 July 2008 16:06:40 (Central Europe Standard Time, UTC+01:00)
Mobilitky | Nativní kód | Net Monitor
Monday, 28 July 2008
Technologie použité při vývoji Net Monitoru
V mailu jsem dostal dotaz, v "čem je napsán" GSM Net Monitor. Možná to bude zajímat i někoho dalšího, protože v Net Monitoru se potkává paleolit s postmodernim věkem . :) Při vývoji aplikace si uvědomíte nejen, kde vám .Net Framework šetří práci, ale i to, kde by jeho režie byla spíš na obtíž, a v čem exceluje C++. Soukromý povzdech: C# generika je skvělá, ale C++ šablony u mě kralují...
Serverová část - databáze MSSQL 2005, ASP.NET 3.5, Windows Communication Foundation 3.5 s podporou toho, čemu se podivně říká REST, LINQ s vlastními "extenzními" metodami, jazyk C#.
Klientská část na PDA - pouze Windows API - protože jsem chtěl, aby aplikace neměla přehnané paměťové nároky, nepoužil jsem ani COM XML parser od Microsoftu a jen doufám, že Windows CE team portuje desktopovou knihovnu XmlLite. Jazyk C++.
Monday, 28 July 2008 10:11:54 (Central Europe Standard Time, UTC+01:00)
Mobilitky | Nativní kód | Net Monitor
Monday, 07 July 2008
Víte, u kolika českých BTS už zná Google jejich přibližnou polohu? Aneb Google Maps Mobile
Google má ve svém program Google Maps Mobile užitečnou funkci pro přibližné určení polohy bez použití GPS. Volba "Moje Poloha" se pokusí o lokalizaci na základě pozice GSM buňky (buněk), ke které je momentálně přihlášen váš mobilní telefon. Od počátku bylo zřejmé, že Google musí sbírat informace o pozici GSM buněk "za pochodu", protože žádné oficiální, a navíc celosvětové, databáze BTS neexistují. Google tedy pravděpodobně na své servery anonymně odesílá informace o aktuální GSM buňce společně s GPS souřadnicemi vždy, když v GM Mobile zapnete GPS. Viz také diskuze pod článkem na Navigovat.cz, který upozorňoval na novou verzi GM Mobile. Dohady potvrdil i můj pokus, kdy jsem několikrát na místech, kde aplikace GM Mobile nejprve sveřepě odmítala zobrazit pozici na základě buňky, pustil GPS, a za několik hodin/dní jsem již byl lokalizován jen přes funkci "Moje poloha".
A jak je na tom Google dnes (7. 7. 2008) s "pokrytím" v ČR? Do statistiky jsou zahrnuty jen buňky v GSM síti (ne UMTS) společností T-Mobile, O2 a Vodafone, jejichž (neoficiální) seznamy naleznete na gsmweb.cz. Celkový počet GSM buněk, u nichž jsem zjištoval "pokrytí" Googlem, je tedy dán součtem evidovaných GSM buněk v odpovídajících seznamech na gsmweb.cz.
Celkem sledováno GSM buněk: 34 773
Z toho buněk s GPS souřadnicemi: 19 435
T-Mobile celkem buněk / z toho s GPS souřadnicemi: 11 060 / 6 379
O2 celkem buněk/z toho s GPS souřadnicemi: 11588 / 6 838
Vodafone celkem buněk/z toho s GPS souřadnicemi: 12125 / 6218
Prosím, neptejte se, jak jsem se k těmto údajům dostal, stejně vám to neřeknu ani nenapíšu. :) A do Googlu asi také nemá cenu psát. :)
Monday, 07 July 2008 18:50:45 (Central Europe Standard Time, UTC+01:00)
Mobilitky | Nativní kód
Wednesday, 28 May 2008
Thursday, 10 January 2008
Wifi profily verze 0.0.4
Verze 0.0.4.
Stáhnout cab: !!!Důležité: Není povoleno cab soubor dále redistribuovat nebo vystavovat na svých stránkách - musí být stažen z mých stránek!!!
Diskuze k programu:
1) Sjednocení EN a CZ verze - podpora pro libovolné množství dalších jazyků.
Aplikace se spustí v angličtině, do češtiny se přepnete vybráním položky Language menu a zvolením CZ v dalším dialogu.
2) Možnost nastavení internetové http proxy. Podpora vrácení se k poslednímu stavu připojení před aplikací proxy. Abyste požívali proxy v Pocket IE a dalších programech, musíte nastavit u wifi sítě, že se připojuje k profilu work. Nové nastavení vás připojí přes proxy na internet - nechci zabíhat do detailů, "global wide proxy" jsou poměrně komplikovaná záležitost a pro jejich programové nastavení neexistuje pořádná dokumentace. :(
Pokud bude zájem, mohu přidat socks proxy.
3) Možnost samostatně z menu nebo i u každého profilu nastavit, zda se má wifi odpojit při vypnutí zařízení. Upozorňuji, že u některých zařízení musíte pro uplatnění hodnot (podobně jako je tomu při zapínání wifi) sami zapnout wifi po soft resetu z manažera připojení před nastavením způsobu odpojování wifi.
4) Možnost spustit po aplikaci profilu (resp. po připojení k wifi síti - platné SSID) libovolný program. Jestliže není wifi síť připojena nebo nelze detekovat připojení k wifi síti, je program spuštěn po uplynuti 20 s.
Pro program je možné zadat příkazový řádek. V příští verzi nebudete muset zadávat cestu k programu sami do textového pole, ale v dialogu si program sami vyberete.
5) Možnost vytvořit si zástupce přímo pro profil (např. v Total Commanderu CE) - profil lze aplikovat vytvořením zástupce s příkazem v následujícím tvaru.
"<Cesta k exe>" <Nazev profilu>
Tedy např. tento zástupce aktivuje profil s názvem MujProfil.
"\Storage Card\Program Files\RStein\WifiProfiles\RStein.WifiProfiles3.exe" MujProfil
6) Možnost nainstalovat aplikaci na SD kartu.
7) Volba v profilu, zda má být po aktivaci profilu zapnuta wifi.
8) Po zvolení DHCP=Ano jsou nepoužívané volby v konfiguraci profilu skryty.
Za nesprávné používání aplikace ani za možné vzniklé škody a následné hard resety nenesu žádnou odpovědnost! To pro jistotu připomínám . :)
Enjoy :)
Thursday, 10 January 2008 20:40:54 (Central Europe Standard Time, UTC+01:00)
Mobilitky | Nativní kód | Wifi Profiles Windows Mobile