<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Oleksandr Turevskiy</title><link>https://turevskiy.com/blog/</link><atom:link href="https://turevskiy.com/blog/feed.xml" rel="self" type="application/rss+xml"/><description>Engineering notes.</description><language>en</language><item><title>Fed up with VPN providers, I wrote my own server</title><link>https://turevskiy.com/blog/fed-up-with-vpn-providers-i-wrote-my-own-server/</link><guid isPermaLink="true">https://turevskiy.com/blog/fed-up-with-vpn-providers-i-wrote-my-own-server/</guid><pubDate>Sun, 01 Nov 2026 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Rust</category><category>OpenVPN</category><category>VPN</category><category>networking</category><category>self-hosting</category><category>open source</category><category>AI-assisted development</category><category>side projects</category><description>just-another-vpn-server began with a sudden change of terms from my paid VPN provider, support that did not help, and no other provider I was happy with. It grew into an OpenVPN-compatible server in Rust that I use myself to reach location-limited sites in Ukraine and Germany from abroad.</description><content:encoded><![CDATA[<p>I did not set out to write a VPN server. I set out to stop being annoyed.</p>
<p>I was paying for a VPN. Then my provider changed its terms suddenly, and customer support was no help in dealing with it. I looked at other providers and did not find one I was satisfied with either.</p>
<p>What I needed was modest. Some sites in Ukraine and Germany only work properly from inside those countries, and I am not always inside them. I did not need thousands of servers or a smart app. I needed a tunnel that answered to me, on terms that could not change overnight.</p>
<p>So on 28 May 2026 I made the first commit to <a href="https://github.com/oltur/just-another-vpn-server" target="_blank" rel="noopener noreferrer">just-another-vpn-server</a>.</p>
<h3>Why speak OpenVPN</h3>
<p>The easy way to make a VPN is to invent a small private protocol. Then you also own every client, on every device, forever.</p>
<p>Speaking the OpenVPN wire protocol avoids that. A stock <code>openvpn</code> client, Tunnelblick and OpenVPN Connect already exist, and none of them should need to know that the other end is a few thousand lines of Rust. It also gives the project an unforgiving definition of done: either an unmodified client connects, or it does not.</p>
<p>"Written from the wire format up" means the protocol, not the cryptography. Packet framing, the reliability layer, the key exchange, sessions and rekeying are in this codebase. TLS comes from <code>rustls</code>, and the ciphers come from established crates. I wanted a VPN server, not my own crypto.</p>
<h3>The first week</h3>
<p>The first commit was already large: 31 files and about 6,900 lines, including a Docker harness that tests against a real OpenVPN client. Claude is credited as co-author on 19 of the 29 commits, including the fixes below. The hard part started when real clients on real machines connected to it.</p>
<p>Linux went first. Writes to the TUN device failed with <code>EINVAL</code>, and the cause turned out to be two separate things. The packet-information header behaved unreliably across versions of the <code>tun</code> crate, so the server now handles those four bytes explicitly. And OpenVPN Connect sends internal messages over the data channel that are not IP packets at all. Writing one to the TUN device failed and killed the writer task, so they are now dropped before that point.</p>
<p>Then something less dramatic: every restart added its <code>iptables</code> rules again, without removing the old ones. Nothing broke at first. There were just more and more duplicates.</p>
<h3>The day macOS said no</h3>
<p>Full-tunnel mode on macOS took a whole day and is my favourite part of the history. On 4 June:</p>
<ol><li><code>redirect-gateway def1</code> seemed to send traffic the wrong way, because the client added a bypass route for the gateway through the physical interface. So I replaced it with explicit <code>/1</code> routes.</li><li>That was not enough, so I switched to point-to-point style addressing.</li><li>That was not it either, and the third attempt of the day went back to the standard subnet format.</li></ol>
<p>The real failure was a chain. An explicit pushed subnet route triggered Tunnelblick's loop protection, which made routing setup fail and retry for over a minute. Meanwhile the server saw no traffic, reached its idle timeout and evicted the session. The client then kept sending packets with a <code>peer_id</code> that no longer existed.</p>
<p>So the fix was mostly to stop being clever: send what a normal OpenVPN server sends, stop pushing that extra route, and give the idle timeout more headroom. When your job is to be compatible, the standard behaviour is usually standard for a reason.</p>
<h3>What it is now</h3>
<p>Version 0.1.8 does what I set out to build, and a bit more:</p>
<ul><li>UDP and TCP transport, with mutual TLS on the control channel</li><li>AES-256-GCM on the data channel, with a replay window</li><li><code>tls-auth</code> and <code>tls-crypt</code></li><li>Rekeying from either side without dropping packets</li><li>Address pools, IPv6 in the tunnel, NAT and per-client settings</li></ul>
<p>OpenVPN has no standards body behind it, so compatibility here means working with the real thing. It is verified end to end against OpenVPN 2.6.14, and there is a script for overnight stability runs. The repository has CI, a dependency security audit and release builds. The audit has already earned its place: the latest commits update a TLS dependency to fix published security advisories.</p>
<p>It also has limits, and the README lists them:</p>
<ul><li>It has not been security-audited.</li><li>Authentication is by client certificate only.</li><li>AES-256-GCM is the only data-channel cipher.</li><li>NAT and IPv6 in the tunnel are Linux-only.</li></ul>
<p>It suits self-hosting and learning. It is not a hardened replacement for the OpenVPN daemon, and I would not suggest anyone treat it as one.</p>
<h3>Using it</h3>
<p>This is the part that makes me happiest. When I am abroad, I connect with an ordinary OpenVPN client and reach the location-limited sites in Ukraine and Germany I built it for, through a server I run myself.</p>
<p>No provider can change the terms on me now. The trade-off is that I have also become customer support, and that department only answers when it feels like it. At least I know who to complain to.</p>
<p>The code is <a href="https://github.com/oltur/just-another-vpn-server" target="_blank" rel="noopener noreferrer">on GitHub</a>, under the MIT licence.</p>]]></content:encoded></item><item><title>A blocked connection proves nothing</title><link>https://turevskiy.com/blog/a-blocked-connection-proves-nothing/</link><guid isPermaLink="true">https://turevskiy.com/blog/a-blocked-connection-proves-nothing/</guid><pubDate>Sun, 01 Nov 2026 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Kubernetes</category><category>RKE2</category><category>air gap</category><category>network security</category><category>network policy</category><category>Calico</category><category>supply chain security</category><category>signed images</category><category>Kyverno</category><category>testing</category><category>Go</category><description>I built a simulated air gap on a single-node Kubernetes lab. The useful tests were the ones that opened each layer and checked that traffic got through.</description><content:encoded><![CDATA[<p>Run <code>curl</code> from a pod, watch it time out, and call the cluster isolated. That test also passes with a typo in the address, a dead listener or a broken pod.</p>
<p>So my rule for this lab was that a block counts only if the same attempt succeeds once the layer blocking it is removed.</p>
<h3>The setup</h3>
<p>The lab is one RKE2 node in a VM on a NUC, on an isolated network. It installs from an offline bundle, pulls only from a read-only internal registry, and admits only images signed with the bundle's key. Two independent layers stop traffic leaving:</p>
<ul><li><strong>In the cluster:</strong> a default-deny network policy that also covers new namespaces.</li><li><strong>At the VM:</strong> a black-hole default route, plus a host firewall.</li></ul>
<h3>The test</h3>
<p>A Go program on the host opens and closes each layer, then tries to reach a canary listener from a pod. The canary lives on temporary host addresses, so opening the VM path never opens a route to the internet.</p>
<pre><code>                  policy on    policy off
VM path closed    blocked      blocked
VM path open      blocked      REACHED</code></pre>
<p>Only the bottom-right cell connects, and the canary's own log has to confirm it. That one cell shows the other three were blocked by the layers, not by a broken test.</p>
<p><code>make rebuild</code> destroys the VM, rebuilds it and runs everything: 185 checks. It took 225 seconds to build and 572 seconds to test, with downloads cached.</p>
<h3>What the controls caught</h3>
<p><strong>A control that did nothing.</strong> To show that cluster DNS can't leak a name, the test temporarily adds a forwarder to CoreDNS and expects a random label to reach the canary. Nothing arrived. The Corefile said <code>cache  30</code> with two spaces, so my edit never matched and no forwarder was added. Without the control, the result would have read as "zero leaks".</p>
<p><strong>A check that accepted a crash loop.</strong> An older test checked for the <code>Running</code> phase, which a crash-looping pod still reports. Its probe pod was in fact exiting on a bad argument. It now has to be ready.</p>
<p><strong>A cost I hadn't planned for.</strong> With the registry down, running pods carry on and containers restart from cache, but new pods are refused, even signed ones, because the verifier needs the registry to check the signature. That fails closed, and a pod rescheduled during an outage won't start.</p>
<h3>What this doesn't prove</h3>
<ul><li>The canary is on the host, not a remote network.</li><li>Public IP and IPv6 targets are blocked, but have no open-path control, because the test never opens the internet.</li><li>The gateway address stayed unreachable even with the policy off, so its block isn't shown to come from the policy.</li><li>Only TCP and DNS over UDP were tested, not ICMP.</li><li>It is one node in a lab, designed for air-gapped environments and not certified for anything.</li></ul>
<p>Next comes what runs inside the gap: retrieval, then a gateway for AI that can act on real systems only after a person confirms.</p>]]></content:encoded></item><item><title>What happens if you just ask for a 3D game</title><link>https://turevskiy.com/blog/what-happens-if-you-just-ask-for-a-3d-game/</link><guid isPermaLink="true">https://turevskiy.com/blog/what-happens-if-you-just-ask-for-a-3d-game/</guid><pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>5001 Axolotls</category><category>browser games</category><category>3D games</category><category>Three.js</category><category>WebGL</category><category>multiplayer</category><category>procedural generation</category><category>AI-assisted development</category><category>side projects</category><description>5001 Axolotls began on 2 September as an experiment — could an AI assistant actually build a 3D game? Five weeks later it was a multiplayer world with sunken playgrounds and zombie pirates, and my children had opinions about it.</description><content:encoded><![CDATA[<p>On 2 September I committed <em>initial version</em> to an empty repository. The experiment was simple and slightly unserious: I wanted to find out whether an AI assistant could actually build a 3D game, as opposed to producing something that compiles and then falls over.</p>
<p>I picked axolotls because they are absurd. A salamander that never grows up, with feathery pink gills and a permanent smile — if you are going to spend evenings staring at a creature model, it may as well be that one.</p>
<p>Twenty commits later, over five weeks, the answer turned out to be yes, with a caveat I will get to.</p>
<h3>What it became</h3>
<p>An endless procedurally generated underwater maze. Seaweed, kelp, coral, mossy rock rooms on a seabed, and a cast of creatures wandering through it — whale, shark, seal, lamprey, octavia the octopus, blobfish, narwhal, and of course the axolotl. Each one announces its own name when you meet it, in whichever of 76 languages you are playing in.</p>
<p>Every so often — four percent of eligible rooms — you swim into something that was placed rather than generated:</p>
<ul><li>A <strong>shipwreck</strong>: a beached hull with masts and rigging, crewed by zombie pirates and a zombie parrot, with its own sea shanty that fades in as you approach and periodically breaks into a spoken chorus of <em>Fifteen Men on a Dead Man's Chest</em>.</li><li>A <strong>sunken temple</strong>: weathered pillars, one fully collapsed, around a robed figure in a praying posture, with a wordless choir drone.</li><li>A <strong>sunken playground</strong>: swing set, slide, seesaw, and a roundabout that actually spins and carries you with it — the one piece in the game with real moving-platform physics. Beside it a small brick fortress with four walled turrets, deliberately left open at the top so that swimming up and over is the only way in. A music-box <em>Frère Jacques</em> drifts in as you get close.</li></ul>
<figure><img src="https://turevskiy.com/blog/images/axolotls-playground.jpg" alt="The sunken playground: the brick fortress, swing set, roundabout, slide and seesaw." loading="lazy"><figcaption>The sunken playground: the brick fortress, swing set, roundabout, slide and seesaw.</figcaption></figure>
<figure><img src="https://turevskiy.com/blog/images/axolotls-shipwreck.jpg" alt="The shipwreck, with a zombie crew and the zombie parrot on the rail." loading="lazy"><figcaption>The shipwreck, with a zombie crew and the zombie parrot on the rail.</figcaption></figure>
<figure><img src="https://turevskiy.com/blog/images/axolotls-temple.jpg" alt="The sunken temple&#x27;s weathered pillars, one of them collapsed." loading="lazy"><figcaption>The sunken temple&#x27;s weathered pillars, one of them collapsed.</figcaption></figure>
<p>My children found the playground first. Then the fortress. Then they worked out that you can get the roundabout going fast enough to be thrown off it, which was not a feature I had planned and which I have left in.</p>
<h3>The caveat</h3>
<p>The honest version of "AI built a 3D game" is that the assistant was extraordinarily good at the parts with known shapes and needed steering on the parts that were actually decisions.</p>
<p>Three examples from the commit log, which is unusually candid: <em>big rework</em> on 13 September, and then <em>ui fixes</em> six times across the following week.</p>
<p><strong>The fortress windows.</strong> The collision system only understands axis-aligned boxes. A window is a hole, and a hole in a box is not a box. The obvious implementation — one solid wall with a dark rectangle painted on it — looks right in a screenshot and wrong the moment a child swims at it. Each wall is built from four separate pieces, two pillars, a lintel and a sill, framing a real gap. Nobody asks for that. You only discover it is necessary by watching someone try to swim through a picture of a window.</p>
<p><strong>The temple figure.</strong> A robed praying figure is trivially easy to make accidentally specific to one religion. The version in the game is a deliberately universal orans posture with a fully shadowed, featureless hood. That is not a technical problem and no amount of model capability would have raised it; it is a judgement about what you are willing to ship to strangers' children.</p>
<p><strong>The music.</strong> Everything audible in the game is either synthesised or public domain — a shanty from <em>Treasure Island</em>, an 18th-century French folk round. Checking that is tedious, unglamorous, and entirely on the human.</p>
<p>So: the assistant wrote a very large share of 28 modules of game code, fast, and the parts I had to own were the taste and the liability. That split has held for me across every project this year, and I find it more reassuring than the alternative would be.</p>
<h3>Multiplayer, in one day</h3>
<p>The most interesting engineering is dated 9 October — four commits, all in one go: <em>multiplayer to web and</em>, <em>multiplayer to web</em>, <em>more fixes</em>, <em>more fixed</em>.</p>
<p>Adding multiplayer to a procedural world normally means a second implementation. The server has to know where every creature is, which means reimplementing the maze generator, the colliders, the spawn tables and the creature AI on the server, in whatever language the server is written in — and then keeping four copies of that logic in step forever.</p>
<p>Instead, the simulation lives in a module that imports no rendering code and no DOM. The browser runs it. The Node server imports the exact same file and runs it too. There is one description of how a creature wanders, and both ends execute it. Deploying the server means shipping two directories.</p>
<p>Identity is a SHA-256 proof-of-work solved in a worker thread while the connecting spinner turns. No email, no password, nothing to type. Nicknames are assigned by the server from a word list, and there is no free text anywhere in the game — which means there is no moderation surface at all. When your users include your own children and their friends, the cheapest way to handle a hard problem is to design it out of existence.</p>
<h3>Was the experiment a success?</h3>
<p>It was supposed to answer a question about tooling and it ended up answering a different one.</p>
<p>The assistant comfortably handled things I would have budgeted weeks for: procedural generation, collision, creature behaviour, a whole audio layer, 76 locales, a Capacitor build for iOS and Android. That is genuinely new, and anyone telling you otherwise has not tried recently.</p>
<p>But the game is good — to the extent it is good — because of a roundabout that spins you off it, a fortress you have to swim over to enter, and a parrot that is also a zombie. None of those came out of a prompt. They came out of watching my children play, and going back in.</p>
<hr>
<p><strong>5001 Axolotls</strong> runs in a browser at <a href="https://turevskiy.com/5001axolotls/" target="_blank" rel="noopener noreferrer">turevskiy.com/5001axolotls</a>, and is free on the <a href="https://apps.apple.com/app/id6813990177" target="_blank" rel="noopener noreferrer">App Store</a> and <a href="https://play.google.com/store/apps/details?id=com.turevskiy.axolotls5001" target="_blank" rel="noopener noreferrer">Google Play</a>.</p>]]></content:encoded></item><item><title>The timer that changed its mind</title><link>https://turevskiy.com/blog/the-timer-that-changed-its-mind/</link><guid isPermaLink="true">https://turevskiy.com/blog/the-timer-that-changed-its-mind/</guid><pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>5001 Tomatoes</category><category>Pomodoro</category><category>Pomodoro technique</category><category>time management</category><category>productivity</category><category>focus timer</category><category>SwiftUI</category><category>Kotlin</category><category>iOS</category><category>Android</category><category>privacy</category><category>side projects</category><description>5001 Tomatoes started on 30 September 2025 as a five-minute interval timer for learning Spanish. The directory on my disk is still called FiveMinutes. A year later one commit renamed it, and the rename is the whole story.</description><content:encoded><![CDATA[<p>The folder on my laptop is called <code>FiveMinutes</code>. The app in the store is called <strong>5001 Tomatoes</strong>. Everything interesting about this project happened between those two names, and it took a year.</p>
<p>It began on 30 September 2025, with three commits in one evening — <em>Initial Commit</em>, <em>initial version</em>, <em>initial version</em> again. I was learning Spanish. The method I had settled on was five minutes at a time, several times a day: short enough that you never negotiate with yourself about starting, frequent enough to stick. What that needs is a timer you can begin in one tap and then forget.</p>
<p>Every timer I tried wanted more from me than that. An account, a streak, cloud sync, a weekly report, a subscription. I wanted a button.</p>
<p>So: one button, five minutes, a sound at the end.</p>
<figure><img src="https://turevskiy.com/blog/images/tomatoes-running.jpg" alt="Five minutes, one second in." loading="lazy"><figcaption>Five minutes, one second in.</figcaption></figure>
<p>This was four months before I started <a href="https://turevskiy.com/blog/from-one-spanish-deck-to-eighty-four-languages/" target="_blank" rel="noopener noreferrer">5001 Words</a>. The same stretch of trying to learn a language produced both, the timer first.</p>
<h3>A week of making it look right</h3>
<p>The commit log for early October 2025 is almost entirely interface: <em>ui cleanup</em>, <em>ipad ui fixes</em>, <em>UI improvements</em>, <em>best ui</em>, <em>BIG refactoring</em>, <em>moving stuff around</em>, <em>uodated ui</em>. For an app with one button.</p>
<p>That ratio was correct. When the whole product is a button, the button is the product.</p>
<p>Then, on 9 October, two commits that matter more than they look: <em>downgrade to ios16</em>, and immediately after, <em>downgrade to ios 15+, with extended functionality available on ios 16+</em>.</p>
<p>I had built against the current SDK, as you do, and then went and looked at who that excluded. Dropping to iOS 15 meant giving up APIs I had already used and writing the graceful-degradation paths to keep the newer features where they were available. It is unglamorous work that no user will ever notice, and it is the difference between an app for people with new phones and an app for people with phones.</p>
<p>Then nothing for almost a year.</p>
<h3>The rename</h3>
<p>The app worked. I used it daily. There was nothing I wanted from it, so I did not touch it — the same dormancy that 5001 Words went through, only longer.</p>
<p>What broke the silence was Android. On 3 September 2026: <em>preparing for publishing to Android store</em>. And while porting, I had to confront what the thing had actually become.</p>
<p>Somewhere in that year I had stopped using it for Spanish and started using it at my desk. Twenty-five minutes, a break, repeat. I had been running a Pomodoro timer for months without renaming it in my head, because a Pomodoro timer <em>is</em> an interval timer with different numbers and a convention attached.</p>
<p>On 19 September 2026, one commit: <strong>"Rename to 5001 Tomatoes: new icon, translations, screenshots, and 1.3.0 release prep."</strong></p>
<p><em>Pomodoro</em> is Italian for tomato; the technique is named after a kitchen timer shaped like one. The name acknowledged what the app had become and brought it into the same family as the vocabulary app. The engine did not change at all — only the numbers it defaults to, an auto-continue setting for the work-break-work cycle, and statistics so it can answer "did I actually do this today".</p>
<p>The directory name never caught up. I have left it alone, because it is an accurate record of what I set out to build.</p>
<h3>Pomodoro, and everything you can change</h3>
<p>The Pomodoro technique comes from Francesco Cirillo, who timed his study sessions in the late 1980s with a tomato-shaped kitchen timer. The classic recipe is 25 minutes of work and a 5-minute break, with a longer break after every four rounds. Its value is the same as my five minutes: you commit to a short block rather than to finishing the task.</p>
<p>The one button is now two. <strong>Simple</strong> runs a single interval, which is the original app. <strong>Pomodoro</strong> runs the work-and-break cycle. Above them sit today's count, the minutes it adds up to, and progress towards a daily goal.</p>
<figure><img src="https://turevskiy.com/blog/images/tomatoes-home.jpg" alt="The home screen: the two modes, today&#x27;s count and the daily goal." loading="lazy"><figcaption>The home screen: the two modes, today&#x27;s count and the daily goal.</figcaption></figure>
<p>A recipe is only a starting point, so all of its numbers can be changed:</p>
<ul><li><strong>Timer duration:</strong> the length of a Simple interval, 5 minutes by default. The Android listing gives the range as 1 to 60 minutes.</li><li><strong>Work, short break, long break:</strong> set separately, starting from 25, 5 and 15 minutes.</li><li><strong>Continue automatically:</strong> off by default. Off, each break waits for you to tap Continue. On, the cycle runs by itself.</li><li><strong>Pause and resume:</strong> the button can be hidden, if you would rather not have the option.</li><li><strong>Daily goal:</strong> 8 intervals by default, which drives the "0 of 8 today" line on the home screen.</li><li><strong>Language and appearance:</strong> the language follows the device unless you choose another, and the background can be your own photo.</li></ul>
<figure><img src="https://turevskiy.com/blog/images/tomatoes-settings.jpg" alt="Settings: separate durations for work and both breaks, auto-continue, the pause button, the daily goal and language." loading="lazy"><figcaption>Settings: separate durations for work and both breaks, auto-continue, the pause button, the daily goal and language.</figcaption></figure>
<p>Settings save as you change them, and you can open them while a timer is running or paused. That means no save button and no need to stop a session to fix a number.</p>
<p>The running screen stays deliberately plain: a dial, the time left, and two buttons. On Android, an ongoing notification lets you pause, resume or stop without opening the app. A separate statistics screen shows your history as daily bar charts.</p>
<figure><img src="https://turevskiy.com/blog/images/tomatoes-android.png" alt="The running timer on Android." loading="lazy"><figcaption>The running timer on Android.</figcaption></figure>
<p>The app is also not only for Pomodoro. Five minutes of Spanish was its first job, and its store listings also suggest study sessions, exercise sets and meditation, because a timer does not care what you are counting.</p>
<figure><img src="https://turevskiy.com/blog/images/tomatoes-ipad.jpg" alt="The same home screen on iPad." loading="lazy"><figcaption>The same home screen on iPad.</figcaption></figure>
<h3>What it deliberately does not do</h3>
<p>No account. No cloud. No ads. No in-app purchase. No analytics.</p>
<p>Everything lives on the device in the platform's own small-settings store — <code>SharedPreferences</code> on Android, its equivalent on iOS. There is no database, no dependency-injection framework, no sync layer, because a timer that counts to twenty-five needs none of them, and each would be a thing to maintain and a place for data to leak.</p>
<p>That is the stance across all my apps, and it is easier to hold than it sounds: most features that require collecting data are features I did not want.</p>
<h3>The one genuinely hard part</h3>
<p>A timer is trivial until you remember that the operating system does not care about you.</p>
<p>Backgrounded, suspended, killed to reclaim memory — in any of those states an in-process countdown stops existing, and the user is standing in the kitchen waiting for a sound that will never come. A timer that silently fails is worse than no timer, because it accepted a promise and then dropped it.</p>
<p>So the countdown is not the authority. The alarm is registered with the system's own alarm service, which outlives the app being killed, with a notification and completion feedback behind it. The in-app state machine is what you watch; the OS-level alarm is what actually fires. Getting those two to agree across every lifecycle state was most of the real work.</p>
<p>The pleasant things came after that foundation was right: Live Activities, home-screen, lock-screen and StandBy widgets, haptics, and a native Watch app.</p>
<h3>Seventy-six languages for four words</h3>
<p>The Android build ships in 76 locales, iOS in rather more. For an app whose entire vocabulary is roughly <em>start</em>, <em>pause</em>, <em>reset</em> and <em>settings</em>, that is close to absurd.</p>
<p>It is also nearly free. The translations are not maintained twice — a script converts the iOS <code>.strings</code> files into Android's <code>strings.xml</code>, so a wording change made once propagates and the platforms cannot drift. Same discipline as the vocabulary app, which has the same problem at much larger scale.</p>
<p>Why bother for four words? Because the cost is a script, and an app that greets someone in their own language has already said something about who it was built for.</p>
<h3>Not everything needs to grow</h3>
<p>One of these projects became a vocabulary platform across 84 languages. The other found its shape in the first week of October 2025 and has spent the time since getting quieter — fewer taps, more reliable alarms, better behaviour when the OS is hostile, and a wider range of phones it will run on at all.</p>
<p>I think the second outcome is underrated. There is no roadmap. It does the thing.</p>
<hr>
<p><strong>5001 Tomatoes</strong> is free, with no ads and no purchases, on the <a href="https://apps.apple.com/app/id6753728553" target="_blank" rel="noopener noreferrer">App Store</a> and <a href="https://play.google.com/store/apps/details?id=com.turevskiy.fiveminutes" target="_blank" rel="noopener noreferrer">Google Play</a>.</p>]]></content:encoded></item><item><title>From one Spanish deck to eighty-four languages</title><link>https://turevskiy.com/blog/from-one-spanish-deck-to-eighty-four-languages/</link><guid isPermaLink="true">https://turevskiy.com/blog/from-one-spanish-deck-to-eighty-four-languages/</guid><pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>5001 Words</category><category>Yiddish</category><category>Yiddish text-to-speech</category><category>text-to-speech</category><category>language learning</category><category>vocabulary app</category><category>spaced repetition</category><category>German for work</category><category>Pflege</category><category>Kita</category><category>Handwerk</category><category>offline apps</category><category>SwiftUI</category><category>iOS</category><category>Android</category><category>AI-assisted development</category><category>side projects</category><description>5001 Words began as three commits on a February evening because Anki worked and I could not stand looking at it. Nine months later it is 294 decks, two platforms, and a Yiddish text-to-speech voice I had to build myself.</description><content:encoded><![CDATA[<p>On 1 February this year I made three commits to an empty repository: <em>initial version</em>, <em>v2</em>, <em>v3</em>. I was learning Spanish — the same stretch that had already produced <a href="https://turevskiy.com/blog/the-timer-that-changed-its-mind/" target="_blank" rel="noopener noreferrer">a five-minute timer</a> four months earlier — I had tried Anki, and Anki is excellent. It is also, to my eye, punishing to look at. Spaced repetition is a solved problem; the interface was not the thing stopping me, but it was the thing I noticed every single day.</p>
<p>So I wrote my own. One deck, Spanish to English, on an iPhone.</p>
<figure><img src="https://turevskiy.com/blog/images/words-card.jpg" alt="The card view, as it looks in the store listing today." loading="lazy"><figcaption>The card view, as it looks in the store listing today.</figcaption></figure>
<p>Then nothing for four and a half months.</p>
<p>That gap is the part I would skip if I were selling this, and it is the part that matters. The app worked. I used it. I did not touch the code because there was nothing I wanted from it. Most side projects die there, and that is usually correct — the itch is scratched and the tool is done.</p>
<h3>Audio, and then the second language</h3>
<p>On 24 June I committed <em>media and content</em>. Reading a word is not learning a word; I wanted to hear it. Microsoft's edge-tts gives you neural voices for most major languages at no cost and with no licensing problem, so the deck gained pronunciation on both sides of the card.</p>
<p>Later the same day: <em>second language</em>. Then <em>dutch sounds</em>, <em>dutch audio</em>. Once audio existed, adding a language was mostly data, and the thing stopped being a Spanish app.</p>
<h3>The plane</h3>
<p>The next day I committed <em>downloadable audio packages</em>.</p>
<p>I had been on a flight, opened the app, and found it useless. The word lists were local but the audio was not, and a vocabulary app that cannot speak on a plane is a vocabulary app that cannot be used in exactly the dead time it was built for.</p>
<p>The fix is a single <code>.pack</code> file per language — word list plus every audio clip, between 55 and 280 MB depending on the language. The detail I am still pleased with is that the word list sits at the front of the file and is fetched with a range request, so you can start studying a few seconds after tapping download while the audio continues in the background. You do not wait for 200 MB to learn your first word.</p>
<h3>A language a day</h3>
<p>Then it ran away from me. Still 25 June: <em>german</em>, <em>jiddish package</em>, <em>more languages</em>, <em>french</em>, <em>ukrainian</em>. Nineteen commits that month, twenty-four in July, twenty-five in August.</p>
<p>Along the way the app grew an Apple Watch build with the full feature set, a home-screen widget, notifications, auto-play that speaks cards on a timer so you can study without hands, bidirectional decks with separate audio per direction, and a focus mode that drills a subset of what you have not learned. On 19 August: <em>android initial version</em>.</p>
<p>None of that was planned. Each piece existed because I wanted it on a particular day.</p>
<h3>Pflege, Kita, Handwerk</h3>
<p>The turn that made this more than a toy came from reading about people learning German for work.</p>
<p>Not German. German <em>for work</em> — and specifically the vocabulary of care homes, of childcare, of the trades. A nurse arriving from Romania does not need the subjunctive before she needs the word for a pressure sore. The general-purpose courses serve her badly, and the specialised material is thin and expensive.</p>
<p>So the app grew vocational tracks: <strong>Pflege</strong> (care), <strong>Kita</strong> (childcare), <strong>Handwerk</strong> (trades). Each paired with the languages the people doing those jobs actually arrive speaking — Arabic, Bulgarian, Polish, Romanian, Turkish, Ukrainian.</p>
<p>That pairing is the whole point. !<a href="https://turevskiy.com/blog/images/words-decks.jpg" target="_blank" rel="noopener noreferrer">A language page: the general deck first, then decks for specific jobs. This one is Italian.</a></p>
<p>German–English vocational decks already exist. German–Bulgarian care vocabulary, with audio, offline, free, on a phone — that is a gap, and it is a gap precisely because the people in it are not a lucrative market.</p>
<h3>The language Google will translate but not speak</h3>
<p>Yiddish broke the pattern.</p>
<p>Every other language in the app gets its audio from edge-tts, with espeak-ng filling in for Latin, Basque and Scottish Gaelic where no neural voice exists. For Yiddish there was nothing. Google Translate handles the text. It will not speak it. No major provider offers a Yiddish voice, because there is no commercial case for one.</p>
<p>A language written down since the thirteenth century, and you cannot make a computer say a word of it.</p>
<p>So I built the text-to-speech myself, wrapping pretrained FastSpeech2 checkpoints into something that takes Yiddish text and returns audio, then generating the deck's clips in batch. It is now a separate project, and the Yiddish deck ships bundled with the app rather than as a download.</p>
<p>This is the part of the whole story I would defend hardest. The app has 294 decks across 84 languages, and the ones that justify its existence are not the ones with the largest speaker counts.</p>
<h3>Where the limit actually is</h3>
<p>Nine months on, the constraint is not code and it is not my time. It is whether a language has a usable text-to-speech voice and a translation source good enough to trust.</p>
<p>Where both exist, a new deck is close to free. Where neither exists, as with Yiddish, it costs a side project of its own. That is a strange and rather cheering place to end up: the bottleneck on teaching a language is no longer software, it is whether anyone has bothered to build a voice for it.</p>
<h3>On the machine that helped</h3>
<p>I built nearly all of this alongside Claude, and I spent a fair amount of the time deliberately trying to find the edge of what it could do — the Watch target, the pack format, batching a TTS model I had just assembled, localising an interface into dozens of languages I do not read.</p>
<p>It kept up. That is not a small thing to say about nine months of work across two platforms, and it is also not the interesting part. The interesting part is that the decisions that made this app worth using — offline because of one flight, vocational German because of one article, Yiddish because nobody else would — were not technical decisions at all. The machine was very good at the half of the work that was.</p>
<hr>
<p><strong>5001 Words</strong> is free on the <a href="https://apps.apple.com/app/id6784747555" target="_blank" rel="noopener noreferrer">App Store</a> and <a href="https://play.google.com/store/apps/details?id=com.turevskiy.a5001word" target="_blank" rel="noopener noreferrer">Google Play</a>. The vocational decks have their own pages: <a href="https://turevskiy.com/5001words/pflege.html" target="_blank" rel="noopener noreferrer">Pflege</a>, <a href="https://turevskiy.com/5001words/kita.html" target="_blank" rel="noopener noreferrer">Kita</a>, <a href="https://turevskiy.com/5001words/handwerk.html" target="_blank" rel="noopener noreferrer">Handwerk</a>.</p>]]></content:encoded></item><item><title>Rebuilding this site in an evening</title><link>https://turevskiy.com/blog/rebuilding-this-site-in-an-evening/</link><guid isPermaLink="true">https://turevskiy.com/blog/rebuilding-this-site-in-an-evening/</guid><pubDate>Thu, 29 Oct 2026 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>personal site</category><category>nginx</category><category>GoAccess</category><category>analytics</category><category>privacy</category><category>internationalization</category><category>HTML</category><category>AI-assisted development</category><description>A personal site that was a business card with five links, next to a server quietly hosting three shipped apps nobody could find from it. Notes on what I changed and why.</description><content:encoded><![CDATA[<p>For years turevskiy.com was a single dark page: my name, a job title, five links. It loaded fast and said nothing.</p>
<p>Meanwhile the same server was hosting three apps I had actually shipped — a vocabulary trainer, a focus timer, a multiplayer game that runs in the browser — and the homepage linked to none of them. Someone landing on the site had no way to know any of it existed. That is the kind of thing you stop seeing after the hundredth visit to your own domain.</p>
<p>So I rebuilt it. These are the decisions worth writing down.</p>
<h3>The CV was eleven months stale</h3>
<p><code>cv/resume.pdf</code> was dated 5 June. The version I would actually send someone was on my laptop. That gap is embarrassing in a specific way: anyone who found the site through search got an out-of-date picture of me while I was busy sending the current one by email.</p>
<p>The fix was boring — regenerate, upload, and point the old URL at the new file so existing bookmarks silently get the current version rather than a 404 or a stale download. The old copy is still on disk under a dated name. Nothing that was ever public should simply vanish.</p>
<h3>Three languages, not four</h3>
<p>The site now exists in English, German and Ukrainian. English is the default, German because I live and work in Germany, Ukrainian because it is my first language and because the audience is real.</p>
<p>I considered Hebrew — I have worked in Israel, and my CV lists it. I left it out, and the reason is not political. I rate myself a beginner in Hebrew, which means I could not proofread my own professional copy. Publishing text you cannot check is worse than not publishing it. If I target Israeli employers later, I will add it with a native speaker reading it first.</p>
<p>Each language gets its own path, <code>hreflang</code> annotations, and a switcher that does not open new tabs. Everything else on the page does, so that clicking a project does not cost you the page you were reading.</p>
<h3>Analytics without a cookie banner</h3>
<p>I wanted to know whether anyone reads this, and specifically which language of my CV gets downloaded. The reflex is to drop in a JavaScript tracker.</p>
<p>I used nginx's own access logs instead, with GoAccess generating a static report hourly. No JavaScript, no cookies, no consent banner, nothing to block — and, critically, <strong>it counts PDF downloads</strong>, which a client-side tracker cannot see at all. The file never executes any of my code; the request simply lands in a log.</p>
<p>Two things went wrong immediately, both instructive. First, every site on the box shared one access log, and the <code>combined</code> log format carries no host field — so the numbers mixed five unrelated domains together with no way to separate them. Second, of roughly 129,000 requests over nine days, 8,000 were an uptime monitor and another 24,000 were crawlers, plus a steady drizzle of probes for <code>/wp-admin</code>, <code>/.env</code> and <code>/phpmyadmin</code> that belongs to every public IP address.</p>
<p>Had I just read the headline number I would have believed my personal site gets 19,000 visits to its front page. It does not. Per-site logging and an explicit filter now, and the honest consequence is that my statistics start from today.</p>
<h3>No chatbot</h3>
<p>I build grounded assistants for a living. The obvious move is to put one on the site that answers questions about my CV.</p>
<p>I decided against it. A public language-model endpoint on a personal site is a bill and a prompt-injection surface, and this is the one page where my credibility has to be unimpeachable — the page where someone checks whether I am worth a conversation. The failure mode is not that it costs money. It is that somebody screenshots my own site saying something stupid.</p>
<p>There is a better version of the same instinct, which is to describe the work plainly in a section about it, including the part that actually matters: every figure the real assistant reports is computed server-side from role-scoped endpoints, never generated by the model, and every device action it offers passes through a confirmation step. In a supervised-autonomy fleet that boundary is not a caveat on the design. It is the design.</p>
<h3>The bug</h3>
<p>I made the project cards clickable by wrapping each one in an anchor. Inside each card were two more anchors, to the App Store and Google Play.</p>
<p>Nested anchors are invalid HTML. The browser does not error — it silently un-nests them, which pushed the store links out of their cards and into the grid as siblings. The layout collapsed into a row of empty boxes with orphaned buttons floating between them, and because I was reading the markup rather than the rendering, it took a screenshot to see it.</p>
<p>The fix is the standard stretched-link pattern: the card is an <code>&lt;article&gt;</code>, the title holds the only real link, and a pseudo-element on it covers the whole card. Sibling links sit above that with <code>z-index</code>, so they keep working. One container, one primary target, no nesting.</p>
<h3>Written with a machine, by me</h3>
<p>I used an AI assistant throughout — drafting copy, generating three language variants, writing the build scripts, and reviewing the rendered output.</p>
<p>I am not labelling the pages as AI-made. Nobody credits their editor or their compiler, and on a page whose entire job is credibility, a badge like that invites the question of what else was generated: the claims, the commit counts, the years. Every number on the site is checkable, and that is the thing I want load-bearing.</p>
<p>What I will say, because it is the actual skill, is that the judgment about what to hand over and what to read line by line is the whole job now. The machine wrote the nested anchors. It also caught them, and then fixed them properly rather than hacking around the symptom. Both of those are worth knowing about.</p>]]></content:encoded></item><item><title>com.docker.vmnet  daemon is blocked as a malware</title><link>https://turevskiy.com/blog/com-docker-vmnet-daemon-is-blocked-as-a-malware/</link><guid isPermaLink="true">https://turevskiy.com/blog/com-docker-vmnet-daemon-is-blocked-as-a-malware/</guid><pubDate>Fri, 17 Jan 2025 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Docker</category><category>macOS</category><category>troubleshooting</category><description>Today I restarted my MacBook and was so &quot;happy&quot; to see a message that my Docker com.docker.vmnet daemon is blocked as a malware by MacOS. They said it did not harm. Haha.…</description><content:encoded><![CDATA[<p>Today I restarted my MacBook and was so "happy" to see a message that my Docker com.docker.vmnet  daemon is blocked as a malware by MacOS. They said it did not harm. Haha.<br /><br />Shortly, this just helped<br /> https://github.com/docker/for-mac/issues/7520<br /><br />Thank you world!</p>]]></content:encoded></item><item><title>Free, automatic and reliable SSL cerificates for both *nix and Windows - now possible!</title><link>https://turevskiy.com/blog/free-automatic-and-reliable-ssl-cerificates-for-both-nix-and/</link><guid isPermaLink="true">https://turevskiy.com/blog/free-automatic-and-reliable-ssl-cerificates-for-both-nix-and/</guid><pubDate>Wed, 15 May 2019 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Let&#x27;s Encrypt</category><category>SSL</category><category>HTTPS</category><category>Windows</category><category>Linux</category><description>I have one more dream coming true - free and automatic SSL cerificates for both *nix and Windows. So it is https://letsencrypt.org/ Thanks guys!!!</description><content:encoded><![CDATA[<br />I have one more dream coming true - free and automatic SSL cerificates for both *nix and Windows.<br />
<br />
So it is <a href="https://letsencrypt.org/">https://letsencrypt.org/</a><br /><br />Thanks guys!!!]]></content:encoded></item><item><title>C++ extensions for Node.js - do absolutely what you want from your JavaScript!</title><link>https://turevskiy.com/blog/c-extensions-for-node-js-do-absolutely-what-you-want-from-yo/</link><guid isPermaLink="true">https://turevskiy.com/blog/c-extensions-for-node-js-do-absolutely-what-you-want-from-yo/</guid><pubDate>Fri, 24 Aug 2018 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Node.js</category><category>C++</category><category>JavaScript</category><category>native addons</category><description>Hi, Just found and tried this amazing technology - you really can write native c++ addons for JavaScript running in Node.js!! https://nodejs.org/api/addons.html It was th…</description><content:encoded><![CDATA[Hi,<br />
<br />
Just found and tried this amazing technology - you really can write native c++ addons for JavaScript running in Node.js!!<br />
<br />
https://nodejs.org/api/addons.html<br /><br />It was the last thing I was in doubt Node.js can do. Now I love it for 100%! :-)]]></content:encoded></item><item><title>Let&#x27;s do TopTal!</title><link>https://turevskiy.com/blog/let-s-do-toptal/</link><guid isPermaLink="true">https://turevskiy.com/blog/let-s-do-toptal/</guid><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>freelancing</category><category>Toptal</category><category>outsourcing</category><description>Hi Everybody, That time has come. 20 years spent mostly in outsourcing companies were great but proper team of freelance expert is something that should overcome it. Why …</description><content:encoded><![CDATA[Hi Everybody,<br /><br />That time has come.<br />20 years spent mostly in outsourcing companies were great but proper team of freelance expert is something that should overcome it.<br />Why not try TopTal?<br /><br />Lots of my friends already told me it is cool so I can not refrain from joining!<br />Will it be a success?<br />Will see!]]></content:encoded></item><item><title>SeeYourTravel source code is in public domain</title><link>https://turevskiy.com/blog/seeyourtravel-source-code-is-in-public-domain/</link><guid isPermaLink="true">https://turevskiy.com/blog/seeyourtravel-source-code-is-in-public-domain/</guid><pubDate>Wed, 23 Aug 2017 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>SeeYourTravel</category><category>open source</category><category>travel</category><category>startups</category><description>Hi, Just if you did not mention yet - SeeYourTravel.com travel community source code is freely available at https://github.com/oltur/seeyourtravel.com Nice sharing!</description><content:encoded><![CDATA[Hi,<br />
<br />
Just if you did not mention yet - SeeYourTravel.com travel community source code is freely available at <a href="https://github.com/oltur/seeyourtravel.com">https://github.com/oltur/seeyourtravel.com</a><br />
<br />
Nice sharing!]]></content:encoded></item><item><title>Hello Protractor!</title><link>https://turevskiy.com/blog/hello-protractor/</link><guid isPermaLink="true">https://turevskiy.com/blog/hello-protractor/</guid><pubDate>Wed, 23 Aug 2017 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Protractor</category><category>end-to-end testing</category><category>testing</category><category>JavaScript</category><category>Jasmine</category><description>Hi Everybody! Just eager to share my excitement about the first real-world usage of Protractor e2e tests. It is really cool, intuitive and sexy, even despite it was not a…</description><content:encoded><![CDATA[Hi Everybody!<br />
<br />
Just eager to share my excitement about the first real-world usage of Protractor e2e tests.<br />
<br />
It is really cool, intuitive and sexy, even despite it was not an Angular app initially targeted by protractor! :-)<br />
<br />
Created with VS.code both on Windows and Mac. <br />
Used nice helpers and page objects, tried data access for future data-driven tests.<br />
<br />
<br />
Source code is here:<br />
<a href="https://github.com/oltur/MSM-protractor-tests">https://github.com/oltur/MSM-protractor-tests</a><br />
<br />
To be continued!!]]></content:encoded></item><item><title>Memory Exception with SQL Server queries</title><link>https://turevskiy.com/blog/memory-exception-with-sql-server-queries/</link><guid isPermaLink="true">https://turevskiy.com/blog/memory-exception-with-sql-server-queries/</guid><pubDate>Sun, 05 Mar 2017 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>SQL Server</category><category>in-memory tables</category><category>Redis</category><category>benchmarking</category><category>troubleshooting</category><description>Hi, I have tried to benchmark the SQl Server vs SQL Service InMemory tables vs. Redis, and got a weird error while intensively working with InMemory tables: System.Data.S…</description><content:encoded><![CDATA[Hi,<br />
<br />
I have tried to benchmark the SQl Server vs SQL Service InMemory tables vs. Redis,<br />
and got a weird error while intensively working with InMemory tables:<br />
<b>System.Data.SqlClient.SqlException: There is insufficient system memory in resource pool 'default' to run this query</b><br />
The data volumes were absolutely reasonable<br />
<br />
It appeared that to fix it we need to limit the SQL Server memory!:) Otherwise it tries to use memory over available.]]></content:encoded></item><item><title>Delete in JavaScript</title><link>https://turevskiy.com/blog/delete-in-javascript/</link><guid isPermaLink="true">https://turevskiy.com/blog/delete-in-javascript/</guid><pubDate>Thu, 09 Feb 2017 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>JavaScript</category><category>C++</category><description>Hello here, After spending couple of hours understanding why my C++ - baked understanding of delete operator does not work in JavaScript I just another time understood th…</description><content:encoded><![CDATA[Hello here,<br />
<p>
<br /></p>
<p>
After spending couple of hours understanding why my C++  - baked understanding of delete operator does not work in JavaScript I just another time understood that JavaScript is completely special language.<br />
<br />
Here is a deep discussion of how it works:</p>
<p>
<a href="http://perfectionkills.com/understanding-delete/">http://perfectionkills.com/understanding-delete/</a> </p>
<p>
<br /></p>
<p>
Tags:<br />
JavaScript delete property delete variable</p>]]></content:encoded></item><item><title>Better client-server communication: replacing AJAX with WebSockets</title><link>https://turevskiy.com/blog/better-client-server-communication-replacing-ajax-with-webso/</link><guid isPermaLink="true">https://turevskiy.com/blog/better-client-server-communication-replacing-ajax-with-webso/</guid><pubDate>Mon, 09 Jan 2017 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>WebSockets</category><category>AJAX</category><category>JavaScript</category><category>BigQuery</category><category>web performance</category><description>We have quite complex reporting application, that was done several dozens AJAX requests from client side to generate a single report. Typically it is either JSON of query…</description><content:encoded><![CDATA[<p>
<br /></p>
We have quite complex reporting application, that was done several dozens AJAX requests from client side to generate a single report. Typically it is either JSON of query results from Google BigQuery or HTML generated by a partial form based on such query results.<br />
Each query could take several seconds to fulfill,  and in view of browser limitations for the number of simultaneous connections, the throttling was visually annoying.<br /><br />
I've got a request to parallelize the requests whenever possible. There were choice of server pulling vs. WebSockets pipeline, so latest is definitely more nice and interesting.<br />
<br />
Below is the skeleton of the solution in C# and JavaScript.<br />
<br />
The server implementation has several interesting moments: except of WebSockets itself, it provides the partial views by rendering them server-side, preserving the context and locale of ASP.NET main thread. Not sure if it is nice approach, but it works so far.<br /><br />
<b>Service implementation:</b><br />
<blockquote class="tr_bq">
    public class ReportsApiController : ApiController    {        [System.Web.Http.HttpGet]        public HttpResponseMessage GetQueryResultByWebSocket()        {            HttpContext.Current.AcceptWebSocketRequest(new MyWebSocketHandler(this));            return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);        }</blockquote>
                           ....<br />
