Creating a SSCLI C# Template for Visual Studio
23 February 08 05:29 AM | jkuhne | 0 Comments   

In my last post (MSBuild your SSCLI C# projects) I demonstrated how to create an MSBuild targets file for the SSCLI (Rotor).   In this post I'll show you how to make it easier to use Visual Studio to develop SSCLI C# projects.

To build successfully in VS you need to do a few things, notably:

  1. Override assembly resolution so the SSCLI assemblies are targeted (the last post shows you how to do this)
  2. Change the startup action for the project to "Start external program" (in the Debug property page for the project)
  3. Enter the path for clix.exe as the startup program
  4. Put the output binary for you application in as the "Command line arguments"

It's a little tedious to set all of this up by hand.  Thankfully it ends up it's pretty easy to make a template to do most of the dirty work.  With a small additional change to the targets file it makes it completely painless.   (Well, with the one major caveat that you can't debug managed SSCLI code with VS.)

The existing templates are in the VS folder under "Common7\IDE\ProjectTemplates".  Each of the templates are stored in a zip file- perusing a few of these in addition to looking at the documentation will get you up to speed in no time.  As such I'm not going to give a tutorial here, I'm just going to talk briefly about what I did to create my template and share it with you.

I started with the "CSharp\Windows\1033\ConsoleApplication.zip" because I wanted the equivalent of that for SSCLI.  I had to make most of the changes to the .vstemplate file and a few changes to the .csproj file to link in my targets.  Here is what my .vstemplate file looks like:

<?xml version="1.0" encoding="utf-8"?>

<VSTemplate Version="3.0.0" Type="Project" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">

       <TemplateData>

              <Name>SSCLI Console Application</Name>

              <Description>Console Application for the SSCLI (Rotor) Framework</Description>

              <Icon>SSCLIConsole.ico</Icon>

              <TemplateID>SSCLI.CSharp.ConsoleApplication</TemplateID>

              <ProjectType>CSharp</ProjectType>

              <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>

              <MaxFrameworkVersion>2.0</MaxFrameworkVersion>

              <CreateNewFolder>true</CreateNewFolder>

              <DefaultName>SSCLIConsoleApplication</DefaultName>

              <ProvideDefaultName>true</ProvideDefaultName>

       </TemplateData>

       <TemplateContent>

              <Project File="ConsoleApplication.csproj" ReplaceParameters="true">

                     <ProjectItem ReplaceParameters="true" TargetFileName="Properties\AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>

                     <ProjectItem ReplaceParameters="true" OpenInEditor="true">Program.cs</ProjectItem>

              </Project>

       </TemplateContent>

</VSTemplate>

Not too hard to wrap your head around.  In the .csproj I changed the import to

<Import Project="$(MSBuildToolsPath)\SSCLI.CSharp.targets" />

Zipped up with a custom icon (had to try it) I dumped it in my "Documents\Visual Studio 2008\Templates\ProjectTemplates\Visual C#" folder, created a few projects off the template to tweak it out and I was almost done.

The other bit that I've already mentioned was setting the project properties to run "clix".  Figuring this bit out isn't too terribly straightforward.  VS project settings that don't go into the .csproj go into a ".csproj.user" file that is created alongside your .csproj whenever you change the defaults.  Change the defaults to create said file and you'll quickly discover what you need to set in your .targets file to establish your own defaults.  (Note that these properties are listed in the Microsoft.Build.Commontypes.xsd, found in the MSBuild directory in your relevant framework directory.  Also note that it doesn't contain valid values for the VS properties.)

Here is what I added to SSCLI.CSharp.targets (which should live in your MSBuild directory):

<!-- VS Specific Properties -->

<PropertyGroup>

       <!-- Set these so VS runs the host exe -->

       <StartAction>Program</StartAction>

       <StartProgram>$(SSCLI_FrameworkPath)\clix.exe</StartProgram>

       <StartArguments>$(TargetFileName)</StartArguments>

       <!-- Might as well turn this on as it is currently the only kind of debugging you can do in VS -->

       <EnableUnmanagedDebugging>true</EnableUnmanagedDebugging>

</PropertyGroup>

That's pretty much it.  Now you can relatively easily create SSCLI projects.   You don't get managed debugging, as mentioned.  You do get the rest of the development enviroment benefits however (intellisense, etc.).

 The complete SSCLI.CSharp.targets file can be found here.  The template can be found here.

 

MSBuild your SSCLI C# projects
20 February 08 06:21 AM | jkuhne | 3 Comments   

[ Edit 22 Feb 2008: The complete SSCLI.CSharp.targets file can be found here. ]

In my last post I got SSCLI compiling with the 2008 C++ compiler (SSCLI 2.0 and Visual Studio 2008).  (A side note: since it is clearly possible to build SSCLI with the compiler there is no reason you shouldn't be able to get a VS Solution up and running that would build the SSCLI- just a lot of work.)  Having done that I thought it would be interesting to see how plausible actually working in VS with SSCLI projects would be.

As a first step, it ends up that there is relatively little you have to do to get a .csproj that will work with SSCLI through MSBuild.  The key items are:

  1. Set the Csc.exe path.
  2. Make sure the TargetFrameworkDirectory gets set to the SSCLI output directory.

The way to figure this out is to work backwards from the compiler (csc.exe).  Look at what csc's arguments are and look for where arguments get generated for it in the build targets.   

Microsoft.CSharp.targets is the file to start looking in.  It is included at the end of a .csproj so this is a pretty logical place to start looking.  Thankfully the file is short, so it is relatively easy to digest.  (The target files are in the Windows\Microsoft.Net\Framework\v0.0 directories.  I used 3.5 for this article- the last set of MSBuild targets before this version are in 2.0.50727.)

The <Csc> task in Microsoft.CSharp.Targets has a "ToolPath" argument that takes $(CscToolPath).  There is #1!  Finding #2 begins with "References" which is set to @(ReferencePath).  You won't find much in Microsoft.CSharp.Targets so you need to look to the included Microsoft.Common.Targets.

You can figure out #2 by looking at the generation of @(ReferencePaths).  Doing a search will drop you in the ResolveAssemblyReferences target- it's in the comments as an [OUT] -- bingo.  In a csproj referenced .NET assemblies are listed under (logically) <Reference> items.  These are listed here as an [IN] in the comments - see the connection?  (Note: the quick way to look for usage of items is to search for '@(ItemName' -- don't use the closing parenthesis as you'll miss transforms.)

In this target the simple "System" <Reference> is turned into a full @(ReferencePaths) item.  If you look at the <ResolveAssemblyReference> task within this target you'll see some promising parameters-- the most promising being SearchPaths="$(AssemblySearchPaths)".   Look for the source of $(AsemblySearchPaths) and you'll find a nice detailed comment  at the top of Common.Targets:

<!--

  The SearchPaths property is set to find assemblies in the following order:

 

    (1) Files from current project - indicated by {CandidateAssemblyFiles}

    (2) $(ReferencePath) - the reference path property, which comes from the .USER file.

    (3) The hintpath from the referenced item itself, indicated by {HintPathFromItem}.

    (4) The directory of MSBuild's "target" runtime from GetFrameworkPath.

        The "target" runtime folder is the folder of the runtime that MSBuild is a part of.

    (5) Registered assembly folders, indicated by {Registry:*,*,*}

    (6) Legacy registered assembly folders, indicated by {AssemblyFolders}

    (7) Look in the application's output folder (like bin\debug)

    (8) Resolve to the GAC.

    (9) Treat the reference's Include as if it were a real file name.

-->

<AssemblySearchPaths Condition=" '$(AssemblySearchPaths)' == '' ">

       {CandidateAssemblyFiles};

       $(ReferencePath);

       {HintPathFromItem};

       {TargetFrameworkDirectory};

       {Registry:$(FrameworkRegistryBase),$(TargetFrameworkVersion),$(AssemblyFoldersSuffix)$(AssemblyFoldersExConditions)};

       {AssemblyFolders};

       {GAC};

       {RawFileName};

       $(OutDir)

</AssemblySearchPaths>

{TargetFrameworkDirectory} is key here.  If you search for "TargetFrameworkDirectory" you'll find the property shows up in the GetFrameworkPaths and GetReferenceAssemblyPaths targets.  It is the one we want.  It gets replaced by $(TargetFrameworkDirectory) inside of the <ResolveAssemblyReference> task.  You can see this if you use Reflector to look at the task-- but you can probably make that leap without digging further.

That gives us everything we need.  We know where all of our framework (SSCLI) assemblies are so we just need to override Common.targets' path building completely for this and inject our overrides into the regular targets.  You can do this by creating a new target file that includes the Microsoft.CSharp.targets and use this new file instead in your .csproj.   Here's the answer:

<Project InitialTargets="SSCLI_InitialChecks" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- Put this first as this file contains overrides for the standard targets -->

  <Import Project="Microsoft.CSharp.targets" />

 

  <PropertyGroup>

    <!-- Assuming x86 debug build of the framework -->

    <SSCLI_FrameworkPath>$(ROTOR_DIR)\binaries.x86chk.rotor</SSCLI_FrameworkPath>

    <CscToolPath>$(SSCLI_FrameworkPath)</CscToolPath>

    <TargetFrameworkDirectory>$(SSCLI_FrameworkPath)</TargetFrameworkDirectory>

    <!-- This one isn't supported by the sscli csc.exe -->

    <ErrorReport></ErrorReport>

  </PropertyGroup>

 

  <Target

    Name="SSCLI_InitialChecks">

    <Error

      Condition=" '$(ROTOR_DIR)' == '' "

      Text="ROTOR_DIR is not set.  Please set an environment variable or property to the SSCLI directory.  Note that running 'env' in your SSCLI directory will set this."/>

    <Error

      Condition="!Exists('$(SSCLI_FrameworkPath)')"

      Text="SSCLI not found at expected path: '$(SSCLI_FrameworkPath)'  Please build SSCLI if necessary." />

  </Target>

 

  <!--

    Override Microsoft.Common.targets GetFrameworkPaths

  -->

  <Target

    Name="GetFrameworkPaths"

    DependsOnTargets="$(GetFrameworkPathsDependsOn)" />

 

  <!--

    Override Microsoft.Common.targets GetFrameworkPaths

  -->

  <Target

    Name="GetReferenceAssemblyPaths"

    DependsOnTargets="$(GetReferenceAssemblyPathsDependsOn)" />

</Project>

Dump the xml above into a file (say, called "SSCLI.CSharp.targets"), drop it in the same directory as the other target files, and (as already stated) include this file in your csproj instead of Microsoft.CSharp.targets.

Now you can 'msbuild myNiftySscliApp.csproj'.   And yes, you can even open said csproj in Visual Studio and do some developin'.  (With the sad lack of managed debugging, but, hey-- you get Intellisense and all of the other goodies of the IDE.)  In my next posts I'll show you how to grease the wheels as much as you can with the IDE, starting with how to build and run easily, then moving on to making an SSCLI project template.

 

SSCLI 2.0 and Visual Studio 2008
19 February 08 09:30 AM | jkuhne | 7 Comments   

The currently available SSCLI 2.0 (Rotor) does not build successfully with Visual Studio 2008.   I've managed to make it build and I thought I'd share my changes.   Be very aware:

  1. I haven't done anything resembling thorough testing on these changes
  2. I made the changes so that it would build for me on my 32bit Vista machine
  3. I avoided changing source files, and focused on build files
  4. The warnings I disabled are pretty tame, see #3
  5. I tried to keep the changes to a minimum
  6. I know next to nothing about NMAKE
  7. I have no idea about any MS plans regarding SSCLI and VS2008
  8. This is does not constitute official support for building SSCLI in VS2008

\sscli20\mswin32.startup.pl

  1. change the if block that begins at line 41:

if (my $msvcdir = Get("MSVCDir")) {

    # These are the INCLUDE paths used during the primary bootstrap.

    my $winsdkdir = Get("ROTOR_WINSDKDIR");

    Set("SDK_INC_PATH_BOOT", catdir ($winsdkdir, "include"));

    Set("CRT_INC_PATH_BOOT", catdir ($msvcdir, "Include"));

    Set("SDK_LIB_PATH", catdir ($winsdkdir, "lib"));

    Set("CRT_LIB_PATH", catdir ($msvcdir, "lib"));

    Set("VC_BIN_PATH", catdir ($msvcdir, "bin"));

}

 \sscli20\win.env.bat

  1. change %VS80COMNTOOLS% on line 37 to %VS90COMNTOOLS%
  2. after line 42, insert the following:

REM Set the SDK path

if "%ROTOR_WINSDKDIR%"=="" set ROTOR_WINSDKDIR=%WindowsSdkDir%

if "%ROTOR_WINSDKDIR%"=="" set ROTOR_WINSDKDIR=%MSVCDIR%\PlatformSDK

call :ShortName "%ROTOR_WINSDKDIR%"

set ROTOR_WINSDKDIR=%RESULT%

\sscli20\clr\src\classlibnative\nls\wks\sources; \sscli20\clr\src\debug\daccess\wks\sources; \sscli20\clr\src\vm\dacwks\sources; \sscli\clr\src\classlibnative\float\wks\sources; \sscli\clr\src\dlls\mscordac\wks\sources; \sscli\clr\src\vm\wks1\sources; \sscli\clr\src\vm\wks2\sources; \sscli\clr\src\vm\wks3\sources; \sscli\clr\src\vm\wks4\sources

  1. Kill line 16 (MSC_WARNING_LEVEL =-Wp64)

\sscli\clr\src\classlibnative\float\sources.inc 

  1. Kill line 21 (COMPILER_WARNINGS =/W4)

\sscli20\clr\src\toolbox\sos\strike\sources

  1. Kill line 18 (MSC_WARNING_LEVEL =/W4)

\env\bin\devdiv.def

  1. Change line 334 to SUBSYSTEM_WINVER = ,5.00
  2. Change line 338 to SUBSYSTEM_CONVER = ,5.00

\pal\inc\vs9.h (NEW FILE)

// Patch up the conflicting defines to allow building with VS9

#undef MAKEWORD

#undef MAKELONG

#undef LOWORD

#undef HIWORD

#undef LOBYTE

#undef HIBYTE

#undef IPPROTO_IP

#undef IPPROTO_ICMP

#undef IPPROTO_IGMP

#undef IPPROTO_GGP

#undef IPPROTO_TCP

#undef IPPROTO_PUP

#undef IPPROTO_UDP

#undef IPPROTO_IDP

#undef IPPROTO_ND

#undef IPPROTO_RAW

#undef INADDR_ANY

#undef INADDR_BROADCAST

#undef INADDR_NONE

 

#pragma warning (disable :4985)

 

#define IMAGE_SIZEOF_NT_OPTIONAL32_HEADER    224

#define IMAGE_SIZEOF_NT_OPTIONAL64_HEADER    240

 

#ifdef _WIN64

#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER     IMAGE_SIZEOF_NT_OPTIONAL64_HEADER

#else

#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER     IMAGE_SIZEOF_NT_OPTIONAL32_HEADER

#endif

 \sscli20\pal\win32\win32pal.h

  1. Add #include <Vs9.h> after line 1272 (before the windows includes) 

\sscli20\tools\binplace\sources; \sscli\clr\src\tools\ildbdump\sources

  1. Change line 27 SUBSYSTEM_VERSION=4.00 to SUBSYSTEM_VERSION=5.00

\sscli20\tools\resourcecompiler\sources

  1. Change line 31 SUBSYSTEM_VERSION=4.00 to SUBSYSTEM_VERSION=5.00

\sscli20\tools\cppmunge\sources

  1. Change line 24 SUBSYSTEM_VERSION=4.00 to SUBSYSTEM_VERSION=5.00

\sscli20\tools\build\sources

  1. Change line 48 SUBSYSTEM_VERSION=4.00 to SUBSYSTEM_VERSION=5.00

\sscli20\pal\win32\make.cmd

  1. Kill the if not "%ROTOR_TOOLSET_VERSION%" block from line 43-46

\sscli20\clr\src\vm\sources.inc

  1. Add -wd4800 to the end of line 22
  2. Delete lines 23-25

\ssclie20\clr\src\classlibnative\nls\sources.inc

  1. Add -wd4800 to the end of line 20

\sscli20\csharp\sccomp\sources.inc

  1. Kill lines 52-54 (USER_C_FLAGS)

\sscli20\pal\win32\rotor_pal.src

  1. Replace line 57 (#if _MSC_VER == 1400) with:

#if _MSC_VER == 1500

  #define _MSVCRT MSVCR90D

 #elif _MSC_VER == 1400

  1. b.  Replace line 67 (#if _MSC_VER == 1400) with:

#if _MSC_VER == 1500

  #define _MSVCRT MSVCR90

 #elif _MSC_VER == 1400

 

Well there you have it.  Hopefully I didn't miss any of my changes or make too many typos.  I ran through these instructions on a clean install of SSCLI and was able to successfully build per the docs and compile and run HelloWorld.

 

Filed under: , ,
Tools for Digging Deeper Into .NET
18 February 08 03:02 AM | jkuhne | 1 Comments   

As I’ve been preparing more things to talk about I realized that it would be beneficial to have a list of tools to refer back to in my posts.  So here you go--a list of tools I find essential to development in .NET.

The most important tool is Lutz Roeder’s .NET Reflector (http://www.aisto.com/roeder/dotnet/).  This powerful tool makes analyzing .NET assemblies easy.  If you aren’t using this already you need to start yesterday.

Some of the .NET sources have been released and as such you can now directly debug into .NET.  You do need VS 2008 (non-express) for this to work. http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx

The Shared Source Common Language Infrastructure (SSCLI) is your friend.  You can learn an awful lot about the internals of the CLR by looking at this shared source version. http://research.microsoft.com/sscli/

If you’re running up against a wall debugging in VS, WinDbg can come to your rescue.  The Debugging Tools for Windows install quickly and are extremely powerful. http://www.microsoft.com/whdc/devtools/debugging/default.mspx

SOS (Son of Strike) is a .NET debugging extension that is usable in both VS and WinDbg.  This comes with the Framework so you’re already good to go.   However, there is a cool extension to the extension available (SOSEX):  http://www.stevestechspot.com/SOSEXANewDebuggingExtensionForManagedCode.aspx

Process Explorer isn’t specifically .NET related, but it is an essential tool for figuring out what the heck has file/directory X open.  Use it to replace Task Manager and find happiness again.  http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx

.NET Profiler lets you take a look at the pressure your application is putting on the managed heap.  It can be downloaded here: http://www.microsoft.com/downloads/details.aspx?FamilyID=a362781c-3870-43be-8926-862b40aa0cd0&DisplayLang=en  Details on usage can be found here: http://msdn2.microsoft.com/en-us/library/ms979205.aspx

Filed under: , ,
Where I've Been
18 February 08 01:03 AM | jkuhne | 0 Comments   

It has been a long time since I've posted here.

For the past couple years I've been moving around the Expression team.  For the first version of Expression Studio I pulled together the setup and the internal build system.  Getting up to speed on MSBuild, localization, code signing, MS Installer technology, WiX, and more than a few other things kept me pretty buried up through the V1 release.

Post V1 I've moved toward feature development on Blend.  I've been primarily working on the project system.

Now that I've gotten my feet planted in the Blend codebase I hope to start being a little more active here in the near future.

Filed under:
Using /clr and __declspec(thread)
19 April 06 01:40 AM | jkuhne | 1 Comments   

Sorry I haven't been writing much lately, but a lot has been going on in Expression land.  I've switched over to working on build and setup development so you can expect to see the flavor of my posts change somewhat.

Here's a frustrating bit I ran into playing around with C++/CLI:  __declspec(thread) isn't supported by the CLR.  You may get the application to compile properly, but you'll get the following error when trying to run:

"The application failed to initialize properly (0xc000007b)."

You can still use thread locals, but you're stuck managing the TLS_ apis yourself.  I'm including a simple template as one suggestion on how to do this:

template <typename T>

class ThreadLocal

{

private:

DWORD threadLocalIndex;

 

       ThreadLocal(ThreadLocal const&);

 

       T *GetPointer(void)

       {

              return static_cast<T*>(::TlsGetValue(this->threadLocalIndex));

       }

 

       void SetPointer(T *value)

       {

              ::TlsSetValue(this->threadLocalIndex, static_cast<void*>(value));

       }

 

public:

       void SetValue(const T &value)

       {

              T* currentPointer = this->GetPointer();

              if (currentPointer == NULL)

              {

                     this->SetPointer(new T(value));

              }

              else

              {

                     *currentPointer = value;

              }

       }

 

       T &GetValue(void)

       {

              T* currentPointer = this->GetPointer();

              if (currentPointer == NULL)

              {

                     this->SetPointer(new T());

              }

              return *this->GetPointer();

       }

 

       void DeleteValue()

       {

              T* currentPointer = this->GetPointer();

              if (currentPointer != NULL)

              {

                     delete currentPointer;

                     this->SetPointer(NULL);

              }

       }

 

       ThreadLocal(const T& value)

       {

              this->threadLocalIndex = ::TlsAlloc();

              this->SetValue(value);

       }

 

       ThreadLocal()

       {

this->threadLocalIndex = ::TlsAlloc();

       }

 

       virtual ~ThreadLocal()

       {

              this->DeleteValue();

              ::TlsFree(this->threadLocalIndex);

       }

};

 

The above template can replace a __declspec(thread) variable fairly easily.  It will handle allocating and deallocating for you.  Simple example:

static __declspec(thread) int threadMeaning=42;

... becomes ..

static ThreadLocal<int> threadMeaning(42);

Using the ThreadLocal is done through SetValue() and GetValue()  ie- threadMeaning.SetValue(2001).

The example doesn't have error checking and I haven't done anything terribly fancy. You really should try and use the framework for your thread locals if you can.  If you can't, hopefully this will be of use.

Enabling Remote Desktop remotely
13 April 06 11:17 PM | jkuhne | 1 Comments   

Ran into a situation where I needed to get remote desktop access to a machine where I hadn't previously enabled it.  I'm not the first to post about this, but the other posts I've seen didn't have information on opening the firewall (XP SP2).  Here's one solution:

reg add "//machinename/HKLM/SYSTEM/CurrentControlSet/Control/Terminal Server" /f /v fDenyTSConnections /t REG_DWORD /d 0

reg add "//machinename/HKLM/SYSTEM/CurrentControlSet/Services/SharedAccess/Parameters/FirewallPolicy/DomainProfile/GloballyOpenPorts/List" /f /v 3389:TCP /t REG_SZ /d 3389:TCP:*:Enabled:@xpsp2res.dll,-22009

reg add "//machinename/HKLM/SYSTEM/CurrentControlSet/Services/SharedAccess/Parameters/FirewallPolicy/StandardProfile/GloballyOpenPorts/List" /f /v 3389:TCP /t REG_SZ /d 3389:TCP:*:Enabled:@xpsp2res.dll,-22009

You have to be an administrator, of course. You can reboot the machine by typing

shutdown -m \\machinename -r -f

Interop with Office
25 January 06 03:15 AM | jkuhne | 0 Comments   

Before I dive in, I've put together a simple doc showing how you can liven up the clip art in Office using Graphic Designer that you might find of interest.

The other "feature" that was included in the current CTP of Expression Graphic Designer (EGD) is better interop with Office apps.  EGD now has vastly better support for importing WMF/EMF files and clipboard data.  In addition, if you want to pull out the original source image for a graphic in Office you can do so through the "Office Image" selection in the Paste Special menu item.

These were both done by me a few months ago as I grew frustrated with trying to get content back and forth between Office.  There is a lot of strangeness that occurs that makes the experience less than stellar.  To help shed some light on the situation I'll run through exactly what happens in the rest of this post.

When you copy from EGD a BMP, PNG, GIF, and JPG are put on the clipboard at the current resolution of the document.  PNG is particularly useful with Office applications as it supports transparency.  The first strangeness is that if you paste into Office you get a different default paste depending on the application you are in.  In the case of Word you do not get PNG by default and as such you must paste special to get the PNG.  In PowerPoint, however, you do get the PNG by default.

Where things get particularly ugly is when you copy out of Office.  Office will always render graphics at your screen resolution (typically 96dpi).  For me, at least, this usually ends up looking horrible.  A great example is taking a screenshot out of a PowerPoint presentation.

To get the original image out of Office you hit the second strangeness...  In Word it isn't possible (afaik).  If you want to get the image out you must first copy from Word and then into PowerPoint where you can save the original.  (There are more differences with the other Office apps.  I'll only detail Word/PowerPoint here for now.)  In PowerPoint if you right click on a graphic and select "Save As Picture".  Depending on the source you may get a drop-down arrow on the "Save" button that will let you "Save Original Picture".

Since Office obviously keeps the original PNG I thought it would be much better to grab that if it were at all possible, which fortunately it is.  If you copy an image out of Office you will get the new paste special selection of "Office Image" as previously described.  The image will be inserted as an image object that will be sized the same as the object in Office with the full DPI of the backing image.  (Rotation is something we might support later.  Some other special features such as drop shadows might be added if there is enough interest.)

If you take things back into Office again you'll rasterize at the doc DPI as stated earlier.  It is best, of course, to keep XPR files around for any content you send to Office.

Office drawings (AutoShapes, WordArt, etc.) are put on the clipboard as EMF, so you're best off pasting special in EGD as this format.  (Same goes with WMF/EMF clipart.)

(This brings up an interesting little side effect that you can take advantage of in Office.  If you copy an Office object that normally doesn't antialias such as a chart you can get antialiasing by making a few in-app conversions.  Take a chart, copy, and then paste as EMF back into the Office app.  After pasting as EMF right click and select "Edit Picture".  You now have an antialiased copy of the original!)

I intend to make the experience a little more transparent in the future if at all possible.  I'll be looking for any and all feedback on what would make life easier for you.  In addition, if you have any WMF/EMF content that you can't get into EGD, please let me know and I'll take a look.  A metafile is simply a collection of Win32 drawing commands, which makes it pretty broad in theory.  In practice, however, not many calls are supported by apps that output metafiles.  Your input will help me prioritize.

Filed under:
New versions of Expression Graphic Designer and Interactive Designer available!
24 January 06 08:07 PM | jkuhne | 1 Comments   

Our January 2006 CTPs are available!  http://www.microsoft.com/products/expression/en/default.mspx

What's new??

  • We've got official names now.  (Acrylic => Graphic Designer; Sparkle => Interactive Designer)
  • Interactive Designer is publicly available for the first time!
  • Exporting to XAML from Graphic Designer (EGD) has been greatly improved.

The XAML exporter is the only real new feature for this CTP for EGD. 

[There is, however, something important buried in there if you do any sort of interop with Office apps.  I'll post about it later today, so keep an eye out. ;)]

Filed under:
New version of Acrylic available for download!
17 October 05 04:35 AM | jkuhne | 0 Comments   

I encourage you to take a look.  We've done a lot to tighten things up since the last release.  We're still hard at work, so any and all feedback is greatly appreciated.  (Links to the newsgroup can be found on the site.)  http://www.microsoft.com/expression.

Filed under:
Expression on Channel9
14 September 05 08:42 PM | jkuhne | 1 Comments   

A nice video that will give you a bit of what's going on with Expression.

http://channel9.msdn.com/Showpost.aspx?postid=115387

Filed under:
How suite it is: Expression!
14 September 05 05:44 PM | jkuhne | 1 Comments   

http://www.microsoft.com/expression

It's a great relief to finally have this out in the open.  There's a suite on the way, folks, and we're working hard every day to pack as much cool functionality in as we can for you.

Take a look at the web site, download the Acrylic preview!  We're eager for feedback!

[Modified 2/17/2008: removed dead picture link]

Filed under:
Looking at double buffering and the new BufferedGraphics classes
08 September 05 07:59 AM | jkuhne | 0 Comments   

I started spending some time looking at double buffering with Windows Forms a little while back and noticed that in the 2.0 framework there are a couple of new BufferedGraphics classes.  I toyed around with them a bit and got them to work, but didn’t really understand what was going on.  Looking into things again the past few days I found the available material somewhat lacking, so I did some investigation using Reflector and thought I’d share what I’ve found.

[What is double buffering?  It’s drawing on an off-screen bitmap then copying it onto the display to help avoid redraw flickering issues.]

Double buffering will be handled automatically for you if the OptimizedDoubleBuffer style is set to true through SetStyle().   MSDN: Double Buffered Graphics.

[Double buffering will also be done if the DoubleBuffer, UserPaint, and AllPaintingInWmPaint styles are all set to true.  Setting the DoubleBuffered property to true is another way to set the OptimizedDoubleBuffer style to true (as well as AllPaintingInWmPaint, but oddly it doesn’t turn off AllPaintingInWmPaint when set to false).  As of Beta2 the documentation says that DoubleBuffer style is obsolete, but it is still checked in the code.  (I’ve seen mention that double buffering is on by default in the 2.0 framework, but I haven’t seen this to be true in Beta2.)]

If you wish to manually manage double buffering you can do so using the new BufferedGraphics classes.   MSDN: How to: Manually Manage Buffered Graphics.  There are three key classes that you need to be familiar with:

  1. BufferedGraphics.
  2. BufferedGraphicsContext.
  3. BufferedGraphicsManager.

These classes are what Windows Forms uses to double buffer if the appropriate styles are set.  Let’s take a in-depth look at each in turn:

BufferedGraphicsManager

System.Drawing.BufferedGraphicsManager is a pretty simple class that has a static constructor that creates a BufferedGraphicsContext instance that you can obtain through the Current property.  It also registers itself with the ProcessExit and DomainUnload events so that it can call Invalidate() on the created BufferedGraphicsContext as the app shuts down.

BufferedGraphicsContext

The BufferedGraphicsContext class handles allocating and releasing the off-screen bitmap that you draw to (which you access through the BufferedGraphics class it gives back to you).  You can get an app-wide instance of this class from the BufferedGraphicsManager.Current property.  You can also manually create a BufferedGraphicsContext class, but then you are responsible for calling Dispose() when you are finished with it to release created resources.  Note that disposing of a context class will delete its BufferedGraphics object if it has one active. (An alternative to calling Dispose is calling Invalidate(), which will call Dispose() when you dispose of the BufferedGraphics object created through the context.)
 
Calling Allocate() from a BufferedGraphicsContext will create a compatible Graphics object / Display Device Context (DC) that wraps an off-screen bitmap for the specified Graphics object / DC.  This new Graphics object is accessed through the Graphics property of the BufferedGraphics object.  [Win32 Fyi:  The bitmap is created using CreateDIBSectionhSection is set to NULL which allows the system to allocate the memory for the bitmap, which is then deallocated when the class later calls DeleteObject on the bitmap handle.]

Setting the MaximumBuffer property sets the threshold for the bitmap size that is kept in memory after disposing of a BufferedGraphics object.  If the size requested is larger than the MaximumBuffer size a new temporary BufferedGraphicsContext is created that will automatically be disposed of when the BufferedGraphics object it returns is disposed.  (Note that if the context has already created a BufferedGraphics object that has not been disposed a temporary BufferedGraphicsContext will also be created.)

BufferedGraphics

This is the object that provides you access to the off-screen bitmap that a BufferedGraphicsContext class creates (through Allocate()).  You can draw to the Graphics object returned from the Graphics property just like you normally would.  When you’re finished drawing, calling Render() on the BufferedGraphics object will call the Win32 BitBlt function using the SRCCOPY ROP method (just copies), positioning the bitmap based off of the Position property of the rectangle passed to the Allocate() method when creating this BufferedGraphics object.   By default this copies to the Graphics object / DC specified in the Allocate() method, but you can specify any Graphics object / DC as a destination through the Render() overloads.


The Windows Forms WM_PAINT  Handler

I’ve included some (very) pseudo-code here to help give you an idea of what happens when a Windows Forms control handles the WM_PAINT message from Windows.  This is important to look at to understand how the different styles will affect the painting process.  (Note that there are also calls to BeginPaint/EndPaint and SelectPalette.  The HDC is also set up.)

if (OptimizedDoubleBuffer || (DoubleBuffer && UserPaint && AllPaintingInWmPaint))
{
 create a BufferedGraphics from the context returned from the BufferedGraphicsManager
 set the clipping rectangle
 save the Graphics state
 call OnPaintBackground  (which calls PaintBackground())  if not Opaque style
 restore the Graphics state
 call OnPaint which then raises the Paint event
 render the BufferedGraphics object
}
else
{
 if (AllPaintingInWmPaint) call OnPaintBackground delegates if not Opaque style // unless an HDC is passed in WPARAM, not sure when this will happen
 call OnPaint which then raises the Paint event
}

Two crucial things:

  1. Don't turn on double buffering if you're handling it yourself. If you have double buffering turned on and you're attempting handle your own double buffering of it you'll end up copying your back buffer into another back buffer that is then copied to the screen.
  2. The other interesting note here is that WM_ERASEBACKBROUND message handler checks for the the AllPaintingInWmPaint style.  You still get the background painted for you in the WM_PAINT handler if you don't have the Opaque style set.


Key Takeaways:

  • Don’t bother with the BufferedGraphics classes if you want simple double buffering.  Set the DoubleBuffered property to true.
  • If you want to handle your own double buffering:
    • Set the DoubleBuffered property and the DoubleBuffer style to false.
    • Override OnPaint(). Don’t call the base unless you deliberately want the Paint event raised.
  • If you are handling the background yourself set the AllPaintingInWmPaint and the Opaque styles.
  • If you want to minimize allocations of bitmaps:
    • Ensure you have created your own separate BufferedGraphicsContext classes for each BufferedGraphics you intend to use.
    • Ensure your MaximumBuffer property is large enough to hold the size of the off-screen bitmap.
    • Always call Dispose() on a BufferedGraphics class when you're done drawing.  (Specifically, before you call Allocate again on it's hosting BufferedGraphicsContext.)
  • Call Dispose() on any BufferedGraphicsContext class you create yourself when you're finished with them.
  • Remember the limitations:
    • You can’t change where the back buffer is copied to after allocating. (The offset in the target DC, that is.)
    • You can’t specify the raster operation (ROP).

 

That's about it for now.  This ended up being much more detailed than I expected and I've gotten a bit drowsy so hopefully this came through somewhat clear.  Any comments/corrections are welcome.


 

Setting up C++ Interop (with Win32) in VS 2005.
08 September 05 01:17 AM | jkuhne | 0 Comments   

I've already given the steps necessary to make this happen in an earlier post, but as this will come up so much I decided to pull it into a seperate post with more explicit details.  The basic things you need again are:

  • /clr option set for the project.  (Common Language Runtime support property under General.)
  • Include the appropriate Win32 headers.  (#include <windows.h> is the main one)
  • Appropriate target OS #defines. (see Using the Windows Headers for the right values)
  • If the project was started from one of the CLR templates, you'll need to remove $(NoInherit) from the Additional Dependencies property under Linker:Input.

As mentioned in the earlier post, it's probably a good idea to put the Win32 #includes in a precompiled header for speedier compilation (stdafx.h typically, see Creating Precompiled Header Files for more info).  Here is what this looks like (for the latest versions):

//
// Target platform defines:
//

#ifndef WINVER
#define WINVER 0x0501
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS 0x0410
#endif

#ifndef _WIN32_IE
#define _WIN32_IE 0x0500
#endif

// Disable warning for XML comments.
#pragma warning( disable : 4634 )

// Windows Header Files:
#include <windows.h>

#pragma warning( default : 4634 )

Ok, but what about the warning disable?  In Beta 2, turning on XML comments would cause parsing of all included headers as well.  Not really what you want given there are a number of places XML delimiters inadvertently show up (they aren't actual XML comments, so you are likely to get errors).  My last word was that in the final code includes that have the #include <header.h>  (as opposed to the #include "header.h") format will not be parsed to make this easier.

So... step-by-step:

  1. Create or load a project.
  2. Set the /clr switch.
  3. Ensure the dependencies property isn't set to $(NoInherit).
  4. Add the relevant Win32 #defines and #includes if needed (see above).
  5. Code. (See my other articles [1], [2] for more details.

That's basically it.  Some other useful links from MSDN:

New Version of Acrylic Available!
15 August 05 10:38 PM | jkuhne | 8 Comments   

I'm happy to report that the August 2005 Community Technology Preview is live on http://www.microsoft.com/products/expression!

There are a number of videos and feature details at http://www.microsoft.com/products/expression/features.aspx.

Have fun!

[Edited 2/17/2008:  Removed dead picture link.]

Filed under:
More Posts Next page »

Search

This Blog

Syndication

Page view tracker