Skip to content

Building detours with the VS 2008 compiler and 6.1/Server 2008 SDK

24-Sep-08

 

There are a couple of potential problems you may run into when trying to build detours using the VS 2008 SDK 6.1 tools and headers:

  • on an x64 system the make file will default to building for x64, which is disabled in the free version. To build for x64 you must get a license from Microsoft for $10000 — most likely you just want to build the x86 version though. You can configure building to x86 by setting an environment variable: 

    set DETOURS_TARGET_PROCESSOR=x86
     

  • your lib and include paths might not be set correctly, I recommend setting both to first the SDKs directory, then the VS directory like:
    set lib=C:\Program Files\Microsoft SDKs\Windows\v6.1\Lib;C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\lib
     
    set include=C:\Program Files\Microsoft SDKs\Windows\v6.1\Include;C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include
     
     
  • When you do a “nmake all” now, detours itself should build, but the compilation of the samples will abort with an error:

    symtest.cpp(270) : error C2664: 'BOOL (HANDLE,PSYM_ENUMMODULES_CALLBACK64,PVOID)' :
     cannot convert parameter 2 from 'overloaded-function' to 'PSYM_ENUMMODULES_CALLBACK64'
    of the functions with this name in scope match the target type
    NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN\cl.EXE"' :
     return code 0x2'
    Stop.
    NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN\nmake.exe"' : return code '0x2'
    Stop.
    NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN\nmake.exe"' : return code '0x2'
    Stop.

    Austin Donnelly has posted a patch for this problem, you’ll need to patch two files, first samples/findfunc/symtest.cpp:

    
    
     
     
    Index: symtest.cpp
    ===================================================================
    --- symtest.cpp    (revision 1)
    +++ symtest.cpp    (revision 2)
    @@ -80,7 +80,7 @@
     //////////////////////////////////////////////////////////////////////////////
     //
     #if (_MSC_VER > 1299)
    -static BOOL WINAPI SymEnumerateCallback(PSTR pszModule,
    +static BOOL WINAPI SymEnumerateCallback(PCSTR  pszModule,
                                             DWORD64 base,
                                             PVOID pvUserContext)
     {
    
    Second samples/traceapi/_win32.cpp: 
    
    
    Index: _win32.cpp
    ===================================================================
    --- _win32.cpp    (revision 1)
    +++ _win32.cpp    (revision 2)
    @@ -389,7 +389,7 @@
         = CharUpperW;
     
     BOOL (__stdcall * Real_CheckColorsInGamut)(HDC a0,
    -                                           LPVOID a1,
    +                                           LPRGBTRIPLE  a1,
                                                LPVOID a2,
                                                DWORD a3)
         = CheckColorsInGamut;
    @@ -5979,7 +5979,7 @@
                                          LPVOID a1,
                                          DWORD a2,
                                          LPDWORD a3,
    -                                     LPVOID a4)
    +                                     PCONSOLE_READCONSOLE_CONTROL a4)
         = ReadConsoleA;
     
     BOOL (__stdcall * Real_ReadConsoleInputA)(HANDLE a0,
    @@ -6033,7 +6033,7 @@
                                          LPVOID a1,
                                          DWORD a2,
                                          LPDWORD a3,
    -                                     LPVOID a4)
    +                                     PCONSOLE_READCONSOLE_CONTROL a4)
         = ReadConsoleW;
     
     BOOL (__stdcall * Real_ReadDirectoryChangesW)(HANDLE a0,
    @@ -7373,8 +7373,8 @@
                                                  LPFILETIME a1)
         = SystemTimeToFileTime;
     
    -BOOL (__stdcall * Real_SystemTimeToTzSpecificLocalTime)(LPTIME_ZONE_INFORMATION a0,
    -                                                        LPSYSTEMTIME a1,
    +BOOL (__stdcall * Real_SystemTimeToTzSpecificLocalTime)(const TIME_ZONE_INFORMATION* a0,
    +                                                        const SYSTEMTIME *a1,
                                                             LPSYSTEMTIME a2)
         = SystemTimeToTzSpecificLocalTime;
     
    @@ -7638,7 +7638,8 @@
     
     BOOL (__stdcall * Real_UpdateColors)(HDC a0)
         = UpdateColors;
    -
    +#pragma warning(push)
    +#pragma warning(disable:4995)
     BOOL (__stdcall * Real_UpdateICMRegKeyA)(DWORD a0,
                                              LPSTR a1,
                                              LPSTR a2,
    @@ -7658,7 +7659,7 @@
                                             LPVOID a4,
                                             DWORD a5)
         = UpdateResourceA;
    -
    +#pragma warning(pop)
     BOOL (__stdcall * Real_UpdateResourceW)(HANDLE a0,
                                             LPCWSTR a1,
                                             LPCWSTR a2,
    @@ -9709,7 +9710,7 @@
     }
     
     BOOL __stdcall Mine_CheckColorsInGamut(HDC a0,
    -                                       LPVOID a1,
    +                                       LPRGBTRIPLE a1,
                                            LPVOID a2,
                                            DWORD a3)
     {
    @@ -26018,7 +26019,7 @@
                                      LPVOID a1,
                                      DWORD a2,
                                      LPDWORD a3,
    -                                 LPVOID a4)
    +                                 PCONSOLE_READCONSOLE_CONTROL a4)
     {
         _PrintEnter("ReadConsoleA(%p,%p,%p,%p,%p)\n", a0, a1, a2, a3, a4);
     
    @@ -26152,7 +26153,7 @@
                                      LPVOID a1,
                                      DWORD a2,
                                      LPDWORD a3,
    -                                 LPVOID a4)
    +                                 PCONSOLE_READCONSOLE_CONTROL a4)
     {
         _PrintEnter("ReadConsoleW(%p,%p,%p,%p,%p)\n", a0, a1, a2, a3, a4);
     
    @@ -30240,8 +30241,9 @@
         return rv;
     }
     
    -BOOL __stdcall Mine_SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION a0,
    -                                                    LPSYSTEMTIME a1,
    +BOOL __stdcall Mine_SystemTimeToTzSpecificLocalTime(const TIME_ZONE_INFORMATION
    + * a0,
    +                                                    const SYSTEMTIME * a1,
                                                         LPSYSTEMTIME a2)
     {
         _PrintEnter("SystemTimeToTzSpecificLocalTime(%p,%p,%p)\n", a0, a1, a2);

    After you applied those two patches, “nmake all” should successfully build detours and all the samples.

MSI setup: how to do a sql server connection test

16-Oct-07

The WiX (Windows Installer XML) toolset is a collection of open source tools that assists the creation of MSI installations.
It includes a collection of custom actions that (among other things) allow including a sql database setup in your installation.
Setting up a database is quite simple:


DropOnReinstall="no" Server="[SQLSERVERNAME]" Database ="[SQLSERVERDATABASENAME]">


Note that the server and database name are variables. They are filled in by the user during the GUI part of the installation.

Unfortunately, no custom action is provided that would allow you to do a connection test before actually executing the sql custom action. If the user makes a typo, the custom action will fail at install time and the entire installation is rolled back.

As this is not really acceptable, I’ve written a C++ custom action that does a connection test. With this custom action I can test the user provided credentials before the actual installation starts – and provide feedback if the credentials are incorrect.

Using the code and libraries that come with WiX, writing the custom action was really quite simple:

 

#include 
#include 
#include 
#include "wcautil.h"
#include "dutil.h"
#include "sqlutil.h"
#include "strutil.h"
 
void ReleaseString(LPWSTR psz) {
if(psz) {
StrFree(psz);
}
}
 
extern "C" UINT __stdcall CheckSqlConnection(MSIHANDLE hInstall) {
LPWSTR pszDbName = NULL;
LPWSTR pszServerName = NULL;
LPWSTR pszUserName = NULL;
LPWSTR pszAuthentificationMode = NULL;
LPWSTR pszPassword = NULL;
BOOL integratedMode = FALSE;
 
HRESULT hr = WcaInitialize(hInstall, "CheckSqlConnection");
ExitOnFailure(hr, "Failed to initialize");
 
hr = WcaSetProperty(L"SQLCONNECTIONTESTRESULT", L"Not checked");
ExitOnFailure(hr, "Failed to set property");
 
hr = WcaGetProperty(L"SQLSERVERDATABASENAME", &pszDbName);
ExitOnFailure(hr, "Failed to get property");
 
hr = WcaGetProperty(L"SQLSERVERNAME", &pszServerName);
ExitOnFailure(hr, "Failed to get property");
 
hr = WcaGetProperty(L"SQLSERVERUSERNAME", &pszUserName);
ExitOnFailure(hr, "Failed to get property");
 
hr = WcaGetProperty(L"SQLSERVERAUTHENTIFICATIONMODE", &pszAuthentificationMode);
ExitOnFailure(hr, "Failed to get property");
 
integratedMode = wcscmp(pszAuthentificationMode, L"1") == 0;
 
hr = WcaGetProperty(L"SQLSERVERPASSWORD", &pszPassword);
ExitOnFailure(hr, "Failed to get property");
 
IDBCreateSession* sessionId;
hr = SqlConnectDatabase(pszServerName, L"", L"master", integratedMode, pszUserName, pszPassword, &sessionId);
 
if (hr == ERROR_SUCCESS) {
hr = WcaSetProperty(L"SQLCONNECTIONTESTRESULT", L"1");
ExitOnFailure(hr, "Failed to set property");
}
else {
hr = WcaSetProperty(L"SQLCONNECTIONTESTRESULT", L"0");
ExitOnFailure(hr, "Failed to set property");
}
 
LExit:
ReleaseString(pszDbName);
ReleaseString(pszServerName);
ReleaseString(pszUserName);
ReleaseString(pszAuthentificationMode);
ReleaseString(pszPassword);
return WcaFinalize(hr);
}
 

You have to export the CheckSqlConnection method (e.g. by using an exports.def file) and link with msi.lbi (comes with the windows sdk) and the WiX libraries dutil.lib and wcautil.lib.

In my setup GUI I’ve included a dialog that allows entering the sql credentials and do a connection test (note: SharpDevelop comes with a handy GUI designer for WiX)

 <dialog id="SCSqlServerConnection" title="!(loc.SC_DefaultDlgTitle)" nominimize="yes" height="270" width="370">
<control id="TextSqlServerName" height="16" width="194" y="54" x="20" type="Text">
<text>!(loc.SC_SqlServerName)</text>
</control>
<control id="editSqlServerName" height="15" width="173" y="72" x="21" type="Edit" property="SQLSERVERNAME"></control>
<control id="SqlAuthenticationSelection" height="44" width="340" y="130" x="20" type="RadioButtonGroup" property="SQLSERVERAUTHENTIFICATIONMODE">
<radiobuttongroup property="SQLSERVERAUTHENTIFICATIONMODE">
<radiobutton height="16" width="179" y="4" x="4" value="1" text="!(loc.SC_SqlServerAuthentificationIntegrated)" />
<radiobutton height="16" width="151" y="24" x="4" value="2" text="!(loc.SC_SqlServerAuthentificationSql)" />
</radiobuttongroup>
</control>
<control id="Back" height="17" width="56" y="243" x="180" type="PushButton" text="!(loc.WixUIBack)" />
<control id="Next" height="17" width="56" y="243" x="236" type="PushButton" text="!(loc.WixUINext)" default="yes">
<condition action="enable">
SQLCONNECTIONTESTRESULT = 1
 
</condition>
<condition action="disable">
<![CDATA[SQLCONNECTIONTESTRESULT <>1]]&gt;
</condition>
<!--
&lt;publish Event="SpawnDialog" Value="CheckConnection">1</publish>&#8211;>
</control>
<control id="Cancel" height="17" width="56" y="243" x="304" type="PushButton" text="!(loc.WixUICancel)" cancel="yes">
 
<publish value="CancelDlg" event="SpawnDialog">1</publish>
</control>
<control id="BannerBitmap" height="44" width="370" y="0" x="0" type="Bitmap" text="!(loc.BrowseDlgBannerBitmap)" tabskip="no" />
<control id="BannerLine" height="2" width="370" y="44" x="0" type="Line" />
<control id="BottomLine" height="2" width="370" y="234" x="0" type="Line" />
<control id="Description" height="15" width="280" y="23" x="25" type="Text" text="!(loc.SC_SqlServerDlgDescription)" noprefix="yes" transparent="yes" />
<control id="Title" height="15" width="200" y="6" x="15" type="Text" text="!(loc.SC_SqlServerDlgTitle)" noprefix="yes" transparent="yes" />
<control id="label1" height="16" width="162" y="54" x="208" type="Text" text="!(loc.SC_SqlServerDatabaseName)" />
<control id="textBox1" height="15" width="152" y="72" x="208" type="Edit" property="SQLSERVERDATABASENAME" />
<control id="label5" height="16" width="243" y="217" x="21" type="Text" text="!(loc.SC_SqlServerNotes)" />
<control id="label4" height="16" width="162" y="176" x="208" type="Text" text="!(loc.SC_SqlServerPassword)" />
<control id="editServerUser" height="15" width="174" y="194" x="20" type="Edit" property="SQLSERVERUSERNAME">
<condition action="disable">
SQLSERVERAUTHENTIFICATIONMODE = 1
</condition>
<condition action="enable">
SQLSERVERAUTHENTIFICATIONMODE = 2
</condition>
</control>
<control id="textBox2" height="15" width="152" y="194" x="208" type="Edit" property="SQLSERVERPASSWORD"><br />
 <condition action="disable">
SQLSERVERAUTHENTIFICATIONMODE = 1
</condition>
<condition action="enable">
SQLSERVERAUTHENTIFICATIONMODE = 2
</condition>
</control>
<control id="label3" height="16" width="174" y="176" x="20" type="Text" text="!(loc.SC_SqlServerUserName)" />
<control id="btnCheckConnection" height="17" width="56" y="213" x="304" type="PushButton" text="!(loc.SC_SqlServerConnectionTest)">
 
<publish value="CheckConnection" event="SpawnDialog" order="2">1</publish>
 
<publish value="CheckSqlConnection" event="DoAction" order="1">1</publish>
</control>
<control id="label2" height="16" width="194" y="112" x="20" type="Text" text="!(loc.SC_SqlServerAuthentificationMode)" />
</dialog>
<dialog id="CheckConnection" title="!(loc.SC_DefaultDlgTitle)" nominimize="yes" height="85" width="260"><br />
 <control id="Yes" height="17" width="56" y="57" x="72" type="PushButton" text="!(loc.WixUIOK)">
 
<publish value="Return" event="EndDialog">1</publish>
</control><br />
 <control id="TextFailed" height="30" width="194" y="15" x="48" type="Text" text="!(loc.SC_SqlServerConnectionTestFailed)">
<condition action="show">
SQLCONNECTIONTESTRESULT = 0
</condition>
<condition action="hide">
<![CDATA[SQLCONNECTIONTESTRESULT <>0]]&gt;
</condition>
</control>
<control id="TextSuccess" height="30" width="194" y="15" x="48" type="Text" text="!(loc.SC_SqlServerConnectionTestSucceeded)">
<condition action="show">
SQLCONNECTIONTESTRESULT = 1
</condition>
<condition action="hide">
<![CDATA[SQLCONNECTIONTESTRESULT <>1]]&gt;
</condition>
</control>
<control id="Icon" ><br height="24" width="24" y="15" x="15" type="Icon" />ToolTip=&quot;Information icon&quot; FixedSize=&quot;yes&quot; IconSize=&quot;32&quot; Text=&quot;InfoIcon&quot; /&gt;<br />
 </dialog>

Which looks like this:

blogsqlsetupscreen.png

There is still a little problem with it: since the custom action runs on the UI thread the installation becomes unresponsive during the check. This is pretty bad since the connection test can take quite a while (when not finding the server on the network for example). I didn’t have the time (and need) to do it yet, but the best way to avoid the unresponsiveness is to take care of it in the custom action itself: creating a "I’m busy" dialog before doing the connection test. There seems to be no way to do it within MSI itself.

generate create assembly from <binary bits> script

07-Oct-07

Just a quick’n'dirty powershell script to generate the t-sql create assemby statement for creating a clr assembly. Converts the dll to the corresponding <assembly_bits> binary values like:

create assembly [TestAssembly] from 0×4D5A90 [...]

Useful when you just want to have a sql script and don’t want to bother copying or otherwise making the dll accessible to the database server.
I use it for building in a makefile target.

param
(
$out = 'createAssembly.txt',
$assemblyFile = 'TheDll.dll',
$assemblyName = 'TestAssembly'
)
 
$stringBuilder = New-Object -Type System.Text.StringBuilder
$stringBuilder.Append("create assembly [") > $null
$stringBuilder.Append($assemblyName) > $null
$stringBuilder.Append("] from `n0x") > $null
 
$assemblyFile = resolve-path $assemblyFile
 
$fileStream = [IO.File]::OpenRead($assemblyFile)
 
while (($byte = $fileStream.ReadByte()) -gt -1) {
$stringBuilder.Append($byte.ToString("X2")) > $null
}
$stringBuilder.Append("`nwith permission_set = SAFE")
 
$stringBuilder.ToString() > $out;
$fileStream.Close()
$fileStream.Dispose()