<blockquote class="tr_bq">
    }</blockquote>
<blockquote class="tr_bq">
    internal class MyWebSocketHandler : WebSocketHandler<br />    {<br />        private static WebSocketCollection _connectedClients;<br />        private ApiController _controller;<br />        static MyWebSocketHandler()<br />        {<br />            _connectedClients = new WebSocketCollection();<br />        }<br />        public MyWebSocketHandler(ApiController controller)<br />        {<br />            this._controller = controller;<br />        }<br />        public override void OnOpen()<br />        {<br />            base.OnOpen();<br />            _connectedClients.Add(this);<br />        }<br />        public override void OnClose()<br />        {<br />            _connectedClients.Remove(this);<br />            base.OnClose();<br />        }<br />        public override void OnMessage(string message)<br />        {<br />            HttpContext ctx = HttpContext.Current;<br />            var currentCulture = System.Threading.Thread.CurrentThread.CurrentCulture;<br />            var currentUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture;<br />            new Task(() =&gt;<br />            {<br />                HttpContext.Current = ctx;<br />                System.Threading.Thread.CurrentThread.CurrentCulture = currentCulture;<br />                System.Threading.Thread.CurrentThread.CurrentUICulture = currentUICulture;<br />                var request =<br />                    JsonConvert.DeserializeObject<umiwebsocketrequest>(message);<br />                Debug.WriteLine("Received #{0} @{1} t:{2}", request.id, DateTime.Now,<br />                    Thread.CurrentThread.ManagedThreadId);<br />                try<br />                {<br />                    if (request.url == "/api/ReportsApi/GetQueryResult")<br />                    {<br />                        var data = MyQueriesModel.GetQueryResult(request.queryParams);<br />                        UmiWebSocketResponse result = new UmiWebSocketResponse<br />                        {<br />                            data = data,<br />                            error = null,<br />                            id = request.id,<br />                            timeStampSent = request.timeStampSent<br />                        };<br />                        Debug.WriteLine("Sent #{0} @{1} t:{2}", request.id, DateTime.Now,<br />                            Thread.CurrentThread.ManagedThreadId);<br />                        this.Send(JsonConvert.SerializeObject(result));<br />                    }<br />                    else if (request.url == "/api/ReportsApi/GetQueryObjResult")<br />                    {<br />                        var jss = new JavaScriptSerializer();<br />                        jss.MaxJsonLength = int.MaxValue;<br />                        var data = jss.Serialize(UmiQueriesModel.GetQueryObj(request.queryParams));<br />                        UmiWebSocketResponse result = new UmiWebSocketResponse<br />                        {<br />                            data = data,<br />                            error = null,<br />                            id = request.id,<br />                            timeStampSent = request.timeStampSent<br />                        };<br />                        Debug.WriteLine("Sent #{0} @{1} t:{2}", request.id, DateTime.Now,<br />                            Thread.CurrentThread.ManagedThreadId);<br />                        this.Send(JsonConvert.SerializeObject(result));<br />                    }<br />                    else if (request.url == "/api/ReportsApi/GetResource")<br />                    {<br />                        var jss = new JavaScriptSerializer();<br />                        jss.MaxJsonLength = int.MaxValue;<br />                        var prefix = "MyLibrary.Resources.Reports.";<br />                        try<br />                        {<br />                            var fullNames = request.queryParams.QueryName.Split(',');<br />                            var data = new Dictionary<string string="">();<br />                            foreach (var fullName in fullNames)<br />                            {<br />                                int idx = fullName.LastIndexOf('.');<br />                                var typeName = fullName.Substring(0, idx);<br />                                var fullTypeName = typeName;<br />                                var resourceName = fullName.Substring(idx + 1);<br />                                if (!fullTypeName.StartsWith(prefix))<br />                                    fullTypeName = prefix + fullTypeName;<br />                                try<br />                                {<br />                                    var rm = new ResourceManager(fullTypeName, Assembly.GetExecutingAssembly());<br />                                    data.Add(typeName + "." + resourceName, rm.GetString(resourceName));<br />                                }<br />                                catch (Exception ex)<br />                                {<br />                                    Debug.WriteLine("Error performing resource request: {0}",<br />                                        request.queryParams == null ? "null" : request.queryParams.QueryName);<br />                                    data.Add(typeName + "." + resourceName, "");<br />                                }<br />                            }<br />                            UmiWebSocketResponse result = new UmiWebSocketResponse<br />                            {<br />                                data = jss.Serialize(data),<br />                                error = null,<br />                                id = request.id,<br />                                timeStampSent = request.timeStampSent<br />                            };<br />                            Debug.WriteLine("Sent #{0} @{1} t:{2}", request.id, DateTime.Now,<br />                                Thread.CurrentThread.ManagedThreadId);<br />                            this.Send(jss.Serialize(result));<br />                        }<br />                        catch (Exception ex)<br />                        {<br />                            Debug.WriteLine("Error performing resource request: {0}",<br />                                request.queryParams == null ? "null" : request.queryParams.QueryName);<br />                            UmiWebSocketResponse result = new UmiWebSocketResponse<br />                            {<br />                                data = "",<br />                                error = "Error performing resource request",<br />                                id = request.id,<br />                                timeStampSent = request.timeStampSent<br />                            };<br />                            Debug.WriteLine("Sent #{0} @{1} t:{2}", request.id, DateTime.Now,<br />                                Thread.CurrentThread.ManagedThreadId);<br />                            this.Send(jss.Serialize(result));<br />                        }<br />                    }<br />                    else<br />                    {<br />                        var routeToSearch = request.url.Replace("/PartialViews/", "");<br />                        var methods = Assembly.GetExecutingAssembly().GetTypes()<br />                          .Where(t =&gt; t.IsSubclassOf(typeof(PartialViewsController)))<br />                          .SelectMany(t =&gt; t.GetMethods())<br />                          .Where(m =&gt;<br />                          {<br />                              if (m.Name == routeToSearch)<br />                                  return true;<br />                              System.Web.Mvc.RouteAttribute attr =<br />                                  m.GetCustomAttributes(typeof(System.Web.Mvc.RouteAttribute), false).FirstOrDefault() as System.Web.Mvc.RouteAttribute;<br />                              if (attr == null)<br />                                  return false;<br />                              return attr.Template == routeToSearch;<br />                          })<br />                          .ToArray();<br />                        if (methods.Length &gt; 0)<br />                        {<br />                            var controller = Activator.CreateInstance(methods[0].DeclaringType) as PartialViewsController;<br />                            string baseUrl = HttpContext.Current<br />                                .Request<br />                                .Url<br />                                .GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped);<br />                            var routeData = new RouteData();<br />                            routeData.Values.Add("controller", controller.GetType().Name.Replace("Controller", ""));<br />                            var controllerContext = new ControllerContext(<br />                                new HttpContextWrapper(<br />                                    new HttpContext(<br />                                        new HttpRequest(null, baseUrl + request.url, null),<br />                                        new HttpResponse(null))),<br />                                routeData, controller);<br />                            controller.ControllerContext = controllerContext;<br />                            PartialViewResult pvr;<br />                            if (methods[0].GetParameters().Length == 2)<br />                            {<br />                                pvr = methods[0].Invoke(controller, new object[] { request.queryParams, Type.Missing }) as PartialViewResult;<br />                            }<br />                            else if (methods[0].GetParameters().Length == 1)<br />                            {<br />                                pvr = methods[0].Invoke(controller, new object[] { request.queryParams }) as PartialViewResult;<br />                            }<br />                            else<br />                            {<br />                                pvr = methods[0].Invoke(controller, new object[0]) as PartialViewResult;<br />                            }<br />                            var data = pvr == null ? null : ConvertPartialViewToString(controller.ControllerContext, pvr);<br />                            UmiWebSocketResponse result = new UmiWebSocketResponse<br />                            {<br />                                data = data,<br />                                error = null,<br />                                id = request.id,<br />                                timeStampSent = request.timeStampSent<br />                            };<br />                            Debug.WriteLine("Sent #{0} @{1} t:{2}", request.id, DateTime.Now,<br />                                Thread.CurrentThread.ManagedThreadId);<br />                            this.Send(JsonConvert.SerializeObject(result));<br />                        }<br />                    }<br />                }<br />                catch (Exception ex)<br />                {<br />                    UmiWebSocketResponse errorResult = new UmiWebSocketResponse<br />                    {<br />                        error = ex.Message,<br />                        data = null,<br />                        id = request.id,<br />                        timeStampSent = request.timeStampSent<br />                    };<br />                    this.Send(JsonConvert.SerializeObject(errorResult));<br />                }<br />            }).Start();<br />        }<br />        private string ConvertPartialViewToString(ControllerContext controllerContext, PartialViewResult partialView)<br />        {<br />            using (var sw = new StringWriter())<br />            {<br />                partialView.View = ViewEngines.Engines<br />                  .FindPartialView(controllerContext, partialView.ViewName).View;<br />                var vc = new ViewContext(<br />                  controllerContext, partialView.View, partialView.ViewData, partialView.TempData, sw);<br />                partialView.View.Render(vc, sw);<br />                var partialViewString = sw.GetStringBuilder().ToString();<br />                return partialViewString;<br />            }<br />        }<br />    }</string></umiwebsocketrequest></blockquote>
