21 February, 2009

Development Server не любит Vista

Под моей Vista x64 не работает Development Server довольно странным образом: он запускался, но ни один browser не мог к нему законнектится. Оказалось, что если заменить в строке browser'а localhost на 127.0.0.1, то все тут же заработает. Причину такого поведения вижу в том, что Vista поддерживает IPv6 и по умолчанию localhost транслируется в ::1. А на соответствующем порту IPv6 наш browser никто не слушает.

20 February, 2009

Не работает асинхронная закачка в WebClient

Обнаружил маленькую проблему с классом System.Net.WebClient. При попытке использования асинхронной закачки с HTTP сервера посредством DownloadStringAsync или DownloadDataAsync не приходили эвенты DownloadStringCompleted и DownloadDataCompleted соответственно. Проблема решиласть следующим оьразом:
ThreadPool.QueueUserWorkItem(state => client.DownloadStringAsync(uri), null);

18 February, 2009

Debug Visual C++ Redistributable Package своими руками

Довольно частое явление когда у клиента девственно чистый компьютер - в смысле без Visual Studio, а вам необходимо запустить debug версию вашего приложения на C++ для отладки например. Если ваша сборка использует динамическую линковку системных DLL, то почти наверняка ваше приложение не загрузится. Скорее всего причина кроется в том, что у клиента не установлен Visual C++ Redistributable Package. На сайте Microsoft лежат такие package, но только для release, для debug вы их там не найдете:
Так как же быть? Создадим такой package сами. Открываем Visual Studio, создаем новый solution. В диалоге выбора типа проекта выбираем Other Project Typoes|Setup Project. Потом добавляем необходимые merge module в проект и компилируем. Учтите, что для каждой платформы будь то x86 или x64 необходимо создать свой project в solution. Не забываем добавлять соответствующие policy. В х64 вариант обьязательно добавляем merge module для x86. Ниже приведен список необходимых merge module для:
Visual Studion 2005 (DebugCRT x86)
  • Microsoft_VC80_DebugCRT_x86.msm
  • policy_8_0_Microsoft_VC80_DebugCRT_x86.msm
Visual Studion 2005 (DebugCRT x64)
  • Microsoft_VC80_DebugCRT_x86.msm
  • Microsoft_VC80_DebugCRT_x86_x64.msm
  • policy_8_0_Microsoft_VC80_DebugCRT_x86.msm
  • policy_8_0_Microsoft_VC80_DebugCRT_x86_x64.msm
Visual Studion 2008 (DebugCRT x86)
  • Microsoft_VC90_DebugCRT_x86.msm
  • policy_9_0_Microsoft_VC90_DebugCRT_x86.msm
Visual Studion 2008 (DebugCRT x64)
  • Microsoft_VC90_DebugCRT_x86.msm
  • Microsoft_VC90_DebugCRT_x86_x64.msm
  • policy_9_0_Microsoft_VC90_DebugCRT_x86.msm
  • policy_9_0_Microsoft_VC90_DebugCRT_x86_x64.msm
Если вы используете MFC, то дополнительно добовляете MFC merge module в соответствующий проект по аналогии - имена должны содержать DebugMFC.
Остается только скомпилировать ваш solution и в результате вы получите msi для установки на компьютере клиента. Рекомендую также правильно и солидно назвать исталляцию и сменить имя msi файла, чтобы на стороне клиента это все не выглядело поделкой на коленке.
P.S. Стиль когда нужные сисетмные DLL кладутся в проект рядом с вашим приложением на мой взгляд является анахронизмом, хоть и работает. С другой стороны мне несложно придумать конфигурацию, когда такой фокус уже не прокатит. Самый простой пример: ваше приложение для x86 и для x64 лежат в одном каталоге.
P.P.S Все доступные merge module хрянятся в "%CommonProgramFiles%\Merge Modules".

14 February, 2009

Чего нету в Profiling API для .NET Compact Framework 3.5

На данный момент не реализованы в ICorProfilerInfo2 for .NET Compact Framework v3.5.7283 следующие методы:
  • GetClassFromObject
  • GetEventMask
  • GetFunctionFromIP
  • GetFunctionFromToken
  • SetFunctionIDMapper
  • GetTokenAndMetadataFromFunction
  • GetModuleMetaData
  • GetILFunctionBody
  • GetILFunctionBodyAllocator
  • SetILFunctionBody
  • GetAppDomainInfo
  • SetFunctionReJIT
  • SetILInstrumentedCodeMap
  • GetInprocInspectionInterface
  • GetInprocInspectionIThisThread
  • GetThreadContext
  • BeginInprocDebugging
  • EndInprocDebugging
  • GetStringLayout
  • GetCodeInfo2
  • GetClassFromTokenAndTypeArgs
  • GetFunctionFromTokenAndTypeArgs
  • EnumModuleFrozenObjects
  • GetThreadAppDomain
  • GetObjectGeneration
  • GetNotifiedExceptionClauseInfo
Дополнительно могу сказать, что GetModuleInfo() всегда в качестве базового адреса модуля возращает NULL.

Устанавливаем output path для C++ проектов из MSBuild

Задача не так проста как кажется с первого взгляда - я потратил несколько часов чтобы ее решить. Итак, нам нужно сгенерировать из MSBuild'а файлик с расширением vsprops и примерно таким содержимым:

