<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Unmanaged Things &#187; C++/ATL</title>
	<atom:link href="http://blog.speedproject.de/category/visual-studio/catl/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.speedproject.de</link>
	<description>Mehr .core als .nett</description>
	<lastBuildDate>Tue, 27 Jul 2010 10:30:34 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>VS 2008: C/C++-Laufzeit selbst kompilieren</title>
		<link>http://blog.speedproject.de/2007/11/30/vs-2008-cc-laufzeit-selbst-kompilieren/</link>
		<comments>http://blog.speedproject.de/2007/11/30/vs-2008-cc-laufzeit-selbst-kompilieren/#comments</comments>
		<pubDate>Fri, 30 Nov 2007 10:19:25 +0000</pubDate>
		<dc:creator>Sven</dc:creator>
				<category><![CDATA[C++/ATL]]></category>
		<category><![CDATA[Visual Studio 2008]]></category>

		<guid isPermaLink="false">http://blog.speedproject.de/2007/11/30/vs-2008-cc-laufzeit-selbst-kompilieren/</guid>
		<description><![CDATA[Wer möchte, dass sich seine Anwendungen ohne Installation von einem USB-Stick starten lassen, der hat ab Visual Studio 2005 schlechte Karten. Bis einschließlich Visual Studio .NET 2003 konnte man die benötigten C/C++-Laufzeitbibliotheken sowie die MFC einfach so dazupacken, ab Visual Studio 2005 beharren diese Module nun auf einer separaten Installation in das WinSxS-Verzeichnis. Es gibt zwar auch [...]]]></description>
			<content:encoded><![CDATA[<p>Wer möchte, dass sich seine Anwendungen ohne Installation von einem USB-Stick starten lassen, der hat ab Visual Studio 2005 schlechte Karten. Bis einschließlich Visual Studio .NET 2003 konnte man die benötigten C/C++-Laufzeitbibliotheken sowie die MFC einfach so dazupacken, ab Visual Studio 2005 beharren diese Module nun auf einer separaten Installation in das WinSxS-Verzeichnis. Es gibt zwar auch die Möglichkeit, <a href="http://msdn2.microsoft.com/en-us/library/ms235291(VS.80).aspx">anwendungslokale Manifeste</a> zu verwenden, aber das ist mir ehrlich gesagt viel zu umständlich. Die beste und vor allem stressfreiste Lösung für einen nativen Entwickler sind immer noch zwei Dlls (oder drei bei Verwendung der STL), die man einfach in das Anwendungsverzeichnis kopiert.</p>
<p>Der Aufwand dafür ist gar nicht so groß. Man muss lediglich die C/C++-Laufzeit sowie die MFC ohne Manifest kompilieren und hat dann alle Möglichkeiten, die man vor Visual Studio 2005 auch hatte. Man muss aber darauf achten, dass man keine Bibliotheken verwendet, die man nur als Binärversionen zur Verfügung hat und die bereits ein Manifest enthalten. Viele Entwickler schrecken aber vor dem Kompilieren zurück, daher möchte ich euch an dieser Stelle eine Schritt-für-Schritt-Anleitung aufzeigen. In diesem Artikel geht es erst einmal um die C/C++-Laufzeit, die MFC nehmen wir uns das nächste Mal vor.</p>
<p>Alle Änderungen spielen sich unterhalb des VC-Verzeichnisses ab, daher sind alle folgenden Pfadangaben relativ zum VC-Verzeichnis (VisualStudioInstallDir\VC).</p>
<p><strong>1. Sicherung</strong></p>
<p>Der erste Schritt ist die Sicherung der originalen Dateien. Bei mir hat es sich bewährt, im VC-Verzeichnis zwei neue Verzeichnisse <strong>!orig</strong> und <strong>!changed</strong> anzulegen. In das <strong>!orig</strong>-Verzeichnis werden zuerst die Verzeichnisse</p>
<ul>
<li>atlmfc</li>
<li>crt</li>
<li>include</li>
<li>lib</li>
</ul>
<p>kopiert, damit wir uns ungestört an den anderen Dateien vergehen können. So erhalten wir eine originale und eine angepasste Konfiguration, die sich dann schnell durch das Verschieben der vier Ordner austauschen lassen.</p>
<p><strong>2. Anpassen der Dateien</strong></p>
<p>Für eine manifestlose Laufzeit müssen wir erst einmal die Manifestdefinitionen entsorgen. Diese befinden sich in den Dateien <strong>crtdefs.h</strong> und <strong>use_ansi.h</strong>. Beide Dateien kommen doppelt vor, einmal in <strong>include</strong> und einmal in <strong>crt\src</strong>. In <strong>crt\src\crtdefs.h</strong> suchen wir die Zeile 118</p>
<blockquote><p>#include &lt;crtassem.h&gt;</p></blockquote>
<p>und kommentieren danach bis einschließlich Zeile 173 alles aus. Bitte unbedingt auf die einzelnen #endif-Kommentare achten, die ein einfaches /* */ erschweren. Bei <strong>include\crtdefs.h</strong> erfolgt das gleiche noch einmal, hier zwischen den Zeilen 93 und 147. Analog erfolgen die Änderungen auch in <strong>crt\use_ansi.h</strong> (Zeilen 59 bis 113) und <strong>include\use_ansi.h</strong> (Zeilen 53 bis 107).</p>
<p><strong>3. Vorbereitung der .DEF-Dateien</strong></p>
<p>Die .def-Dateien für die nötigen Exporte sind bereits im <strong>crt\src</strong>-Verzeichnis vorhanden, allerdings mit etwas kryptischen Namen. Wir benennen die Dateien wie folgt um:</p>
<blockquote><p>sample_m.def -&gt; msvcmrt.def<br />
sample_p.def -&gt; msvcprt.def<br />
sample_u.def -&gt; msvcurt.def<br />
sampld_m.def -&gt; msvcmrtd.def<br />
sampld_p.def -&gt; msvcprtd.def<br />
sampld_u.def -&gt; msvcurtd.def</p></blockquote>
<p>In den Verzeichnissen <strong>crt\src\intel</strong> und <strong>crt\src\amd64</strong> befinden sich jeweils zwei weitere Dateien, die auch umbenannt werden müssen:</p>
<blockquote><p>_sample_.def -&gt; msvcrt.def<br />
_sampld_.def -&gt; msvcrtd.def</p></blockquote>
<p>Nun müssen wir uns vernünftigen Namen für die Laufzeit-Dlls ausdenken. Ganz wichtig ist, dass wir hier einen eigenen Namen wählen, um Komplikationen mit den originalen (MsVcr90.dll) sowie anderen angepasste Dateien zu vermeiden. Als Beispiel verwenden wir <strong>MyVcr90.dll</strong>, die Modulnamen in den Definitionsdateien werden nun mit einem Texteditor wie folgt angepasst:</p>
<blockquote><p>msvcmrt.def: MyVcm90<br />
msvcmrtd.def: MyVcm90d<br />
msvcprt.def: MyVcp90<br />
msvcprtd.def: MyVcp90d<br />
msvcurt.def: MyVcm90<br />
msvcurtd.def: MyVcm90d</p></blockquote>
<p>Nun noch die vier Dateien aus den <strong>intel\amd64</strong>-Verzeichnissen:</p>
<blockquote><p>msvcrt.def: MyVcr90<br />
msvcrtd.def: MyVcr90d</p></blockquote>
<p>Damit das Kompilieren der MFC später nicht mit einem Fehler beendet wird, ergänzen wir die beiden <strong>msvcrtd.def</strong>-Dateien noch um diese vier Zeilen:</p>
<blockquote><p>_strdup_dbg<br />
_fullpath_dbg<br />
_wcsdup_dbg<br />
_wfullpath_dbg</p></blockquote>
<p><strong>MyVcr90.dll</strong> steht hier aber wirklich nur als Platzhalter, ihr solltet das nach dem ersten funktionierenden Testlauf durch euren Wunschnamen ersetzen.</p>
<p><strong>4. Vorbereitung der Ressourcendateien</strong></p>
<p>Nun müssen noch die Ressourcendateien in <strong>crt\src</strong> umbenannt</p>
<blockquote><p>_sample_.rc -&gt; MyVcr90.rc<br />
sample_m.rc -&gt; MyVcm90.rc<br />
sample_p.rc -&gt; MyVcp90.rc</p></blockquote>
<p>und die Versionsinfos in diesen drei Dateien angepasst werden.</p>
<p><strong>5. Anpassung des Makefiles</strong></p>
<p>Jetzt fehlt eigentlich nur noch die Anpassung des Makefiles an unsere Modulnamen. Dazu öffnen wir die Datei <strong>crt\src\makefile</strong> mit einem Texteditor und ändern die Modulnamen wie folgt:</p>
<blockquote><p>RETAIL_DLL_NAME=MyVcr90<br />
RETAIL_LIB_NAME=msvcrt<br />
RETAIL_DLLCPP_NAME=MyVcp90<br />
RETAIL_LIBCPP_NAME=msvcprt<br />
RETAIL_DLLMIXED_NAME=MyVcm90<br />
RETAIL_LIBMIXED_NAME=msvcmrt<br />
RETAIL_LIBPURE_NAME=msvcurt<br />
RETAIL_PT_LIBMIXED_NAME=ptrustm<br />
RETAIL_PT_LIBPURE_NAME=ptrustu<br />
DEBUG_DLL_NAME=MyVcr90d<br />
DEBUG_LIB_NAME=msvcrtd<br />
DEBUG_DLLCPP_NAME=MyVcp90d<br />
DEBUG_LIBCPP_NAME=msvcprtd<br />
DEBUG_DLLMIXED_NAME=MyVcm90d<br />
DEBUG_LIBMIXED_NAME=msvcmrtd<br />
DEBUG_LIBPURE_NAME=msvcurtd<br />
DEBUG_PT_LIBMIXED_NAME=ptrustmd<br />
DEBUG_PT_LIBPURE_NAME=ptrustud<br />
RC_NAME=MyVcr90<br />
RCCPP_NAME=MyVcp90<br />
RCMIXED_NAME=MyVcm90</p></blockquote>
<p>In der Datei <strong>crt\src\makefile.sub</strong> ersetzen wir in den Zeilen 106, 111 und 116 die Option <strong>-Wp64</strong> durch <strong>-MP</strong>. <strong>-Wp64</strong> wird seit Visual Studio 2008 nicht mehr unterstützt, bei aktiviertem Schalter wird das Kompilieren der CRT sonst abgebrochen. <strong>-MP</strong> sorgt bei Multicore-Prozessoren dafür, dass das Kompilieren wesentlich flotter abläuft.</p>
<p><strong>5. Kompilieren</strong></p>
<p>Jetzt sind endlich fast alle Vorbereitungen abgeschlossen und wir nähern uns langsam dem Kompilieren. Vorher müssen wir aber noch dafür sorgen, dass der Compiler auch alle Dateien findet. Dazu erstellen wir uns in <strong>crt\src</strong> zwei Batchdateien. Zuerst <strong>vcvars_x86.bat</strong> mit:</p>
<blockquote><p>@ECHO OFF<br />
@SET PSDKINC=E:\Visual Studio.9\VC\PlatformSDK\include<br />
@SET PSDKLIB=E:\Visual Studio.9\VC\PlatformSDK\lib<br />
@SET PSDKBIN=E:\Visual Studio.9\VC\PlatformSDK\bin<br />
@SET VSCOMMON=E:\Visual Studio.9\Common7<br />
@SET VCTOOLS=E:\Visual Studio.9\VC<br />
@SET VCTOOLSINC=$(PSDKINC)<br />
@SET LLP64=0</p>
<p>@echo Setting environment for using Microsoft Visual Studio 2008 x86 tools.</p>
<p>@set PATH=%VCTOOLS%\BIN;%VCTOOLS%\BIN;%PSDKBIN%;%VSCOMMON%\IDE;%PATH%;<br />
@set INCLUDE=%VCTOOLS%\ATLMFC\INCLUDE;%VCTOOLS%\INCLUDE;%PSDKINC%;%INCLUDE%<br />
@set LIB=%VCTOOLS%\ATLMFC\LIB;%VCTOOLS%\LIB;%PSDKLIB%;%LIB%</p></blockquote>
<p>und anschließend <strong>vcvars_amd64.bat</strong> mit:</p>
<blockquote><p>@ECHO OFF<br />
@SET PSDKINC=E:\Visual Studio.9\VC\PlatformSDK\include<br />
@SET PSDKLIB=E:\Visual Studio.9\VC\PlatformSDK\lib<br />
@SET PSDKBIN=E:\Visual Studio.9\VC\PlatformSDK\bin<br />
@SET VSCOMMON=E:\Visual Studio.9\Common7<br />
@SET VCTOOLS=E:\Visual Studio.9\VC<br />
@SET VCTOOLSINC=$(PSDKINC)<br />
@SET LLP64=2</p>
<p>@echo Setting environment for using Microsoft Visual Studio 2008 x64 cross tools.</p>
<p>@set PATH=%VCTOOLS%\BIN\x86_amd64;%VCTOOLS%\BIN;%PSDKBIN%;%VSCOMMON%\IDE;%PATH%;<br />
@set INCLUDE=%VCTOOLS%\ATLMFC\INCLUDE;%VCTOOLS%\INCLUDE;%PSDKINC%;%INCLUDE%<br />
@set LIB=%VCTOOLS%\ATLMFC\LIB\amd64;%VCTOOLS%\LIB\amd64;%PSDKLIB%\x64;%LIB%</p></blockquote>
<p>Die Pfade der Umgebungsvariablen müsst ihr an euer System anpassen. Der Übersichtlichkeit halber habe ich die Dateien aus dem Windows-SDK wie bei Visual Studio 2005 in das Unterverzeichnis <strong>PlatformSDK</strong> kopiert. Bei Visual Studio 2008 erfolgt die Installation der SDK-Headerdateien und Bibliotheksdateien normalerweise nach <strong>C:\Program Files\Microsoft SDKs\Windows\v6.0A</strong>.</p>
<p>Das war es auch schon mit den Vorbereitungen. Nun öffnen wir eine Eingabeaufforderung, wechseln in das Verzeichnis <strong>crt\src</strong> und geben zum Kompilieren der x86-Version</p>
<blockquote><p>vcvars_x86.bat<br />
nmake</p></blockquote>
<p>und für die x64-Version</p>
<blockquote><p>vcvars_amd64.bat<br />
nmake</p></blockquote>
<p>ein. Danach wird der Rechner jeweils ein wenig beschäftigt sein und die Laufzeitbibliotheken erstellen. Etwaige Warnungen können ignoriert werden, nur Fehler sollten keine auftreten. Wenn sich der Befehlsprompt wieder meldet, dann ist das Gröbste erledigt.</p>
<p><strong>6. Kopieren der Lib-Dateien</strong></p>
<p>Der letzte Schritt ist das Kopieren der erstellten Importbibliotheken aus den Verzeichnissen <strong>crt\src\build\intel</strong> bzw. <strong>crt\src\build\amd64</strong> in das <strong>lib</strong>-bzw. <strong>lib\amd64</strong>-Verzeichnis. Da wir uns nur für die dynamischen Bibliotheken interessieren, reichen diese vollkommen aus:</p>
<blockquote><p>msvcrt.lib<br />
msvcrtd.lib<br />
msvcprt.lib<br />
msvcprtd.lib</p></blockquote>
<p>In den Buildverzeichnissen finden wir auch die angepassten Dlls <strong>MyVcr90(d).dll</strong> für die C-Laufzeit sowie <strong>MyVcp90(d).dll</strong> für die C++-Laufzeit (STL). Die ebenfalls erstellten <strong>MyVcm(d).dll</strong> enthalten die Laufzeit für verwalteten Code. Die hier vorgestellte Vorgehensweise <strong>ist aber wirklich nur für rein nativen Code gedacht</strong>, daher können wir die Vcm-Dlls links liegen lassen.</p>
<p>Das war&#8217;s auch schon, mit der MFC geht es dann morgen weiter. Im Vergleich mit der C/C++-Laufzeit wird das aber eher ein Kindergeburtstag.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.speedproject.de/2007/11/30/vs-2008-cc-laufzeit-selbst-kompilieren/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Falsch multipliziert</title>
		<link>http://blog.speedproject.de/2005/08/12/falsch-multipliziert/</link>
		<comments>http://blog.speedproject.de/2005/08/12/falsch-multipliziert/#comments</comments>
		<pubDate>Fri, 12 Aug 2005 21:57:15 +0000</pubDate>
		<dc:creator>Sven</dc:creator>
				<category><![CDATA[C++/ATL]]></category>

		<guid isPermaLink="false">/?p=41</guid>
		<description><![CDATA[Böser Bug in der Funktion AtlMultiply.]]></description>
			<content:encoded><![CDATA[<p>Beim Debuggen von SpeedCommander unter x64 bin ich auf einen unschönen Bug in der ATL vom Visual Studio 2005 gestoßen. Während des Einlesens der Symbolleisten-Einstellungen wurde eine Exception geworfen und SpeedCommander stürzte ab. Nach dem Durchkämpfen bis zum Auslöser der Exception war der Fehler auch recht schnell sichtbar.</p>
<p>Die Exception wurde nach der Funktion <strong>AtlMultiplyThrow</strong> ausgelöst. <strong>AtlMultiplyThrow</strong> wird beim Reservieren eines Speicherbereichs aufgerufen und dient dazu, die Größe des zu reservierenden Speicherbereichs aus einer Anzahl von Elementen und deren Blockgröße zu ermitteln. Der Parameter <strong>tLeft</strong> gibt in diesem Fall die Anzahl der zu reservierenden Elemente und der Parameter <strong>tRight</strong> die Blockgröße an:</p>
<pre class="brush: cpp;">
template &lt;typename T&gt;
inline T AtlMultiplyThrow(T tLeft, T tRight)
{
    T tResult;
    HRESULT hr=AtlMultiply(&amp;tResult, tLeft, tRight);
    if(FAILED(hr))
    {
        AtlThrow(hr);
    }
    return tResult;
}
</pre>
<p><strong>AtlMultiplyThrow</strong> ruft lediglich <strong>AtlMultiply</strong> auf, im Fehlerfall wird eine Exception ausgelöst:</p>
<pre class="brush: cpp;">
/* generic but compariatively slow version */
template&lt;typename T&gt;
inline HRESULT AtlMultiply(T* ptResult,	T tLeft, T tRight)
{
    /* avoid divide 0 */
    if(tLeft==0)
    {
        return 0;
    }
    if(::ATL::AtlLimits&lt;T&gt;::_Max/tLeft &lt; tRight)
    {
        return E_INVALIDARG;
    }
    *ptResult= tLeft * tRight;
    return S_OK;
}
</pre>
<p>Der Fehler tritt nur auf, wenn der Wert <strong>tLeft</strong> Null ist. In diesem Fall gibt <strong>AtlMultiply</strong> 0 zurück, was auch der Wert für S_OK ist. <strong>AtlMultiplyThrow</strong> wird erfolgreich verlassen und gibt den Wert von <strong>tResult</strong> zurück.  Wenn man sich die beiden Funktionen nun etwas genauer anschaut, dann entdeckt man, dass der Inhalt von <strong>tResult</strong> undefiniert ist. In der Debugversion werden alle Werte automatisch mit 0xCCCCCCCC initialisiert, somit gibt <strong>AtlMultiplyThrow</strong> hier 3435973836 zurück.</p>
<p>Die Allokation des Speichers erfolgt beim Einlesen der Einstellungen, genauer gesagt wenn ein String geladen wird. In der entsprechenden MFC-Funktion wird Speicher entsprechend der Zeichen im String reserviert. Für einen leeren String werden 0 Bytes reserviert, durch den Bug in der Berechnung soll Windows allerdings knapp 3.5 GB bereitstellen. Das kann natürlich nicht gut gehen.</p>
<p>Die Korrektur ist sehr einfach, in der Funktion <strong>AtlMultiply</strong> muss lediglich die Variable <strong>ptResult</strong> genullt werden, wenn <strong>tLeft</strong> den Wert 0 enthält:</p>
<pre class="brush: cpp;">
/* generic but compariatively slow version */
template&lt;typename T&gt;
inline HRESULT AtlMultiply(T* ptResult,	T tLeft, T tRight)
{
    /* avoid divide 0 */
    if(tLeft==0)
    {
        *ptResult = 0;
        return S_OK;
    }
    if(::ATL::AtlLimits&lt;T&gt;::_Max/tLeft &lt; tRight)
    {
        return E_INVALIDARG;
    }
    *ptResult= tLeft * tRight;
    return S_OK;
}
</pre>
<p>Abschließend muss die MFC zwingend neu kompiliert werden, da der Fix ansonsten nur für eigene Anwendungen gilt und nicht für Funktionen innerhalb der MFC.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.speedproject.de/2005/08/12/falsch-multipliziert/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