<p>
<br /></p>
<br />
<b><br /></b>
<b>Client code sample:</b><br />
<blockquote class="tr_bq">
    my.infrastructure.webSocketHandler.send({ dataType: 'html', url: "/PartialViews/MyPartialView" }, function (result) {        // do something with result    });</blockquote>
<p>
<br /></p>
<br />
<b>JavaScript client library implementation:</b><br />
<br />
<blockquote>
my.infrastructure.useWebSocket = true;<br />my.infrastructure.webSocketHandler = (function () {<br />    var uri = (location.protocol === "https:" ? "wss://" : "ws://").concat(window.location.hostname).concat("/api/reportsApi/GetQueryResultByWebSocket");<br />    var requests = {<br />    };<br />    var counter = 0;<br />    var websocket;<br />    if (my.infrastructure.useWebSocket) {<br />        websocket = new WebSocket(uri);<br />        websocket.onopen = function () {<br />            console.log("Connected to Web Socket");<br />        };<br />        websocket.onerror = function (event) {<br />            console.log("ERROR CONNECTING TO WEB SOCKET");<br />        };<br />        websocket.onmessage = function (event) {<br />            var message = JSON.parse(event.data);<br />            message.timeStampReceived = new Date();<br />            var id = message.id;<br />            if (requests[id]) {<br />                var request = requests[id];<br />                console.log(message.timeStampReceived.toISOString() + ": Web Socket response received from " + request.request.url + " (queryName: " + (request.request.queryParams ? request.request.queryParams.queryName : "undefined") + "): "<br />                    //+ JSON.stringify(message)<br />                    );<br />                if (message.Error) {<br />                    request.onError(message.error);<br />                } else {<br />                    var data = message.data;<br />                    request.onSuccess(data);<br />                }<br />                delete requests[request[id]];<br />            }<br />        };<br />    }<br />    function sendRequest(settings, onSuccess, onError) {<br />        if (!my.infrastructure.useWebSocket) {<br />            // do some AJAX if asked for<br />        }<br />        var id = counter++;<br />        var message = {<br />            id: id,<br />            queryParams: settings.data,<br />            url: settings.url,<br />            type: settings.type,<br />            dataType: settings.dataType,<br />            timeStampSent: new Date()<br />        };<br />        var messageStr = JSON.stringify(message);<br />        requests[id] = {<br />            onSuccess: onSuccess, onError: onError, request: message<br />        };<br />        waitForSocketConnection(websocket, function () {<br />            console.log(message.timeStampSent.toISOString() + ": Web Socket message sent to " + settings.url + " (queryName: " + (message.queryParams ? message.queryParams.queryName : "undefined") + "): "<br />                //+ messageStr<br />                );<br />            websocket.send(messageStr);<br />        });<br />    }<br />    function abortAll() {<br />        requests = {<br />        };<br />    }<br />    function abortQuery(queryName) {<br />        $.each(requests, function (index, request) {<br />            if (request &amp;&amp; request.request.queryName === queryName) {<br />                delete requests[request[id]];<br />            }<br />        });<br />    }<br />    function waitForSocketConnection(socket, callback) {<br />        var retryCounter = 3000; // 5 minutes<br />        setTimeout(<br />            function () {<br />                if (socket.readyState === 1) {<br />                    if (callback != null) {<br />                        callback();<br />                    }<br />                    return;<br />                } else {<br />                    if (retryCounter-- &gt; 0) {<br />                        console.log("Waiting for Web Socket connection...");<br />                        waitForSocketConnection(socket, callback);<br />                    }<br />                }<br />            }, 100);<br />    }<br />    return {<br />        send: sendRequest,<br />        abortAll: abortAll,<br />        abortQuery: abortQuery<br />    }<br />})();</blockquote>]]></content:encoded></item><item><title>Diving into Angular 2 and NODE.JS world</title><link>https://turevskiy.com/blog/diving-into-angular-2-and-node-js-world/</link><guid isPermaLink="true">https://turevskiy.com/blog/diving-into-angular-2-and-node-js-world/</guid><pubDate>Sun, 01 Jan 2017 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Angular</category><category>Node.js</category><category>npm</category><category>Visual Studio Code</category><category>JavaScript</category><description>Hi Everybody, I will post here my findings in a way of learning Angular 2, VS.CODE and probably MongoDB. So keep updated. Avoiding the dependencies versioning hell Once I…</description><content:encoded><![CDATA[<br />
Hi Everybody,<br />
<br />
I will post here my findings in a way of learning Angular 2, VS.CODE and probably MongoDB. So keep updated.<br />
<br />
<b>Avoiding the dependencies versioning hell</b><br /><br />
Once I was stuck with non-compilable NPM-based project because of newly added libraries have brought their incompatible dependencies' version. It was sad and I expected a lot of manual fixes.<br />
But the life is better! Thanks josh3736 from StackOverflow:<br />
<br />
<br />
<p>
Looks like <a href="https://www.npmjs.org/package/npm-check-updates" rel="nofollow noreferrer">npm-check-updates</a> is the only way to make this happen now.</p>
<pre class="lang-js prettyprint prettyprinted"><code>npm i -g npm-check-updates
npm-check-updates -u
npm install</code></pre>
<br />
<a href="http://stackoverflow.com/questions/16073603/how-do-i-update-each-dependency-in-package-json-to-the-latest-version" target="_blank">http://stackoverflow.com/questions/16073603/how-do-i-update-each-dependency-in-package-json-to-the-latest-version </a>]]></content:encoded></item><item><title>IIS conflicts for port 80 with some applications: 503 Server unavailable or showing Windows Authentication system login dialog</title><link>https://turevskiy.com/blog/iis-conflicts-for-port-80-with-some-applications-503-server-/</link><guid isPermaLink="true">https://turevskiy.com/blog/iis-conflicts-for-port-80-with-some-applications-503-server-/</guid><pubDate>Mon, 10 Oct 2016 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>IIS</category><category>Windows</category><category>HTTP</category><category>troubleshooting</category><description>Just spent another half of day solving the same problem already solved before. If you have these symptoms: * Http error 503 Server Not Available or * System login dialog …</description><content:encoded><![CDATA[Just spent another half of day solving the same problem already solved before.<br />
<br />
If you have these symptoms:<br />
* Http error 503 Server Not Available<br />
or<br />
* System login dialog even as you have no Windows authentication enabled<br />
<br />
First of all, look at MS SQL Server Reporting Services configuration.<br />
It creates TWO sites during install at localhost:80/Reports and localhost:80/ReportServer.<br />
<br />
You should move both of them to other ports using Reporting Services Configuration Manager utility<br />
<br />
The deep root reason is described here:<br />
https://blogs.msdn.microsoft.com/webtopics/2010/02/17/a-not-so-common-root-cause-for-503-service-unavailable/<br />
<br />
Another reason possible is Skype messenger. It also likes to take the port 80 preventing others from using it.<br />
<br />
Good luck!<br /><br />PS: Don 't forget about another very simple reason for Error 503: <br />You might be running your site in IIS under an Application Pool with user account credentials, and your password has just expired!<br />]]></content:encoded></item><item><title>One more great promotion tool - LinkedIn!</title><link>https://turevskiy.com/blog/one-more-great-promotion-tool-linkedin/</link><guid isPermaLink="true">https://turevskiy.com/blog/one-more-great-promotion-tool-linkedin/</guid><pubDate>Mon, 26 Oct 2015 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>LinkedIn</category><category>marketing</category><category>startups</category><category>SeeYourTravel</category><description>Good morning, and let any fall&#x27;s rains make you even happier! :) I got a great idea today - create company profile at LinkedIn. Looks self-evident, but anyway exciting. S…</description><content:encoded><![CDATA[Good morning, and let any fall's rains make you even happier! :)<br />
<br />
I got a great idea today - create company profile at LinkedIn.<br />
Looks self-evident, but anyway exciting.<br />
<br />
So Ta-Da! <a href="https://www.linkedin.com/company/seeyourtravel-com">https://www.linkedin.com/company/seeyourtravel-com</a>.<br />
<br />
Good promotion opportunities and great free visibility!]]></content:encoded></item><item><title>Wow! CRM!! HubSpot.com!!!</title><link>https://turevskiy.com/blog/wow-crm-hubspot-com/</link><guid isPermaLink="true">https://turevskiy.com/blog/wow-crm-hubspot-com/</guid><pubDate>Fri, 16 Oct 2015 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>CRM</category><category>HubSpot</category><category>startups</category><category>SeeYourTravel</category><description>Hi there! I have just created an account @ HubSpot and it looks cool!! We have used MS CRM before.. Well so many regrets we&#x27;ve spent time for it.. And most exciting so fa…</description><content:encoded><![CDATA[Hi there!<br /><br />I have just created an account @ HubSpot and it looks cool!!<br /><br />We have used MS CRM before.. Well so many regrets we've spent time for it..<br />And most exciting so far - HubSpot even has Kanban board for tasks!!!]]></content:encoded></item><item><title>Welcome to startup world!</title><link>https://turevskiy.com/blog/welcome-to-startup-world/</link><guid isPermaLink="true">https://turevskiy.com/blog/welcome-to-startup-world/</guid><pubDate>Thu, 15 Oct 2015 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>startups</category><category>SeeYourTravel</category><category>travel</category><description>Hello everybody! Some time ago I have started my small startup - seeyourtravel.com . I have got many insight and wonderful experience out of it so would like to share the…</description><content:encoded><![CDATA[Hello everybody!<br />
<br />
Some time ago I have started my small startup - <a href="http://seeyourtravel.com/" target="_blank">seeyourtravel.com</a>.<br />
I have got many insight and wonderful experience out of it so would like to share them with you in this blog. It is about technology, business, integration, media, PR, hosting, travel industry and many other things around.<br /><br />
Follow, more will come soon!]]></content:encoded></item><item><title>Startup technology agenda - what addresses mean to you</title><link>https://turevskiy.com/blog/startup-technology-agenda-what-addresses-mean-to-you/</link><guid isPermaLink="true">https://turevskiy.com/blog/startup-technology-agenda-what-addresses-mean-to-you/</guid><pubDate>Thu, 15 Oct 2015 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>startups</category><category>SeeYourTravel</category><category>web apps</category><category>integrations</category><description>So, let&#x27;s start our strartup journey. First, let&#x27;s remember all links, sites, urls and integrations we have in some way related to our baby, and maybe some more we would …</description><content:encoded><![CDATA[So, let's start our strartup journey.<br />
<br />
First, let's remember all links, sites, urls and integrations we have in some way related to our baby, and maybe some more we would like to be related. Just a shortlist now, with more details to come<br />
<br />
<ul>
<li>Hosting: GoDaddy.com now (never again!!!), <a href="http://schnellnode.de/">schnellnode.de</a> (fantastically cheap, reliable and nice German hosting guys!)</li>
<ul>
<li>At least two parts: main site at <a href="http://seeyourtravel.com/">seeyourtravel.com</a> and corporate presence/contacts at <a href="http://seeyourtravel.com/corporate">seeyourtravel.com/corporate</a></li>
</ul>
<li>Facebook: <a href="http://facebook.com/seeyourtravel">facebook.com/seeyourtravel</a> - just the must in today's world</li>
<li>Twitter: <a href="http://twitter.com/seeyourtravel">twitter.com/seeyourtravel</a> - be annoying enough!</li>
<li>Blog: <a href="http://seeyourtravel.blogspot.com/">seeyourtravel.blogspot.com</a>. Need to pay more time and attention to that yet!</li>
<li>Analytics</li>
<ul>
<li>Google Analytics. Free and powerful. <a href="http://www.google.com/analytics/">www.google.com/analytics/</a></li>
<li>Clicky: <a href="http://clicky.com/">clicky.com/</a></li>
</ul>
<li>YouTube: <a href="http://www.youtube.com/watch?v=2bSKNKqO2Uo">www.youtube.com/watch?v=2bSKNKqO2Uo</a> </li>
<li>Google+</li>
<li>Feedback analysis from <a href="http://visitorengage.com/">VisitorEngage.com</a></li>
</ul>
<p>
Thinking of:</p>
<p>
<ul>
<li>Flickr</li>
<li>Instagram</li>
</ul>
</p>]]></content:encoded></item><item><title>Libraries all around</title><link>https://turevskiy.com/blog/libraries-all-around/</link><guid isPermaLink="true">https://turevskiy.com/blog/libraries-all-around/</guid><pubDate>Thu, 15 Oct 2015 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>startups</category><category>SeeYourTravel</category><category>open source</category><category>software libraries</category><category>Leaflet</category><description>The second question I would like to stop on in my startup journey are libraries. Not the paper ones this time, but software components we are so dependent on. So what we …</description><content:encoded><![CDATA[The second question I would like to stop on in my startup journey are libraries. Not the paper ones this time, but software components we are so dependent on.<br />So what we wouldn't be where we are without:<br />
<br />
<ul>
<li>Leaflet and its plugins for maps: <a href="http://leafletjs.com/">leafletjs.com/</a> ! Free, stable, and so pleaseant to use!</li>
<li>i18next for translations. Nice!</li>
<li>jstorage - good wrapper for HTML client storage stuff</li>
<li>toastr - simply cool</li>
<li>Carousels.... Still a tough question - looking for perfection.</li>
<li>JQuery, JQuery UI and its plugins,  and all the standard stuff</li>
</ul>
<p>
Integration libraries or Web API, to be moved to separate article: facebook, google, panoramio,</p>]]></content:encoded></item><item><title>Note: browser-based 3D modelling with Clara.io</title><link>https://turevskiy.com/blog/note-browser-3d-modelling-clara-io/</link><guid isPermaLink="true">https://turevskiy.com/blog/note-browser-3d-modelling-clara-io/</guid><pubDate>Wed, 26 Nov 2014 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>3D modelling</category><category>Clara.io</category><category>web apps</category><description>I have found a nice browser-based 3D modelling solution - http://Clara.io . It is amazing what is possible online nowadays :-)</description><content:encoded><![CDATA[<br />
I have found a nice browser-based 3D modelling solution - <a href="http://clara.io/" target="_blank">http://Clara.io</a>. It is amazing what is possible online nowadays :-)<br />
<br />]]></content:encoded></item><item><title>PMP certification - passed!</title><link>https://turevskiy.com/blog/pmp-certification-passed/</link><guid isPermaLink="true">https://turevskiy.com/blog/pmp-certification-passed/</guid><pubDate>Mon, 20 May 2013 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>PMP</category><category>PMI</category><category>project management</category><category>certification</category><description>After all the nervous and preparations the certification is passed. Nice feeling, and good chance for a new challenges, nearer to business now.</description><content:encoded><![CDATA[After all the nervous and preparations the certification is passed. Nice feeling, and good chance for a new challenges, nearer to business now.]]></content:encoded></item><item><title>Treatment of insubordination</title><link>https://turevskiy.com/blog/treatment-of-insubordination/</link><guid isPermaLink="true">https://turevskiy.com/blog/treatment-of-insubordination/</guid><pubDate>Wed, 21 Nov 2012 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>people management</category><category>management</category><description>Yesterday I met probably the most insolent insubordination ever: one of developers in my project has asked for good recommendation from customer and kindly got it without…</description><content:encoded><![CDATA[Yesterday I met probably the most insolent insubordination ever: one of developers in my project has asked for good recommendation from customer and kindly got it without notifying any superiors.<br />And (!) tried to use it in contrary to managers' bad opinion about his performance.<br /><br />
Some people would just benefit from being fired a couple of times.]]></content:encoded></item><item><title>ISTQB Certified - Foundation</title><link>https://turevskiy.com/blog/istqb-certified-foundation/</link><guid isPermaLink="true">https://turevskiy.com/blog/istqb-certified-foundation/</guid><pubDate>Wed, 21 Nov 2012 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>ISTQB</category><category>testing</category><category>certification</category><description>Just a citation: ------------------------------------------------------------------------ Congratulations! Dear Mr. Turevskiy, You have successfully passed the examinatio…</description><content:encoded><![CDATA[Just a citation:<br />
------------------------------------------------------------------------<br />
<br />
<p>
Congratulations!<a href="" name="Anrede"></a><o:p></o:p></p>
<p>
<br /></p>
<p>
Dear Mr. Turevskiy,<o:p></o:p></p>
<p>
<br /></p>
<p>
You have successfully passed the examination to ISTQB<sup>®
</sup>Certified Tester, Foundation Level.<o:p></o:p></p>
<p>
<br /></p>
<p>
You scored <b>35</b> out of <b>40</b> possible
credits.<o:p></o:p></p>
<p>
This equals <b>87,50%</b>. <o:p></o:p></p>
<p>
To pass the exam a minimum of 65% was necessary.<o:p></o:p></p>
<p>
<br /></p>
<p>
We wish you all the best for your professional future.
The certificate will be issued and sent to you within four weeks after the
examination fee has been paid. If you have paid by voucher or in advance the
certificate will be issued and send to you within four weeks after the
examination date.<o:p></o:p></p>
<p>
<br /></p>
<p>
<br /></p>
<p>
Kind regards,<o:p></o:p></p>
<p>
<br /></p>
<p>
Your Certification team<o:p></o:p></p>
<br />
------------------------------------------------------------------------<br />
Yes, I am a PM, not test engineer, but one should know how the activities he manages are done from inside.<br />
<br />
Also, learning itself was very usefull to arrange my knowledge of SDLC and fill out the gaps in terms and processes.]]></content:encoded></item><item><title>Hello PMI</title><link>https://turevskiy.com/blog/hello-pmi/</link><guid isPermaLink="true">https://turevskiy.com/blog/hello-pmi/</guid><pubDate>Wed, 21 Nov 2012 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>PMI</category><category>PMP</category><category>project management</category><category>certification</category><description>Recently I have started the PMP preparation training and certificatuion preparation. What I did yet: Training with Spider Project in Kiev, Ukraine ( http://spiderproject.…</description><content:encoded><![CDATA[Recently I have started the PMP preparation training and certificatuion preparation.<br />
<br />
What I did yet:<br />
<br />
<ul>
<li>Training with Spider Project in Kiev, Ukraine (<a href="http://spiderproject.com.ua/">http://spiderproject.com.ua</a>)</li>
<li>PMI membership: <a href="http://pmi.org/">pmi.org</a> and my own PMBOK</li>
</ul>
<br />
So I will put here all the news, links and ideas I have related to the PMI and their activities<br />
<br />
PMP certification experience (sorry in russian) <a href="http://pmi.org.ua/ru/articles/test">http://pmi.org.ua/ru/articles/test</a><br />
<br />
Certification price, procedure and other questions: <a href="http://www.pmi.org/en/Certification/~/media/PDF/Certifications/pdc_pmphandbook.ashx">http://www.pmi.org/en/Certification/~/media/PDF/Certifications/pdc_pmphandbook.ashx</a><br />
<br />
New Agile certification from PMI practice advises: maybe sometimes in future...<br />
 (sorry in russian) <a href="http://pmi.org.ua/ru/articles/9067004">http://pmi.org.ua/ru/articles/9067004</a>]]></content:encoded></item><item><title>Note: Android NDK setup for Eclipse</title><link>https://turevskiy.com/blog/note-android-ndk-setup-for-eclipse/</link><guid isPermaLink="true">https://turevskiy.com/blog/note-android-ndk-setup-for-eclipse/</guid><pubDate>Thu, 09 Aug 2012 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Android</category><category>Android NDK</category><category>Eclipse</category><category>C++</category><description>Android NDK setup for Eclipse IDE Here is a guide that helped http://mhandroid.wordpress.com/2011/01/23/using-eclipse-for-android-cc-development/ NDK Android Eclipse</description><content:encoded><![CDATA[<h2>
Android NDK setup for Eclipse IDE</h2>
<br />
Here is a guide that helped<br />
<a href="http://mhandroid.wordpress.com/2011/01/23/using-eclipse-for-android-cc-development/">http://mhandroid.wordpress.com/2011/01/23/using-eclipse-for-android-cc-development/</a><br />
<br />
NDK Android Eclipse]]></content:encoded></item><item><title>ASP.NET application does not work: 0x80070021. This configuration section cannot be used at this path</title><link>https://turevskiy.com/blog/asp-net-application-does-not-work-0x80070021-this-configurat/</link><guid isPermaLink="true">https://turevskiy.com/blog/asp-net-application-does-not-work-0x80070021-this-configurat/</guid><pubDate>Tue, 01 Mar 2011 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>ASP.NET</category><category>IIS</category><category>Windows</category><category>troubleshooting</category><description>After reinstalling the Windows, Suite ASP.NET application does not work: 0x80070021 . This configuration section cannot be used at this path Unexpectedly help comes from …</description><content:encoded><![CDATA[<p class="MsoNormal">After reinstalling the Windows, Suite ASP.NET application does not work: <b>0x80070021</b>.<b> This configuration section cannot be used at this path</b><o:p></o:p></p>  <p class="MsoNormal"><o:p> </o:p></p>  <p class="MsoNormal">Unexpectedly help comes from Microsoft Knowledge Base:<o:p></o:p></p>  <p class="MsoNormal"><a href="http://support.microsoft.com/kb/942055/">http://support.microsoft.com/kb/942055/</a><o:p></o:p></p>  <p class="MsoNormal">See case #9<o:p></o:p></p>  <p class="MsoNormal"><a href="http://learn.iis.net/page.aspx/145/how-to-use-locking-in-iis-70-configuration">http://learn.iis.net/page.aspx/145/how-to-use-locking-in-iis-70-configuration</a><o:p></o:p></p>  <p class="MsoNormal"><o:p> </o:p></p>  <p class="MsoNormal">Editing the %windir%\system32\inetsrv\config\applicationHost.config helped.<o:p></o:p></p>  <p class="MsoNormal"><o:p> </o:p></p>  <p class="MsoNormal">Error message:<b><o:p></o:p></b></p>  <p class="MsoNormal"><b><o:p> </o:p></b></p>  <p class="MsoNormal"><b>HTTP Error 500.19 - Internal Server Error<o:p></o:p></b></p>  <p class="MsoNormal"><b>The requested page cannot be accessed because the related configuration data for the page is invalid.<o:p></o:p></b></p>  <p class="MsoNormal">Detailed Error Information <o:p></o:p></p>  <table class="MsoNormalTable" border="0" cellspacing="0" cellpadding="0" width="100%">  <tbody><tr>   <td width="30%" valign="top">   <p class="MsoNormal" align="right">Module<o:p></o:p></p>   </td>   <td valign="top">   <p class="MsoNormal"><b>AnonymousAuthenticationModule<o:p></o:p></b></p>   </td>  </tr>  <tr>   <td width="30%" valign="top">   <p class="MsoNormal" align="right">Notification<o:p></o:p></p>   </td>   <td valign="top">   <p class="MsoNormal"><b>AuthenticateRequest<o:p></o:p></b></p>   </td>  </tr>  <tr>   <td width="30%" valign="top">   <p class="MsoNormal" align="right">Handler<o:p></o:p></p>   </td>   <td valign="top">   <p class="MsoNormal"><b>PageHandlerFactory-ISAPI-2.0-64<o:p></o:p></b></p>   </td>  </tr>  <tr>   <td width="30%" valign="top">   <p class="MsoNormal" align="right">Error Code<o:p></o:p></p>   </td>   <td valign="top">   <p class="MsoNormal"><b>0x80070021<o:p></o:p></b></p>   </td>  </tr>  <tr>   <td width="30%" valign="top">   <p class="MsoNormal" align="right">Config Error<o:p></o:p></p>   </td>   <td valign="top">   <p class="MsoNormal"><b>This configuration section cannot be   used at this path. This happens when the section is locked at a parent level.   Locking is either by default (overrideModeDefault="Deny"), or set   explicitly by a location tag with overrideMode="Deny" or the legacy   allowOverride="false". <o:p></o:p></b></p>   </td>  </tr>  <tr>   <td width="30%" valign="top">   <p class="MsoNormal" align="right">Config File<o:p></o:p></p>   </td>   <td valign="top">   <p class="MsoNormal"><b>\\?\C:\Dev\531Patch00\Root\Web\Logon\web.config<o:p></o:p></b></p>   </td>  </tr> </tbody></table>  <p class="MsoNormal"><o:p> </o:p></p>]]></content:encoded></item><item><title>SQL 2008 setup error: Rebuild the performance counters of a SQL Server 2005 or 2008 instance</title><link>https://turevskiy.com/blog/sql-2008-setup-error-rebuild-the-performance-counters-of-a-s/</link><guid isPermaLink="true">https://turevskiy.com/blog/sql-2008-setup-error-rebuild-the-performance-counters-of-a-s/</guid><pubDate>Fri, 18 Feb 2011 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>SQL Server</category><category>Windows</category><category>installation</category><category>troubleshooting</category><description>It happens if you changed the language of your system using MUI. Here are the pills: http://www.sqldev.org/sql-server-setup--upgrade/error-on-install-performance-counter-…</description><content:encoded><![CDATA[It happens if you changed the language of your system using MUI.<br /><br />Here are  the pills:<br /><a href="http://www.sqldev.org/sql-server-setup--upgrade/error-on-install-performance-counter-registry-hive-consistency-check--statusfailed-89399.shtml">http://www.sqldev.org/sql-server-setup--upgrade/error-on-install-performance-counter-registry-hive-consistency-check--statusfailed-89399.shtml</a>]]></content:encoded></item><item><title>Importance of order</title><link>https://turevskiy.com/blog/importance-of-order/</link><guid isPermaLink="true">https://turevskiy.com/blog/importance-of-order/</guid><pubDate>Thu, 03 Feb 2011 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>logging</category><category>databases</category><category>software maintenance</category><description>Hi. Just what I understand while trying to update a super-customized database came from client to our newest version: There can never be too much journaling and logging!!…</description><content:encoded><![CDATA[Hi.<br />Just what I understand while trying to update a super-customized database came from client  to our newest version:<br />There can never be too much journaling and logging!!!<br />Every megabyte you spent for detailed history of your application life can save you hours of your life those you'll damn hundred times otherwise.<br />Think early :-)]]></content:encoded></item><item><title>Self-hosted WCF service with SSL certificate security</title><link>https://turevskiy.com/blog/self-hosted-wcf-service-with-ssl-certificate-security/</link><guid isPermaLink="true">https://turevskiy.com/blog/self-hosted-wcf-service-with-ssl-certificate-security/</guid><pubDate>Wed, 25 Aug 2010 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>WCF</category><category>.NET</category><category>HTTPS</category><category>SSL</category><category>Windows</category><category>troubleshooting</category><description>Spent the whole day trying to force my self-hosted service to respond. Finally what has helped is two lines below to execute (port, certhash, path and user name are up to…</description><content:encoded><![CDATA[<p class="MsoNormal">Spent the whole day trying to force my self-hosted service to respond.<br /><br />Finally what has helped is two lines below to execute (port, certhash, path and user name are up to you. Not sure about appid yet :-) ):<br /><br />netsh http add urlacl url=httpS://+:2202/PatientService user=DOMAIN\user<o:p></o:p></p>  <p class="MsoNormal">netsh http add sslcert ipport=0.0.0.0:2202  certhash=ea81a9b4c330247f966d23d7e21266171f92cd47 appid={4EF5AB95-0A11-43b1-842C-51D56687CC7A}<br /><br />Now need to understand - WHY??<o:p></o:p></p>]]></content:encoded></item><item><title>Dynamics magic in C#</title><link>https://turevskiy.com/blog/dynamics-magic-in-c/</link><guid isPermaLink="true">https://turevskiy.com/blog/dynamics-magic-in-c/</guid><pubDate>Mon, 02 Aug 2010 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>C#</category><category>.NET</category><category>dynamic</category><description>Just Wow! :-) http://www.codeproject.com/KB/cs/dynamicincsharp.aspx</description><content:encoded><![CDATA[<p>Just Wow! :-)<br /><br /></p><a href="http://www.codeproject.com/KB/cs/dynamicincsharp.aspx">http://www.codeproject.com/KB/cs/dynamicincsharp.aspx</a>]]></content:encoded></item><item><title>Trying Word 2010</title><link>https://turevskiy.com/blog/trying-word-2010/</link><guid isPermaLink="true">https://turevskiy.com/blog/trying-word-2010/</guid><pubDate>Thu, 08 Jul 2010 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Microsoft Word</category><category>blogging</category><description>See how to post directly from Word</description><content:encoded><![CDATA[<p>See how to post directly from Word</p>]]></content:encoded></item><item><title>SQL Server 2008 setup: Error &quot;Restart Computer Failed&quot;</title><link>https://turevskiy.com/blog/sql-server-2008-setup-error-restart-computer-failed/</link><guid isPermaLink="true">https://turevskiy.com/blog/sql-server-2008-setup-error-restart-computer-failed/</guid><pubDate>Tue, 20 Apr 2010 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>SQL Server</category><category>Windows</category><category>installation</category><category>troubleshooting</category><description>When trying to install SQL Server 2008 I found that it requires me to restart computer even after I did it two times. It was really bad. However, there are kind people ar…</description><content:encoded><![CDATA[<p>When trying to install SQL Server 2008 I found that it requires me to restart computer even after I did it two times.<br />It was really bad.</p><p><br />However, there are kind people around!<br />Especially here:</p><a href="http://www.sqlcoffee.com/Troubleshooting051.htm">http://www.sqlcoffee.com/Troubleshooting051.htm</a><p>So the reason was in pending after-reboot file copying requested (and never performed)<br /><br /></p><p>I believe this might cause some mistakes if you really have pending files, so make sure to reboot first.</p>]]></content:encoded></item><item><title>Note: scripting an MS SQL database with its data</title><link>https://turevskiy.com/blog/note-scripting-ms-sql-database-with-data/</link><guid isPermaLink="true">https://turevskiy.com/blog/note-scripting-ms-sql-database-with-data/</guid><pubDate>Thu, 04 Feb 2010 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>SQL Server</category><category>Visual Studio</category><category>databases</category><description>Needed to script the MS SQL database including data. Strangely :-), but Microsoft did it well: http://blog.sqlauthority.com/2007/11/16/sql-server-2005-generate-script-wit…</description><content:encoded><![CDATA[Needed to script the MS SQL database including data.<br /><br /><p>Strangely :-), but Microsoft did it well:<br />http://blog.sqlauthority.com/2007/11/16/sql-server-2005-generate-script-with-data-from-database-database-publishing-wizard/<p><br /></p><p>Visual Studio 2008 also have such tool built in.<br /><br />(However I am not sure about complex databases (like encrypted) yet...)</p></p>]]></content:encoded></item><item><title>Note: setting the SQL Server authentication mode from code</title><link>https://turevskiy.com/blog/note-sql-server-authentication-mode-from-code/</link><guid isPermaLink="true">https://turevskiy.com/blog/note-sql-server-authentication-mode-from-code/</guid><pubDate>Fri, 22 Jan 2010 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>SQL Server</category><category>Windows</category><category>authentication</category><description>how can I set the authentication mode for a SQLServer from code? Change HKLM\Software\Microsoft\Microsoft SQL Server\ \MSSQLServer\LoginMode 1 = integrated only, 2-mixed …</description><content:encoded><![CDATA[how can I set the authentication mode for a SQLServer from code?<br /><br />Change<br />HKLM\Software\Microsoft\Microsoft SQL Server\<instance>\MSSQLServer\LoginMode<br />1 = integrated only, 2-mixed<br /><br />For <instance> you will need to use MSSQL.n, where n relates to the instance you are interested in. You can find the right value by looking at HKLM\Software\Microsoft\Microsoft SQL Server\Instance Names\SQLame, and looking for the value that matches your instance name. SQL Express has an instance name of SQLEXPRESS, and the default SQL 2005 instance has a name of MSSQLSERVER. (on my box ...\Instance Names\SQL\SQLEXPRESS has a value of MSSQL.4)<br /><br />From here: <a href="http://www.sqlservercentral.com/Forums/Topic439615-359-1.aspx">http://www.sqlservercentral.com/Forums/Topic439615-359-1.aspx</a>]]></content:encoded></item><item><title>Note: what company culture really is</title><link>https://turevskiy.com/blog/note-what-company-culture-really-is/</link><guid isPermaLink="true">https://turevskiy.com/blog/note-what-company-culture-really-is/</guid><pubDate>Thu, 21 Jan 2010 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>company culture</category><category>management</category><description>Good explanation of what &quot;Company culture&quot; really is http://www.slideshare.net/reed2001/culture-1798664</description><content:encoded><![CDATA[Good explanation of what "Company culture" really is<br /><br /><a href="http://www.slideshare.net/reed2001/culture-1798664">http://www.slideshare.net/reed2001/culture-1798664</a>]]></content:encoded></item><item><title>PM Labs 2009 conference by Luxoft Training Center</title><link>https://turevskiy.com/blog/pm-labs-2009-conference-by-luxoft-training-center/</link><guid isPermaLink="true">https://turevskiy.com/blog/pm-labs-2009-conference-by-luxoft-training-center/</guid><pubDate>Mon, 27 Jul 2009 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>project management</category><category>conference</category><category>Kyiv</category><category>Luxoft</category><description>Hi all, Last Saturday (2009 July 25th) I&#x27;ve attended a quite funny event - PM Labs 2009 held by Luxoft Training Center, this year in Kiev. There way some issues, but gene…</description><content:encoded><![CDATA[Hi all,<p><br /></p><p>Last Saturday (2009 July 25th) I've attended a quite funny event - PM Labs 2009 held by Luxoft Training Center, this year in Kiev. There way some issues, but generally it look like a most interesting PM community event in Ukraine - thanks Luxoft.</p><p>There was an assumption that it may be too headhunting-targeted, but it was rather an advvertisement of  training center services those are not so imposing.<br /><br />More info is here: <a href="http://www.pm-labs.ru/news/105">http://www.pm-labs.ru/news/105</a><br /><br />So let's go on!</p>]]></content:encoded></item><item><title>Azure Cloud over firewall: HTTP protocol incompatibility</title><link>https://turevskiy.com/blog/azure-cloud-over-firewall-http-protocol-incompatibility/</link><guid isPermaLink="true">https://turevskiy.com/blog/azure-cloud-over-firewall-http-protocol-incompatibility/</guid><pubDate>Thu, 25 Jun 2009 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Azure</category><category>cloud computing</category><category>HTTP</category><category>Squid</category><category>troubleshooting</category><description>Hello there, Just got a real problem - out corporative Squid firewall does not support HTTP 1.1 properly, and Azure data storage strictly requires it. As a result, access…</description><content:encoded><![CDATA[Hello there,<br /><br />Just got a real problem - out corporative Squid firewall does not support HTTP 1.1 properly, and Azure data storage strictly requires it.<br />As a result, accessing Azure data from local application is not possible at all.<br />For example, log viewer application are useless.<br /><br />And of course it is really hard to kick our administrators to change it.]]></content:encoded></item><item><title>Issue with parsing command lines containing path in c#</title><link>https://turevskiy.com/blog/issue-with-parsing-command-lines-containing-path-in-c/</link><guid isPermaLink="true">https://turevskiy.com/blog/issue-with-parsing-command-lines-containing-path-in-c/</guid><pubDate>Tue, 23 Jun 2009 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>C#</category><category>.NET</category><category>command line</category><category>Windows</category><description>Hi! Another strange detail of .NET/C#: I needed to pass two pathes to c# command line arguments, and wasn&#x27;t able to control whether they are ended with backslash or not. …</description><content:encoded><![CDATA[Hi!<br />Another strange detail of .NET/C#:<br />I needed to pass two pathes to c# command line arguments, and wasn't able to control whether they are ended with backslash or not. Really it was a C# post-build tool so arguments were passed by MsBuild. Final command line looked like following:<br />"C:\My Path\My Program.exe" "C:\First Path\" "C:\Second Path\"<br />However C# command-line parser treats \" as esacape sequence<br />so for this sample<br />args[0]=="C:\\First Path\" "<br />and args[1]=="C:\Second"<br />and even args[2]=="Path\"".<br />Absolutely unacceptable, isn't it?<br />The only way to solve it I can see is to get the whole original command line from Environment.CommandLine and have your own parser. Please remember that this value includes executable name as well that may be or may be not quoted too.<br /><br />Some explanation is here:<br /><a href="http://www.eggheadcafe.com/conversation.aspx?messageid=31268633&amp;threadid=31268617">http://www.eggheadcafe.com/conversation.aspx?messageid=31268633&amp;threadid=31268617</a>]]></content:encoded></item><item><title>Converting Word document to clean HTML</title><link>https://turevskiy.com/blog/converting-word-document-to-clean-html/</link><guid isPermaLink="true">https://turevskiy.com/blog/converting-word-document-to-clean-html/</guid><pubDate>Wed, 03 Jun 2009 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Microsoft Word</category><category>HTML</category><category>documentation</category><description>Just got a request to convert our User Manual from Word 2007 to nice HTML format. As you may know Word provides really ugly formatting when exporting to HTML. There is an…</description><content:encoded><![CDATA[Just got a request to convert our User Manual from Word 2007 to nice HTML format. As you may know Word provides really ugly formatting when exporting to HTML. There is another option named "Filtered HTML" that provides much cleaner one but still not acceptable.<br />I tried different approached found from Google - exporting and cleaning by Tidy, manual replaces, even using Gmail preview function (send document to GMail account and use preview on site).<br />Still looks ugly. I did not try DreamWeaver funcion for Word HTMl cleaning yet, maybe found one to try.<br />If it didn't help - need to copy-paste without formatting and format manually :-(.<br /><br />Really Format Manual seemed to be the only possible solution - did it finally in Expression Web I had at hand. Funny tool but still missing some important features like formatting copier.]]></content:encoded></item><item><title>Troubles accessing WCF service on Vista</title><link>https://turevskiy.com/blog/troubles-accessing-wcf-service-on-vista/</link><guid isPermaLink="true">https://turevskiy.com/blog/troubles-accessing-wcf-service-on-vista/</guid><pubDate>Mon, 01 Jun 2009 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>WCF</category><category>.NET</category><category>Windows</category><category>UAC</category><category>troubleshooting</category><description>Got this problem seconf time so need to describe it for future. (Strange that there are so few comments on it found by Google. Do any other developers use Vista? Or they …</description><content:encoded><![CDATA[Got this problem seconf time so need to describe it for future.<br />(Strange that there are so few comments on it found by Google.<br />Do any other developers use Vista? Or they know some secret way?<br />One of useful advises I met was "Get rid of Vista")<br /><br />So, back to the problem.<br />I tried to debug by WCF service-based application on Vista<br />after succesfully running it on Windows XP.<br />However, it did not work.<br />First idea was to restart VisualStudio with Administrator permissions that is usual.<br />But it did not work again and gave smth. "is in Faulted state" error.<br /><br /><p>Magic words are the command:<br /><strong><em>netsh http add urlacl url=</em></strong><a href="http://+:9001/" rel="nofollow"><strong><em>http://+:9001/</em></strong></a><strong><em> user=DOMAIN\user</em></strong><br />Replace URL and user domain/name with your data.</p><p>More details are here:<br /><a href="http://stackoverflow.com/questions/598643/hosting-a-wcf-service-in-vista">http://stackoverflow.com/questions/598643/hosting-a-wcf-service-in-vista</a><br />and here<br /><a href="http://msdn.microsoft.com/en-us/library/ms733768.aspx">http://msdn.microsoft.com/en-us/library/ms733768.aspx</a></p><p>By the way, thanks a lot for all the guys from <a href="http://stackoverflow.com/">http://stackoverflow.com/</a>. Great job!</p>(Sidenote. Why is it so complex to request permissions elevation from .NET? I got samples from Microsoft <a href="http://msdn.microsoft.com/en-us/library/aa970890.aspx">here</a> but it is very unconmmon anyway)]]></content:encoded></item><item><title>Note: my Azure crawler is working</title><link>https://turevskiy.com/blog/note/</link><guid isPermaLink="true">https://turevskiy.com/blog/note/</guid><pubDate>Tue, 26 May 2009 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Azure</category><category>cloud computing</category><category>web crawler</category><category>Google</category><category>Amazon</category><description>My Azure Crawler is quite working now, will publish it soon either if have time to improve or not. Also, going to try cloud solution from Google and maybe Amazon but it s…</description><content:encoded><![CDATA[My Azure Crawler is quite working now, will publish it soon either if have time to improve or not.<br />Also, going to try cloud solution from Google and maybe Amazon but it seems last one have no free option, and I haven't internet-enabled card now  (another big story)]]></content:encoded></item><item><title>Visual Studio Team System 2008 Test Load Agent troubles</title><link>https://turevskiy.com/blog/visual-studio-team-system-2008-test-load-agent-troubles/</link><guid isPermaLink="true">https://turevskiy.com/blog/visual-studio-team-system-2008-test-load-agent-troubles/</guid><pubDate>Wed, 20 May 2009 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Visual Studio</category><category>load testing</category><category>testing</category><category>Microsoft</category><description>Recently we had an idea to test one of sites being developed from multiple client locations - so have a distributed test. Since I am usually oriented at Microsoft product…</description><content:encoded><![CDATA[Recently we had an idea to test one of sites being developed from multiple client locations - so have a distributed test.<br />Since I am usually oriented at Microsoft product and moreover I remember old Web Application Stress Tools from Microsoft thst was really good for that time, I believed there should be something for distribute testing in Visual Studio 2008 Team Suite Edition.<br />Sounds logically? Not for Microsoft!<br />VSTE does not support distributed testing.<br />If you really want it so much - you should install separate product: Visual Studio Team System 2008 Test Load Agent<br />Pricing? Well.. Around $5000 for volume license. No other options.<br />But! there is a 90-day Trial here: <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=572e1e71-ae6b-4f92-960d-544cabe62162&amp;displaylang=en">http://www.microsoft.com/downloads/details.aspx?FamilyID=572e1e71-ae6b-4f92-960d-544cabe62162&amp;displaylang=en</a><br />Again but! It does not work for me - I have VS 2008 SP1 but trial is compiled against pre-SP1 libraries. Really there are LOTS of reasons it may not work for you at all. Some of them are here: <a href="http://weblogs.asp.net/okloeten/archive/2008/09/09/6612870.aspx">http://weblogs.asp.net/okloeten/archive/2008/09/09/6612870.aspx</a>.<br />So it seems we'll look for some other product - more friendly and worth its money...]]></content:encoded></item><item><title>Cloud computing: Microsoft Azure</title><link>https://turevskiy.com/blog/cloud-computing-microsoft-azure/</link><guid isPermaLink="true">https://turevskiy.com/blog/cloud-computing-microsoft-azure/</guid><pubDate>Wed, 20 May 2009 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>Azure</category><category>cloud computing</category><category>Microsoft</category><description>Recently I had a closer look at Microsoft implementation of cloud computing paradigm: Azure plateform. Despite of issues those are inevitable for pre-release product, it …</description><content:encoded><![CDATA[Recently I had a closer look at Microsoft implementation of cloud computing paradigm: Azure plateform. Despite of issues those are inevitable for pre-release product, it looks really promising and quite developer-friendly.<br />Hope to show something runnable and presentable soon!<br /><br />I'll be adding my thoughts here for now, andlater structure them up<br /><br />0. Use StorageClient helpers from Azure SDK. It'll save you lots of effort<br /><br />1. When you save data in Table storage - always fill both Partition and Row key, or you'll be unable to delete records. Also remember that keys cannot contain / \ # , ?<br /><br />2. Select operations to Table storage are limited to 1000 records and is surely limited by 1000 charcters per text field. It brings sound limitations for your data operations. For example, it does not make sence to select all records - instead  you should have some filtering field (partition key or smth else) able to divide data to bunches below 1000 items each.<br /><br />3. Read focumentation or goggle for other workarounds like foreign keys simulation.]]></content:encoded></item><item><title>How to make InputBox in WSH JavaScript</title><link>https://turevskiy.com/blog/how-to-make-inputbox-in-wsh-javascript/</link><guid isPermaLink="true">https://turevskiy.com/blog/how-to-make-inputbox-in-wsh-javascript/</guid><pubDate>Wed, 03 Dec 2008 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>JavaScript</category><category>Windows Script Host</category><category>.NET</category><category>Windows</category><description>I was just asked if Windows Shell (WSH) JavaScript has something like Visual Basic InputBox function. It seems no. So here is a small .NET utility showing the box and sav…</description><content:encoded><![CDATA[I was just asked if Windows Shell (WSH) JavaScript has something like Visual Basic InputBox function.<br />
It seems no.<br />
So here is a small .NET utility showing the box and saving value to given environment variable.<br />
See usage sample in *.js file.<br />
<br />
<a href="http://www.savefile.com/files/1913944">http://www.savefile.com/files/1913944</a> (LET ME KNOW IF THIS SERVICE ISN'T WORKING)]]></content:encoded></item><item><title>How to unlock locked file in TFS</title><link>https://turevskiy.com/blog/how-to-unlock-locked-file-in-tfs/</link><guid isPermaLink="true">https://turevskiy.com/blog/how-to-unlock-locked-file-in-tfs/</guid><pubDate>Mon, 13 Oct 2008 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>TFS</category><category>Visual Studio</category><category>version control</category><category>command line</category><description>Just solved a problem. Imagine some bad guys left your team and did not check in their files. So you cannot deal with them anymore from Visual Studio. Here is great expla…</description><content:encoded><![CDATA[Just solved a problem.<br /><br />Imagine some bad guys left your team and did not check in their files.<br />So you cannot deal with them anymore from Visual Studio.<br /><br />Here is great explanation of how to rescue:<br /><a href="http://blogs.msdn.com/rimuri/archive/2006/03/06/544686.aspx">http://blogs.msdn.com/rimuri/archive/2006/03/06/544686.aspx</a><br /><br />Also - description of TFS command-line tools you'll surely need:<br /><a href="http://blogs.msdn.com/buckh/articles/CommandLineSummary.aspx">http://blogs.msdn.com/buckh/articles/CommandLineSummary.aspx</a>]]></content:encoded></item><item><title>Troubles with TFS: TF30063: You are not authorized to access Microsoft-IIS/6.0.</title><link>https://turevskiy.com/blog/troubles-with-tfs-tf30063-you-are-not-authorized-to-access-m/</link><guid isPermaLink="true">https://turevskiy.com/blog/troubles-with-tfs-tf30063-you-are-not-authorized-to-access-m/</guid><pubDate>Thu, 02 Oct 2008 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>TFS</category><category>Visual Studio</category><category>IIS</category><category>Windows</category><category>troubleshooting</category><description>Hello there, Couple of days ago we got following trouble message when connecting to TFS from one of our PCs: TF30063: You are not authorized to access Microsoft-IIS/6.0. …</description><content:encoded><![CDATA[Hello there,<br /><br />Couple of days ago we got following trouble message when connecting to TFS from one of our PCs:<br />TF30063: You are not authorized to access Microsoft-IIS/6.0.<br /><br />Gooogl'ing brings idea that it is connected with local Internet proxy settings, however proxy was not set directly.<br />But! We found that some software specified the proxy configuration script setting some local proxy server. We thought about Selenium IDE installed there but aren't sure since have it installed elsewhere and no such problems yet.<br /><br />Anyway, turning the script off solved the problem. We will check if it brings other issues.<br /><br />Enjoy!]]></content:encoded></item><item><title>&quot;Seven Sins of Offshoring&quot;</title><link>https://turevskiy.com/blog/seven-sins-of-offshoring/</link><guid isPermaLink="true">https://turevskiy.com/blog/seven-sins-of-offshoring/</guid><pubDate>Thu, 02 Oct 2008 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>offshoring</category><category>outsourcing</category><category>software development</category><description>Just mentioned this article - http://www.telecommerce.co.uk/download/OffshoringReport2007.pdf (In Russian - http://www.pcweek.ru/themes/detail.php?ID=114469) Nice modern …</description><content:encoded><![CDATA[Just mentioned this article - http://www.telecommerce.co.uk/download/OffshoringReport2007.pdf<br />(In Russian - http://www.pcweek.ru/themes/detail.php?ID=114469)<br /><br />Nice modern ideas in comparison with Bible ones!]]></content:encoded></item><item><title>Just a bit about politics (sorry, can&#x27;t bear...)</title><link>https://turevskiy.com/blog/just-a-bit-about-politics-sorry-can-t-bear/</link><guid isPermaLink="true">https://turevskiy.com/blog/just-a-bit-about-politics-sorry-can-t-bear/</guid><pubDate>Thu, 02 Oct 2008 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>politics</category><category>Russia</category><category>Putin</category><category>personal</category><description>With nowadays Russia approach to internal and external affairs I&#x27;m feeling like just after Munich&#x27;38. Don&#x27;t like to be around if it continues. Remember - Hitler had no nu…</description><content:encoded><![CDATA[With nowadays Russia approach to internal and external affairs I'm feeling like just after Munich'38.<br />Don't like to be around if it continues. Remember - Hitler had no nuke but managed to kill millions. Putin has :-(]]></content:encoded></item><item><title>Throwing and catching an Exception from remoting object problem</title><link>https://turevskiy.com/blog/throwing-and-catching-an-exception-from-remoting-object-prob/</link><guid isPermaLink="true">https://turevskiy.com/blog/throwing-and-catching-an-exception-from-remoting-object-prob/</guid><pubDate>Tue, 08 Aug 2006 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>.NET</category><category>C#</category><category>.NET Remoting</category><category>exceptions</category><category>debugging</category><description>So here is the problem I have: When throwing any exception from Remoting&#x27;ly created object on the client side we have RemotingException instead having something like this…</description><content:encoded><![CDATA[So here is the problem I have:<br /><br />When throwing any exception from Remoting'ly created object<br />on the client side we have RemotingException instead having something like this:<br /><em><strong>Server encountered an internal error. For more information, turn off customErrors in the server's .config file.<br /></strong></em>instead of the real exception.<br />Be aware - it happens only when calling object really remotely, not by remoting on the same computer.<br />The solution should be to add following lines to your *.config file (either <em>app.config </em>copyied then to <em>appname.exe.config </em>or <em>web.config):</em><br /><strong><system.runtime.remoting><br /><customerrors mode="Off"><br /></system.runtime.remoting></strong><br />However it does not help!<br />The reason is in strange fact that Remoting does not load the config settings by default. Can't imagine why...<br />To workaround this, have something like this in startup method of your application:<br /><strong>string configPath =<br />System.Reflection.Assembly.GetExecutingAssembly().Location + ".config"; </strong><br /><strong>System.Runtime.Remoting.RemotingConfiguration.Configure(configPath);</strong><br />The goal is to force Remoting to final load the config from proper file.<br />I saw an idea to use a <em>null</em> instead of file name, but this did  no work for me.<br />Enjoy!<br />You can use code like this<br /><strong>bool b = System.Runtime.Remoting.RemotingConfiguration.CustomErrorsEnabled(false);<br /></strong>to check this setting.<br /><br />Thanks Chris Taylor for great info from here:<br /><a href="http://dotnetjunkies.com/WebLog/chris.taylor/articles/5566.aspx">http://dotnetjunkies.com/WebLog/chris.taylor/articles/5566.aspx</a>]]></content:encoded></item><item><title>FileInfo and DirectoryInfo does not work with long file names (260 chars or something like it)</title><link>https://turevskiy.com/blog/fileinfo-and-directoryinfo-does-not-work-with-long-file-name/</link><guid isPermaLink="true">https://turevskiy.com/blog/fileinfo-and-directoryinfo-does-not-work-with-long-file-name/</guid><pubDate>Tue, 08 Aug 2006 00:00:00 +0000</pubDate><dc:creator>Oleksandr Turevskiy</dc:creator><category>.NET</category><category>C#</category><category>Windows</category><category>long file paths</category><category>FileInfo</category><category>DirectoryInfo</category><category>MAX_PATH</category><description>What I found: NET Framework FileInfo and DirectoryInfo can&#x27;t work with file and folder names longer then 260 chars. This is by design. For compatibility or something like…</description><content:encoded><![CDATA[What I found:<br /><br />NET Framework FileInfo and DirectoryInfo can't work with file and folder names longer then 260 chars. This is by design. For compatibility or something like this. Great. But you can stil lcreate them with calling the Unicode version of API function and prepending the name with<br />"<a href="file:///?\">file:////?\</a>".<br />See for example CreateFile docs in MSDN <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/createfile.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/createfile.asp</a>.<br />Isn't it good to can't see the data you can create?!<br /><br />So need to solve.<br />The only way - get back to API and write the classes from scratch.<br />I did not have time to duplicate them fully (if you really need this - see at Reflector tool <a href="http://www.aisto.com/roeder/dotnet/">http://www.aisto.com/roeder/dotnet/</a>, it will help you alot to dig into Framework code).<br />So below are just simplest implementation, covering my needs.<br />Let me know if you have a better one!<br /><br />Below are three separated classes.<br />Sorry for pure formatting, I'm new to blogging<br />and don't know if there are attachments or something here...<br /><br /><pre><br />-------------------------------------------------------------------------------<br /><br />using System;<br />using System.Collections.Generic;<br />using System.Text;<br />using System.Runtime.InteropServices;<br />using System.IO;<br />using System.ComponentModel;<br />using Microsoft.Win32.SafeHandles;<br />namespace MyCompany.MyProduct.Common.Win32<br />{<br />[Serializable]<br />public abstract class MyCompanyFileSystemInfo : MarshalByRefObject<br />{<br />const string UNC_PREFIX = "\\\\?\\UNC\\";<br />const string PREFIX = "\\\\?\\";<br />protected string _path;<br />protected WIN32_FILE_ATTRIBUTE_DATA _attributes;<br />protected bool _attributesNotFilled = false;<br />protected int _realPathStart;<br />int GetLastError()<br />{<br />return Marshal.GetLastWin32Error();<br />}<br />public string FullNameWithPrefix<br />{<br />get { return _path; }<br />}<br />public string FullName<br />{<br />get<br />{<br />//return _path.Substring(_realPathStart);<br />string t = _path;<br />if (t.StartsWith(UNC_PREFIX))<br />t = "\\\\" + t.Substring(UNC_PREFIX.Length);<br />else if (t.StartsWith(PREFIX))<br />t = t.Substring(PREFIX.Length);<br />return t;<br />}<br />}<br />public override string ToString()<br />{<br />return FullName;<br />}<br />public MyCompanyFileSystemInfo(string path)<br />{<br />path = path.TrimEnd('\\');<br />if (!path.StartsWith(PREFIX))<br />{<br />if (path.StartsWith("\\\\"))<br />{<br />// Unicode<br />path = UNC_PREFIX + path.TrimStart('\\');<br />_realPathStart = 8;<br />}<br />else<br />{<br />if (path[1] != ':')<br />{<br />if(path.StartsWith(".."))<br />{<br />path = Environment.CurrentDirectory.TrimEnd('\\') + "\\" + path.Substring(3);<br />}<br />else if (path.StartsWith("."))<br />{<br />path = Environment.CurrentDirectory.TrimEnd('\\') + "\\" + path.Substring(2);<br />}<br />}<br />// Local<br />path = PREFIX + path;<br />_realPathStart = 4;<br />}<br />}<br />_path = path;<br />FillAttributes();<br />}<br />private void FillAttributes()<br />{<br />if (!GetFileAttributesEx(_path, EFileInfoLevel.GetFileExInfoStandard, out _attributes))<br />_attributesNotFilled = true;<br />else<br />_attributesNotFilled = false;<br />}<br />public long Length<br />{<br />get<br />{<br />FillAttributes();<br />if (_attributesNotFilled)<br />throw new ApplicationException("File could not be opened");<br />long result = MakeLong(_attributes.fileSizeHigh, _attributes.fileSizeLow);<br />return result;<br />}<br />}<br />private long MakeLong(int high, int low)<br />{<br />return (((long)high) << result =" (EFileAttributes)_attributes.fileAttributes;" result =" DateTime.FromFileTime(MakeLong(" result =" DateTime.FromFileTime(MakeLong(" result =" DateTime.FromFileTime(MakeLong(" handle =" CreateFileInternal(" handle ="=" result =" false;" result =" true;" handle =" INVALID_HANDLE_VALUE;" handle =" CreateFile(" lastbslash =" _path.LastIndexOf('\\');">= _realPathStart)<br />{<br />string t = _path.Substring(0, lastBSlash).Substring(_realPathStart);<br />return new MyCompanyDirectoryInfo(t);<br />}<br />else<br />{<br />return null;<br />}<br />}<br />}<br />public string Name<br />{<br />get<br />{<br />string result = "";<br />int lastBSlash = _path.LastIndexOf('\\');<br />if (lastBSlash >= _realPathStart)<br />{<br />result = _path.Substring(lastBSlash+1);<br />}<br />else<br />{<br />result = _path.Substring(_realPathStart);<br />}<br />return result;<br />}<br />}<br />public abstract bool Delete();<br />#region API Wrappers<br />public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);<br />[Flags]<br />public enum EFileAccess : uint<br />{<br />None = 0x0,<br />GenericRead = 0x80000000,<br />GenericWrite = 0x40000000,<br />GenericExecute = 0x20000000,<br />GenericAll = 0x10000000,<br />}<br />[Flags]<br />public enum EFileShare : uint<br />{<br />None = 0x00000000,<br />Read = 0x00000001,<br />Write = 0x00000002,<br />Delete = 0x00000004,<br />}<br />public enum ECreationDisposition : uint<br />{<br />New = 1,<br />CreateAlways = 2,<br />OpenExisting = 3,<br />OpenAlways = 4,<br />TruncateExisting = 5,<br />}<br />[Flags]<br />public enum EFileAttributes : uint<br />{<br />Readonly = 0x00000001,<br />Hidden = 0x00000002,<br />System = 0x00000004,<br />Directory = 0x00000010,<br />Archive = 0x00000020,<br />Device = 0x00000040,<br />Normal = 0x00000080,<br />Temporary = 0x00000100,<br />SparseFile = 0x00000200,<br />ReparsePoint = 0x00000400,<br />Compressed = 0x00000800,<br />Offline = 0x00001000,<br />NotContentIndexed = 0x00002000,<br />Encrypted = 0x00004000,<br />Write_Through = 0x80000000,<br />Overlapped = 0x40000000,<br />NoBuffering = 0x20000000,<br />RandomAccess = 0x10000000,<br />SequentialScan = 0x08000000,<br />DeleteOnClose = 0x04000000,<br />BackupSemantics = 0x02000000,<br />PosixSemantics = 0x01000000,<br />OpenReparsePoint = 0x00200000,<br />OpenNoRecall = 0x00100000,<br />FirstPipeInstance = 0x00080000<br />}<br />[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true,<br />CharSet = CharSet.Unicode, ExactSpelling = true)]<br />public static extern IntPtr CreateFile(<br />string lpFileName,<br />EFileAccess dwDesiredAccess,<br />EFileShare dwShareMode,<br />IntPtr lpSecurityAttributes,<br />ECreationDisposition dwCreationDisposition,<br />EFileAttributes dwFlagsAndAttributes,<br />IntPtr hTemplateFile);<br />[StructLayout(LayoutKind.Sequential)]<br />public struct WIN32_FILE_ATTRIBUTE_DATA<br />{<br />public uint fileAttributes;<br />public System.Runtime.InteropServices.ComTypes.FILETIME creationTime;<br />public System.Runtime.InteropServices.ComTypes.FILETIME lastAccessTime;<br />public System.Runtime.InteropServices.ComTypes.FILETIME lastWriteTime;<br />public uint fileSizeHigh;<br />public uint fileSizeLow;<br />}<br />[DllImport("kernel32.dll", EntryPoint = "GetFileAttributesExW", SetLastError = true,<br />CharSet = CharSet.Unicode, ExactSpelling = true)]<br />public static extern bool GetFileAttributesEx<br />(string path, EFileInfoLevel level, out WIN32_FILE_ATTRIBUTE_DATA data);<br />[DllImport("kernel32.dll", EntryPoint = "CreateDirectoryExW", SetLastError = true,<br />CharSet = CharSet.Unicode, ExactSpelling = true)]<br />public static extern bool CreateDirectoryEx(string lpTemplateDirectory,<br />string lpNewDirectory, IntPtr lpSecurityAttributes);<br />public enum EFileInfoLevel : int<br />{<br />GetFileExInfoStandard = 0<br />}<br />public const int MAX_PATH = 260;<br />public const int MAX_ALTERNATE = 14;<br />[StructLayout(LayoutKind.Sequential, CharSet =<br />CharSet.Unicode)]<br />public struct WIN32_FIND_DATA<br />{<br />public EFileAttributes dwFileAttributes;<br />public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;<br />public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;<br />public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;<br />public int nFileSizeHigh;<br />public int nFileSizeLow;<br />public int dwReserved0;<br />public int dwReserved1;<br />[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]<br />public string cFileName;<br />[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)]<br />public string cAlternate;<br />}<br />[DllImport("kernel32", EntryPoint = "FindFirstFileW", SetLastError = true,<br />CharSet = CharSet.Unicode, ExactSpelling = true)]<br />public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);<br />[DllImport("kernel32", EntryPoint = "FindNextFileW", SetLastError = true,<br />CharSet = CharSet.Unicode, ExactSpelling = true)]<br />public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);<br />[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]<br />public static extern bool FindClose(IntPtr hFindFile);<br />[DllImport("kernel32.dll", EntryPoint = "RemoveDirectoryW", SetLastError = true,<br />CharSet = CharSet.Unicode, ExactSpelling = true)]<br />public static extern bool RemoveDirectory(string lpPathName);<br />[DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true,<br />CharSet = CharSet.Unicode, ExactSpelling = true)]<br />[return: MarshalAs(UnmanagedType.Bool)]<br />public static extern bool DeleteFile([MarshalAs(UnmanagedType.LPTStr)]string lpFileName);<br />[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]<br />[return: MarshalAs(UnmanagedType.Bool)]<br />static extern bool CloseHandle(IntPtr hObject);<br />[DllImport("kernel32.dll", EntryPoint = "CopyFileW", SetLastError = true,<br />CharSet = CharSet.Auto, ExactSpelling = true)]<br />public static extern bool CopyFile(<br />String src, String dst, bool failIfExists);<br />#endregion<br />}<br />}<br /><br />---------------------------------------------<br /><br /><br />using System;<br />using System.Collections.Generic;<br />using System.Text;<br />using System.IO;<br />using System.ComponentModel;<br />using System.Runtime.InteropServices;<br />using Microsoft.Win32.SafeHandles;<br />namespace MyCompany.MyProduct.Common.Win32<br />{<br />public class MyCompanyDirectoryInfo : MyCompanyFileSystemInfo<br />{<br />public MyCompanyDirectoryInfo(string path) : base(path)<br />{<br />//if ((int)(Attributes &amp; EFileAttributes.Directory) == 0)<br />// throw new ApplicationException("File path is specified. Use MyCompanyFileInfo instead");<br />}<br />public bool Create()<br />{<br />if (Exists)<br />return true;<br />if (Parent == null)<br />return true;<br />string t = Parent.FullNameWithPrefix;<br />if (t.EndsWith(":"))<br />t = Environment.SystemDirectory;<br />return CreateDirectoryEx(t, _path, new IntPtr());<br />}<br />public MyCompanyFileInfo[] GetFiles()<br />{<br />return GetFiles("*");<br />}<br />public MyCompanyDirectoryInfo[] GetDirectories()<br />{<br />return GetDirectories("*");<br />}<br />public MyCompanyFileInfo[] GetFiles(string pattern)<br />{<br />if (string.IsNullOrEmpty(pattern))<br />pattern = "*";<br />List<mycompanyfileinfo> _files = new List<mycompanyfileinfo>();<br />WIN32_FIND_DATA findData;<br />IntPtr findHandle;<br />findHandle = FindFirstFile(_path + @"\" + pattern, out findData);<br />if (findHandle != INVALID_HANDLE_VALUE)<br />{<br />do<br />{<br />if ((findData.dwFileAttributes &amp; EFileAttributes.Directory) != 0)<br />{<br />}<br />else<br />{<br />string file = _path + @"\" + findData.cFileName;<br />_files.Add(new MyCompanyFileInfo(file));<br />}<br />}<br />while (FindNextFile(findHandle, out findData));<br />FindClose(findHandle);<br />}<br />return _files.ToArray();<br />}<br />public MyCompanyDirectoryInfo[] GetDirectories(string pattern)<br />{<br />if (string.IsNullOrEmpty(pattern))<br />pattern = "*";<br />List<mycompanydirectoryinfo> _folders = new List<mycompanydirectoryinfo>();<br />WIN32_FIND_DATA findData;<br />IntPtr findHandle;<br />findHandle = FindFirstFile(_path + @"\" + pattern, out findData);<br />if (findHandle != INVALID_HANDLE_VALUE)<br />{<br />do<br />{<br />if ((findData.dwFileAttributes &amp; EFileAttributes.Directory) != 0)<br />{<br />if (findData.cFileName != "." &amp;&amp; findData.cFileName != "..")<br />{<br />string subdirectory = _path + @"\" + findData.cFileName;<br />_folders.Add(new MyCompanyDirectoryInfo(subdirectory));<br />}<br />}<br />else<br />{<br />}<br />}<br />while (FindNextFile(findHandle, out findData));<br />FindClose(findHandle);<br />}<br />return _folders.ToArray();<br />}<br />public override bool Delete()<br />{<br />return RemoveDirectory(_path);<br />}<br />public bool Delete(bool recursively)<br />{<br />if (!recursively)<br />return Delete();<br />else<br />{<br />DeleteFilesAndFolders();<br />return Delete();<br />}<br />}<br />public void DeleteFilesAndFolders()<br />{<br />foreach (MyCompanyDirectoryInfo di in GetDirectories())<br />{<br />di.Delete(true);<br />}<br />foreach (MyCompanyFileInfo fi in GetFiles())<br />{<br />fi.Delete();<br />}<br />}<br />public bool Create(bool recursively)<br />{<br />if (recursively &amp;&amp;amp; Parent != null &amp;&amp; !Parent.Exists)<br />Parent.Create(recursively);<br />bool b = Create();<br />return b;<br />}<br />public void CopyTo(MyCompanyDirectoryInfo dest, string pattern, bool recursively)<br />{<br />if (recursively)<br />{<br />foreach (MyCompanyDirectoryInfo di in GetDirectories(pattern))<br />{<br />MyCompanyDirectoryInfo di2 = new MyCompanyDirectoryInfo(dest.FullName + "\\" + di.Name);<br />di2.Create(true);<br />di.CopyTo(di2, pattern, true);<br />}<br />}<br />foreach (MyCompanyFileInfo fi in GetFiles(pattern))<br />{<br />MyCompanyFileInfo fi2 = new MyCompanyFileInfo(dest.FullName + "\\" + fi.Name);<br />fi.CopyTo(fi2, false);<br />}<br />}<br />}<br />}<br /><br />----------------------------------------------------------------------------<br /><br /><br />using System;<br />using System.Collections.Generic;<br />using System.Text;<br />using System.IO;<br />using System.ComponentModel;<br />using System.Runtime.InteropServices;<br />using Microsoft.Win32.SafeHandles;<br />namespace MyCompany.MyProduct.Common.Win32<br />{<br />public class MyCompanyFileInfo : MyCompanyFileSystemInfo<br />{<br />public MyCompanyFileInfo(string path) : base(path)<br />{<br />//if ((int)(Attributes &amp; EFileAttributes.Directory) != 0)<br />// throw new ApplicationException("Directory path is specified. Use MyCompanyDirectoryInfo instead");<br />}<br />public FileStream Open(EFileAccess access,<br />EFileShare share,<br />ECreationDisposition creationDisposition,<br />EFileAttributes fileAttributes)<br />{<br />IntPtr handle = CreateFileInternal(access, share, creationDisposition, fileAttributes);<br />if (handle == INVALID_HANDLE_VALUE)<br />{<br />throw new Win32Exception(Marshal.GetLastWin32Error());<br />}<br />else<br />{<br />return new FileStream(new SafeFileHandle(handle, true), FileAccess.ReadWrite);<br />}<br />}<br />public MyCompanyDirectoryInfo Directory<br />{<br />get<br />{<br />return Parent;<br />}<br />}<br />public FileStream OpenRead()<br />{<br />return Open(EFileAccess.GenericRead,EFileShare.Read,ECreationDisposition.OpenExisting,EFileAttributes.Normal);<br />}<br />public FileStream Create()<br />{<br />return Open(EFileAccess.GenericAll, EFileShare.None, ECreationDisposition.CreateAlways, EFileAttributes.Normal);<br />}<br />public FileStream OpenWrite()<br />{<br />return Open(EFileAccess.GenericWrite, EFileShare.None, ECreationDisposition.OpenAlways, EFileAttributes.Normal);<br />}<br />public override bool Delete()<br />{<br />return DeleteFile(_path);<br />}<br />internal void CopyTo(MyCompanyFileInfo fi2, bool failIfExists)<br />{<br />CopyFile(_path, fi2._path, failIfExists);<br />}<br />}<br />}<br /></pre>]]></content:encoded></item></channel></rss>