共用方式為


Controlling App Capabilities when Using Shared Libraries in Windows Phone Apps

The Problem:

The other day I published an updated version of my Windows Phone app CalcuFitness after some major code refactoring.  Much to my surprise, I discovered that the app certification process determined that my app required the location services capability when it in fact does not make use of location services.  It wasn’t long before I realized what caused this.

I have several Windows Phone apps in development and, being a good developer, I try to share as much code between them as I can.  So I create common library projects that many of my apps reference.  Well, one of my apps did need to use location service functionality and I decided to put some of that functionality into one of the common libraries because it was general purpose enough to be used by other apps.  Well, my CalcuFitness app referenced that library and compiled that code into its binary.

When you submit your app to the Marketplace, they automatically scan your binary looking to see whether you use certain APIs to determine what app capabilities are required.  Since my app’s binary contained code that referenced an API associated with location services, the system marked my app as requiring that capability.  It doesn’t matter whether there’s a code path in my app that actually makes use of that API.

The Solution:

The way I went about solving this problem was to make use of conditional symbols and build configurations in Visual Studio to control the code that actually gets compiled for my phone app.  First, I define a conditional symbol for each of the capabilities that my code makes use of and wrap that code within #if…#endif preprocessing directives.  In my case, I created a LOCATION_SERVICE_CAPABILITY symbol and added it to my code like this:

 #if LOCATION_SERVICE_CAPABILITY
    // my location service code
#endif

Ok, so now this code will only be compiled if the project is built with the LOCATION_SERVICE_CAPABILITY symbol.  By default, my app will not compile this code since it’s not configured to have the LOCATION_SERVICE_CAPABILITY symbol defined.

Now let’s say that I have another app – let’s call it “MyLocationPhoneApp” – where I do want this location service functionality to be used.  I can’t just set the LOCATION_SERVICE_CAPABILITY symbol for the Debug and Retail build configurations because that would cause this code to be compiled into all my phone apps that reference this project.  Instead, I’m going to create a specific build configuration for my app.  To do this, I do the following:

  1. Open the project file of my library.
  2. Create a new build configuration by copying the Debug and/or Release configuration.
  3. Change the name of this new configuration by including the name of the phone app which references this library.
  4. Include additional symbol names in the DefineConstants that correlate to the symbols defined in the library that are used by the app.

For example, here’s the Debug configuration I created with the important parts highlighted:

 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'MyLocationPhoneApp_Debug|AnyCPU' ">
  <DebugSymbols>true</DebugSymbols>
  <DebugType>full</DebugType>
  <Optimize>false</Optimize>
  <OutputPath>Bin\Debug</OutputPath>
  <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE;LOCATION_SERVICE_CAPABILITY</DefineConstants>
</PropertyGroup>

I actually do this for both Debug and Release to ensure that I can build with the constant in either mode. Whenever this project is built using this build configuration, the location service-related code will be included in the compilation.  Ok, that’s good but I’m not done yet.

Next I need to open up Configuration Manager (Build –> Configuration Manager) in Visual Studio.  This window allows me to define which build configuration to use for each of the projects in my solution.  So when my solution is configured to build Debug, I configure my library project to build with the “MyLocationPhoneApp_Debug” configuration.

And I follow this pattern for each app that references this library project.  Essentially, the build configuration let’s me create an app profile that defines which capabilities should be exposed by the library.  This prevents code which impacts the phone capability requirements from entering into my app’s binary if it doesn’t require that capability.