<?xml version="1.0"?>
<visualstudiopropertysheet
  ProjectType="Visual C++"
  Version="8.00"
  Name="my project name"
  OutputDirectory="my output path">
</VisualStudioPropertySheet>

Далее мы должны передать имя этого файлика в таску VCBuild через параметр Override. При вызове дочернего MSBuild нужно использовать Properties прмерно такого выда:

<msbuild
  Projects="My.sln"
  Targets="Rebuild"
  Properties="$(BuildSolutionProperties);Configuration=Release;Platform=Win32;VCBuildOverride=My.vsprops;" />

Вот собственно и все...

06 February, 2009

Компилируем сборки для .NET 1.1 под Visual Studio 2008

Для начала нужно поставить .NET Framework 1.1. Теперь мы должны создать target файл, который мы затем подключим в csproj. Проблема в том, что набор опций csc.exe для .NET 1.1 немного не совпадает с набором опций для .NET 3.5. Поэтому некоторые опции задавлены. Вот пример такого файла с именем Csc11.Targets:

<?xml version="1.0" encoding="utf-8"?>
<!-- Included into the project files right after MS common targets. -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
    <PropertyGroup>
        <UseHostCompilerIfAvailable>False</UseHostCompilerIfAvailable>
        <TargetFrameworkVersion>v1.1</TargetFrameworkVersion>
        <ErrorReport></ErrorReport>
        <NoWarn>1591</NoWarn>
        <AssemblyOriginatorKeyFile></AssemblyOriginatorKeyFile>
        <CscToolPath>$(WinDir)\Microsoft.NET\Framework\v1.1.4322</CscToolPath>
    </PropertyGroup>
</Project>

Далее модифицируем csproj. Внимание, наш импорт должен быть последним в списке импортов!

<Import Project="Csc11.Targets" />

На этом все должно заработать. После модификации файла csproj, при загрузке, вам могут задать вопрос о том что ваш проект имеет внешние ссылки. Ответить нужно это вопрос нужно так:

04 February, 2009

А знаете ли вы что...

...размер файла в NTFS c атрибутом Compressed не может превышать 32Gbytes.

Получение полного имени OS

Зачем я публикую эту информацию? Все просто, когда она мне понадобилась я потратил кучу времени, чтобы ее найти в достаточно полном объеме.

Сразу оговорюсь, что OS семейства Windows 95/98/Milenium/NT 3.5x/NT 4.0 я не рассматриваю - так как у меня есть причины считать, что эти OS уже не встречаются сегодня достаточно широко.

Теперь несколько комментариев к аргументам функции. processorArchitecture получается при помощи native вызова GetNativeSystemInfo(). GetVersionEx() дает нам majorVersion, minorVersion, buildNumber, csdVersion, suiteMask и productType. GetProductInfo() - vistaProductType. А флаги IsXPTabletPC, IsXPMediaCenter, IsXPStarter, Is2K3ServerR2 - при помощи нескольких вызовов GetSystemMetrics() с соответствующими аргументами.

Как найти Adaption Kit Updates (AKU)?

Это достаточно важное число и оно по сути эквивалентно номеру ServicePack в обычных Windows. Найти его можно в реестре HKEY_LOCAL_MACHINE\SYSTEM\Versions|Aku

Секреты IL инструментации и профайлерного API для .NET Framework

По роду своей деятельности я занимаюсь в том числе и инструментацией IL. Задача состоит в инжекции в оригинальный IL callback'ов: в начало каждого стейтмента (вынимается из PDB) и в каждую точку выхода из функции. С точками возврата есть две проблемы: префикс tail. и throw/rethrow.

Первый в реальных программах практически не встречается, но при встрече гарантирует автору инструментатора головную боль. По спецификации после префикса tail. должен следовать call/calli/callvirt, а после него обязательно ret. Все бы хорошо, но по спецификации на ret разрешен переход (и какой урод это придумал). Объясню более подробно - вы должны проинструментировать все точки выхода из программы, с одной стороны вы не имеете права вставлять свои команды между tail., call/calli/callvirt и ret, а с другой стороны вы должны вставить перед командой ret код, в случае перехода на этот самый ret. Выход один - править структуру метода...

Второй вариант коварнее. При кидании exception'а невозможно на основании только кода метода понять куда нас этот exception занесет. Мы можем остаться в том же самом методе - в обработчике exception, а можем вылететь из метода как пробка из бутылки на несколько функций наверх по стеку. К чему я все это говорю? А к тому - слава великому Аллаху, что как минимум одному сотруднику компании Microsoft пришла в голову мысль добавить немного жизненно нужных event'ов в профайлерное API. Особый интерес представляют: ExceptionUnwindFunctionEnter, ExceptionUnwindFunctionLeave и ExceptionCatcherEnter.

И еще, в свете вышесказанного, флаги COR_PRF_MONITOR_EXCEPTIONS, COR_PRF_MONITOR_ENTERLEAVE и COR_PRF_MONITOR_CODE_TRANSITIONS должны всегда устанавливаться вместе. Последний флаг - это вообще магия без него нет метаданных в памяти для mscorlib и нет 90% Enter/Leave/Tailcall или Enter2/Leave2/Tailcall2 для managed кода.

А напоследок граф переходов обработчика exception так сказать для полноты картины: