Linkerwarnungen beim Erstellen verwalteter Erweiterungen für C++-DLL-Projekte

SPRACHE AUSWÄHLEN SPRACHE AUSWÄHLEN
Artikel-ID: 814472 - Produkte anzeigen, auf die sich dieser Artikel bezieht
Dieser Artikel ist eine Übersetzung des folgenden englischsprachigen Artikels der Microsoft Knowledge Base:
814472 You receive linker warnings when you build Managed Extensions for C++ DLL projects
Bitte beachten Sie: Bei diesem Artikel handelt es sich um eine Übersetzung aus dem Englischen. Es ist möglich, dass nachträgliche Änderungen bzw. Ergänzungen im englischen Originalartikel in dieser Übersetzung nicht berücksichtigt sind. Die in diesem Artikel enthaltenen Informationen basieren auf der/den englischsprachigen Produktversion(en). Die Richtigkeit dieser Informationen in Zusammenhang mit anderssprachigen Produktversionen wurde im Rahmen dieser Übersetzung nicht getestet. Microsoft stellt diese Informationen ohne Gewähr für Richtigkeit bzw. Funktionalität zur Verfügung und übernimmt auch keine Gewährleistung bezüglich der Vollständigkeit oder Richtigkeit der Übersetzung.
Alles erweitern | Alles schließen

Auf dieser Seite

Problembeschreibung

Beim Kompilieren oder Verknüpfen kann eine der folgenden Fehlermeldungen angezeigt werden:
Linkertoolsfehler LNK2001 (Linker Tools Error LNK2001)
'Nicht aufgelöstes externes Symbol "Symbol" '

Linkertoolswarnung LNK4210 (Linker Tools Warning LNK4210)
' .CRT-Abschnitt vorhanden; es sind möglicherweise unbehandelte statische Initialisierer oder Terminatoren vorhanden' Beim Erstellen verwalteter Erweiterungen für C++-DLL-Projekte werden Linkerwarnungen angezeigt (You receive Linker Warnings when you build Managed Extensions for C++ DLL projects)

Linkertoolswarnung LNK4243 (Linker Tools Warning LNK4243)
'DLL, die mit /clr kompilierte Objekte enthält, ist nicht mit /NOENTRY verknüpft; Abbild wird nicht korrekt ausgeführt'.
Diese Warnungen können unter den folgenden Umständen angezeigt werden:
  • Wenn Sie verknüpfende Objekte mit der Befehlszeilenoption /clr kompilieren.
  • Wenn Sie eines der folgenden Projekte erstellen: ASP.NET-Webdienstvorlage, Klassenbibliotheksvorlage oder Windows-Steuerelementbibliotheksvorlage.
  • Wenn Sie Code hinzugefügt haben, der globale Variablen oder systemeigene Klassen (also nicht __gc oder __value) mit statischen Datenelementen verwendet. Zum Beispiel die Klassen "ActiveX Template Library" (ATL), "Microsoft Foundation Classes" (MFC) und "C Run-Time" (CRT).
Hinweis: Die Fehler LNK2001 und LNK4210 können auch für Projekte angezeigt werden, die nicht von dem in diesem Artikel beschriebenen Problem betroffen sind. Das Projekt ist jedoch definitiv von dem in diesem Artikel beschriebenen Problem betroffen, falls das Auflösen der Warnungen LNK2001 oder LNK4210 oder das Verknüpfen des Projekts zu einer LNK4243-Warnung führen.

Ursache

Die folgenden Projekte werden standardmäßig als Dynamic Link Library (DLL) ohne jegliche Verknüpfung zu systemeigenen Bibliotheken (wie CRT, ATL oder MFC) sowie ohne globale Variablen oder systemeigene Klassen mit statischen Datenelementen erstellt:
  • ASP.NET-Webdienstvorlage
  • Klassenbibliotheksvorlage
  • Windows-Steuerelementbibliotheksvorlage
Wenn Sie Code hinzufügen, der globale Variablen oder systemeigene Klassen mit statischen Datenelementen verwendet (die Bibliotheken ATL, MFC und CRT verwenden beispielsweise globale Variablen), werden Ihnen beim Kompilieren Linkerfehlermeldungen angezeigt. In diesem Fall müssen Sie Code hinzufügen, um die statischen Variablen manuell zu initialisieren. Weitere Informationen zur Vorgehensweise finden Sie im Abschnitt "Lösung" in diesem Artikel.

Aus Gründen einer besseren Lesbarkeit werden in diesem Artikel von jetzt an globale Variablen und statische Datenelemente als "statische Variablen" bezeichnet.

Dieses Problem tritt beim Laden gemischter DLLs auf. Bei gemischten DLLs (also DLLs, in denen es sowohl verwalteten als auch systemeigenen Code gibt) kann es unter bestimmten Umständen zu einem Deadlock kommen, wenn sie in den Adressbereich des Prozesses geladen werden (besonders bei einer hohen Belastung des Systems). Die zuvor erwähnten Linkerfehlermeldungen wurden im Linker aktiviert, um sicherzustellen, dass sich die Kunden des Risikos für einen Deadlock bewusst sind und dass ihnen die in diesem Dokument beschriebenen Workarounds zur Kenntnis gebracht werden. Eine detaillierte Beschreibung des Problems beim Laden gemischter DLLs finden Sie im folgenden Whitepaper:
Mixed DLL Loading Problem (Problem beim Laden gemischter DLLs)
http://msdn2.microsoft.com/en-us/library/aa290048(vs.71).aspx

Lösung

Verwaltete Erweiterungen für C++-Projekte, die als DLLs erstellt werden, nehmen standardmäßig keine Verknüpfung zu systemeigenen C/C++-Bibliotheken vor, wie C Run-Time (CRT), ATL oder MFC, und verwenden keinerlei statische Variablen. Außerdem legen die Projekteinstellungen fest, dass die DLLs verknüpft werden sollten, wenn die Option /NOENTRY aktiviert ist.

Dies ist deshalb der Fall, weil eine Verknüpfung mit einem Einstiegspunkt dazu führt, dass verwalteter Code während DllMain ausgeführt wird, was nicht sicher ist (siehe DllMain für die begrenzte Menge an Aktionen, die während der Ausführung von DllMain ausgeführt werden dürfen).

Eine DLL ohne Einstiegspunkt kann keine statischen Variablen initialisieren (mit Ausnahme sehr einfacher Typen, z. B. ganze Zahlen). In einer /NOENTRY-DLL haben Sie in der Regel keine statischen Variablen.

Die Bibliotheken ATL, MFC und CRT sind jedoch sämtlich von statischen Variablen abhängig, so dass Sie sie nicht aus diesen DLLs heraus verwenden können, ohne zunächst gewisse Änderungen vorzunehmen.

Falls Ihre gemischte DLL statische Variablen oder Bibliotheken verwenden muss, die von statischen Variablen abhängig sind (wie ATL, MFC oder CRT), müssen Sie Ihre DLL so modifizieren, dass statische Variablen manuell initialisiert werden.

Der erste Schritt zu einer manuellen Initialisierung besteht darin, sicherzustellen, dass der automatische Initialisierungscode deaktiviert wird, der in Verbindung mit gemischten DLLs nicht sicher ist und einen Deadlock verursachen kann. Gehen Sie folgendermaßen vor, um den Initialisierungscode zu deaktivieren:

Entfernen des Einstiegspunkts der verwalteten DLL

  1. Erstellen Sie eine Verknüpfung mit /NOENTRY. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektknoten, und klicken Sie dann auf Eigenschaften. Klicken Sie im Dialogfeld Eigenschaftenseiten auf Linker, klicken Sie auf Befehlszeile, und fügen Sie diese Befehlszeilenoption dann dem Feld Zusätzliche Optionen hinzu.
  2. Verknüpfen Sie msvcrt.lib. Klicken Sie im Dialogfeld Eigenschaftenseiten auf Linker, klicken Sie auf Eingabe (Input), und fügen Sie dann msvcrt.lib der Eigenschaft Additional Dependencies hinzu.
  3. Entfernen Sie nochkclr.obj. Auf der Seite Eingabe (Input) (die gleiche Seite wie im vorherigen Schritt) entfernen Sie nochkclr.obj aus der Eigenschaft Additional Dependencies.
  4. Erstellen Sie eine Verknüpfung mit der CRT. Auf der Seite Eingabe (Input) (die gleiche Seite wie im vorherigen Schritt) fügen Sie __DllMainCRTStartup@12 zu der Eigenschaft Symbolverweise erzwingen hinzu.

    Falls Sie die Eingabeaufforderung verwenden, geben Sie die vorstehend beschriebenen Projekteinstellungen wie folgt ein:
    LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12

Modifizieren Sie die Komponenten, die die DLL für die manuelle Initialisierung verwenden

Nachdem Sie den expliziten Einstiegspunkt entfernt haben, müssen Sie die Komponenten entfernen, die die DLL für die manuelle Initialisierung verwenden (je nachdem, wie Ihre DLL implementiert wurde):
  • Ihre DLL wird mit DLL-Exporten (__declspec(dllexport)) eingegeben und Ihre Consumer können keinen verwalteten Code verwenden, wenn Sie statisch oder dynamisch mit Ihrer DLL verknüpft sind.
  • Ihre DLL ist eine COM-basierte DLL.
  • Consumer Ihrer DLL können verwalteten Code verwenden, und Ihre DLL enthält entweder DLL-Exporte oder verwaltete Einstiegspunkte.

Modifizieren von DLLs, die Sie mit DLL-Exporten und Consumern eingeben, die keinen verwalteten Code verwenden können

Gehen Sie folgendermaßen vor, um DLLs zu modifizieren, die Sie mit DLL-Exporten und Consumern eingeben, die keinen verwalteten Code verwenden können:
  1. Fügen Sie Ihrer DLL wie in dem folgenden Code dargestellt zwei neu Exporte hinzu:
    // init.cpp
    
    #include <windows.h>
    #include <_vcclrit.h>
    
    // Call this function before you call anything in this DLL.
    // It is safe to call from multiple threads; it is not reference
    // counted; and is reentrancy safe.
    
    __declspec(dllexport) void __cdecl DllEnsureInit(void)
    {
    	// Do nothing else here. If you need extra initialization steps,
    	// create static objects with constructors that perform initialization.
    	__crt_dll_initialize();
    	// Do nothing else here.
    }
    
    // Call this function after this whole process is totally done
    // calling anything in this DLL. It is safe to call from multiple
    // threads; is not reference counted; and is reentrancy safe.
    // First call will terminate.
    
    __declspec(dllexport) void __cdecl DllForceTerm(void)
    {
    	// Do nothing else here. If you need extra terminate steps, 
    	// use atexit.
    	__crt_dll_terminate();
    	// Do nothing else here.
    }
    
    Hinweis: In Visual C++ 2005 müssen Sie die Compileroption "Common Language Runtime-Unterstützung" /clr:oldSyntax hinzufügen, um den vorhergehenden Beispielcode kompilieren zu können. Gehen Sie folgendermaßen vor, um die Compileroption "Common Language Runtime-Unterstützung" hinzuzufügen:
    1. Klicken Sie auf Projekt, danach auf Projektname Eigenschaften.

      Hinweis: Projektname ist ein Platzhalter für den Namen des Projekts.
    2. Erweitern Sie Konfigurationseigenschaften, und klicken Sie anschließend auf Allgemein.
    3. Klicken Sie im rechten Teilfenster der Projekteinstellungen von Common Language Runtime-Unterstützung auf Common Language Runtime Support, Old Syntax (/clr:oldSyntax).
    4. Klicken Sie auf Übernehmen und anschließend auf OK.
    Weitere Informationen zu Optionen bzgl. der "Common Language Runtime-Unterstützung" finden Sie auf folgender MSDN-Website (Microsoft Developer Network):
    http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx
    Diese Schritte beziehen sich auf den gesamten Artikel.
  2. Ihre DLL kann mehrere Consumer haben. Falls Ihre DLL mehrere Consumer hat, fügen Sie dem Exportabschnitt der .def-Datei der DLL den folgenden Code hinzu:
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    Wenn Sie diese Zeilen nicht hinzufügen und Sie zwei DLLs haben, die Funktionen exportieren, treten in der Anwendung, welche die Verknüpfung zu der DLL erstellt, Verknüpfungsfehler auf. Typischerweise haben die exportierten Funktionen identische Namen. Bei mehreren Consumern kann jeder Consumer statisch oder dynamisch mit Ihrer DLL verknüpft werden.
  3. Wird der Consumer statisch mit der DLL verknüpft bevor Sie die DLL erstmals verwenden oder bevor Sie Komponenten in Ihrer Anwendung verwenden, die von ihm abhängig sind, fügen Sie den folgenden Aufruf hinzu:
    // Snippet 1
    
    typedef void (__stdcall *pfnEnsureInit)(void);
    typedef void (__stdcall *pfnForceTerm)(void);
    
    {
    	// ... initialization code
    	HANDLE hDll=::GetModuleHandle("mydll.dll");
    	If(!hDll)
    	{
    		// Exit, return; there is nothing else to do.
    	}
    	pfnEnsureInit pfnDll=::( pfnEnsureInit) GetProcAddress(hDll, 
       "DllEnsureInit");
    	if(!pfnDll)
    	{
    		// Exit, return; there is nothing else to do.
    	}
    		pfnDll();
    		// ... more initialization code
    }
    
  4. Nach der letzten Verwendung der DLL in Ihrer Anwendung fügen Sie den folgenden Code hinzu:
    // Snippet 2
    
    {
    	// ... termination code
    	HANDLE hDll=::GetModuleHandle("mydll.dll");
    	If(!hDll)
    	{
    		// exit, return; there is nothing else to do
    	}
    	pfnForceTerm pfnDll=::( pfnForceTerm) GetProcAddress(hDll, 
       "DllForceTerm");
    	if(!pfnDll)
    	{
    		// exit, return; there is nothing else to do
    	}
    		pfnDll();
    		// ... more termination code
    }
    
  5. Falls der Consumer dynamisch mit der DLL verknüpft ist, fügen Sie wie folgt Code ein:
    • Fügen Sie den Codeabschnitt 1 (siehe Schritt 3) unmittelbar nach der ersten LoadLibrary für die DLL ein.
    • Fügen Sie den Codeabschnitt 2 (siehe Schritt 4) unmittelbar nach der letzten FreeLibrary für die DLL ein.

Modifizieren einer COM-basierten DLL

  • Modifizieren Sie die DLL-Exportfunktionen DllCanUnloadNow, DllGetClassObject, DllRegisterServer und DllUnregisterServer wie in dem folgenden Code demonstriert:
    //  Implementation of DLL Exports.
    
    #include <_vcclrit.h>
    
    STDAPI DllCanUnloadNow(void)
    {
        
        if ( _Module.GetLockCount() == 0 )
    	{
    		__crt_dll_terminate();
            return S_OK;
    	}
        else
        {
            return S_FALSE;
    
        }
    	}
    
    STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
    {
        if ( !( __crt_dll_initialize() ) )
    	{
    		return E_FAIL;
    	}
        else
        {
            return _Module.GetClassObject(rclsid, riid, ppv);
        }
    }
    
    STDAPI DllRegisterServer(void)
    {
    	if ( !( __crt_dll_initialize() ) )
    	{
    		return E_FAIL;
    	}
    	// Call your registration code here
        HRESULT hr = _Module.RegisterServer(TRUE)
     
        return hr;
    }
    
    
    STDAPI DllUnregisterServer(void)
    { 
        HRESULT hr = S_OK;
    	__crt_dll_terminate();
    
        // Call your unregistration code here
        hr = _Module.UnregisterServer(TRUE);
        return hr;
    }
    

Modifizieren von DLLs, die Consumer enthalten, die verwalteten Code und DLL-Exporte oder verwaltete Einstiegspunkte verwenden

Gehen Sie folgendermaßen vor, um DLLs zu modifizieren, die Consumer enthalten, die verwalteten Code und DLL-Exporte oder verwaltete Einstiegspunkte verwenden:
  1. Implementieren Sie eine verwaltete Klasse mit statischen Memberfunktionen für Initialisierung und Beendigung. Fügen Sie Ihrem Projekt eine .cpp-Datei hinzu, die eine verwaltete Klasse mit statischen Memberfunktionen für Initialisierung und Beendigung implementiert:
    // ManagedWrapper.cpp
    
    // This code verifies that DllMain is not automatically called 
    // by the Loader when linked with /noentry. It also checks some
    // functions that the CRT initializes.
    
    #include <windows.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    #include "_vcclrit.h"
    
    #using <mscorlib.dll>
    using namespace System;
    
    public __gc class ManagedWrapper {
    public:
    	static BOOL minitialize() {
    		BOOL retval = TRUE;
    		try {
               retval =  __crt_dll_initialize();
    		} catch(System::Exception* e) {
    			Console::WriteLine(e->Message);
    			retval = FALSE;
    		}
    		return retval;
    	}
    	static BOOL mterminate() {
    		BOOL retval = TRUE;
    		try {
                retval = __crt_dll_terminate();
    		} catch(System::Exception* e) {
    						Console::WriteLine(e->Message);
    			retval = FALSE;
    		}
    		return retval;
    	}
    };
    
    BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID 
    lpvReserved) {
    	Console::WriteLine(S"DllMain is called...");
    	return TRUE;
    } /* DllMain */
    
  2. Rufen Sie diese Funktionen auf, bevor Sie auf die DLL verweisen und nachdem deren Verwendung abgeschlossen ist. Rufen Sie die Memberfunktionen für Initialisierung und Beendigung in der Datei "Main.cpp" mit dem folgenden Code auf:
    // Main.cpp
    
    #using <mscorlib.dll>
    using namespace System;
    using namespace System::Reflection;
    #using "ijwdll.dll";
    
    int main() {
    	int retval = ManagedWrapper::minitialize();
        ManagedWrapper::mterminate();
    }
    

Benutzer von Visual C++ .NET 2002

Hinweis: Obwohl die Linker-Fehlermeldung LNK4243 in Visual C++ .NET 2002 nicht existiert, wird Benutzern von Visual C++ .NET 2002 empfohlen, bei der Entwicklung gemischter DLLs die Richtlinien zu befolgen, die zuvor in diesem Artikel beschrieben wurden.

Visual C++ .NET 2003 enthält eine zusätzliche Headerdatei, um die manuelle Initialisierung für den Benutzer einfacher zu gestalten. Damit die in diesem Artikel beschriebenen Lösungen auch in Visual C++ .NET 2002 funktionieren, müssen Sie Ihrem Projekt eine Headerdatei mit dem Namen _vcclrit.h hinzufügen, die den folgenden Text enthält:
/***
* _vcclrit.h
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
* Purpose:
*       This file defines the functions and variables used by user
*       to initialize CRT and the dll in IJW scenario.
*
****/

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

extern IMAGE_DOS_HEADER __ImageBase;

BOOL WINAPI _DllMainCRTStartup(
        HANDLE  hDllHandle,
        DWORD   dwReason,
        LPVOID  lpreserved
        );
#ifdef __cplusplus
}
#endif

#ifdef _cplusplus
#define __USE_GLOBAL_NAMESPACE  ::
#else
#define __USE_GLOBAL_NAMESPACE
#endif

// Used to lock 
__declspec( selectany ) LONG  volatile __lock_handle = 0;

// Init called
__declspec(selectany) BOOL volatile __initialized = FALSE;

// Term called
__declspec( selectany ) BOOL volatile __terminated = FALSE;

__inline BOOL WINAPI __crt_dll_initialize()
{
    // Try to make the variable names unique, so that the variables 
    // do not even clash with macros.
    static BOOL volatile (__retval) = FALSE;
    static DWORD volatile (__lockThreadId) = 0xffffffff;
    DWORD volatile (__currentThreadId) = __USE_GLOBAL_NAMESPACE(GetCurrentThreadId)();
    int (__int_var)=0;
    
    // Take Lock; this is needed for multithreaded scenario. 
    // Additionally, the threads need to wait here to make sure that the dll 
    // is initialized when they get past this function.
    while ( __USE_GLOBAL_NAMESPACE(InterlockedExchange)( &(__lock_handle), 1) == 1 )
	{
        ++(__int_var);
        if ((__lockThreadId) == (__currentThreadId)) 
        {
            return TRUE;
        }
		__USE_GLOBAL_NAMESPACE(Sleep)( (__int_var)>1000?100:0 );

        // If you hang in this loop, this implies that your 
        // dllMainCRTStartup is hung on another thread. 
        // The most likely cause of this is a hang in one of your 
        // static constructors or destructors.
	}
    // Note: you do not really need any interlocked stuff here because the 
    // writes are always in the lock. Only reads are outside the lock.
    (__lockThreadId) = (__currentThreadId);
    __try {
        if ( (__terminated) == TRUE )
        {
            (__retval) = FALSE;
        }
        else if ( (__initialized) == FALSE )
        {
            (__retval) = (_DllMainCRTStartup)( ( HINSTANCE )( &__ImageBase ), DLL_PROCESS_ATTACH, 0 );
            (__initialized) = TRUE;
        }

    } __finally {
        // revert the __lockThreadId
        (__lockThreadId) = 0xffffffff;
        // Release Lock
       __USE_GLOBAL_NAMESPACE(InterlockedExchange)(&(__lock_handle),0);
    }
    return (__retval);
}

__inline BOOL WINAPI __crt_dll_terminate()
{
    static BOOL volatile (__retval) = TRUE;
    static DWORD volatile (__lockThreadId) = 0xffffffff;
    DWORD volatile (__currentThreadId) = __USE_GLOBAL_NAMESPACE(GetCurrentThreadId)();
    int (__int_var)=0;
    
    // Take Lock; this lock is needed to keep Terminate 
    // in sync with Initialize.
    while ( __USE_GLOBAL_NAMESPACE(InterlockedExchange)( &(__lock_handle), 1) == 1 )
	{
        ++(__int_var);
        if ((__lockThreadId) == (__currentThreadId)) 
        {
            return TRUE;
        }
		__USE_GLOBAL_NAMESPACE(Sleep)( (__int_var)>1000?100:0 );

        // If you hang in this loop, this implies that your 
        // dllMainCRTStartup is hung on another thread. The most likely 
        // cause of this is a hang in one of your static constructors 
        // or destructors.
    }
    // Note: you do not really need any interlocked stuff here because the 
    // writes are always in the lock. Only reads are outside the lock.
    (__lockThreadId) = (__currentThreadId);
    __try {
        if ( (__initialized) == FALSE )
        {
            (__retval) = FALSE;
        }
        else if ( (__terminated) == FALSE )
        {
            (__retval) = _DllMainCRTStartup( ( HINSTANCE )( &(__ImageBase) ), DLL_PROCESS_DETACH, 0 );
            (__terminated) = TRUE;
        }
    } __finally {
        // revert the __lockThreadId
        (__lockThreadId) = 0xffffffff;
        // Release Lock
       __USE_GLOBAL_NAMESPACE(InterlockedExchange)(&(__lock_handle),0);
    }
    return (__retval);
}

Informationsquellen

Weitere Informationen finden Sie in folgendem Artikel der Microsoft Knowledge Base:
309694 BUG: AppDomainUnloaded-Ausnahme bei Verwendung verwaltete Erweiterungen für Visual C++-Komponenten

Eigenschaften

Artikel-ID: 814472 - Geändert am: Freitag, 11. Mai 2007 - Version: 7.2
Die Informationen in diesem Artikel beziehen sich auf:
  • Microsoft Visual C++ 2005 Express Edition
  • Microsoft Visual C++ .NET 2003 Standard Edition
  • Microsoft Visual C++ .NET 2002 Standard Edition
  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0
Keywords: 
kbdll kbprb kbijw kbcominterop kbmanaged KB814472
Microsoft stellt Ihnen die in der Knowledge Base angebotenen Artikel und Informationen als Service-Leistung zur Verfügung. Microsoft übernimmt keinerlei Gewährleistung dafür, dass die angebotenen Artikel und Informationen auch in Ihrer Einsatzumgebung die erwünschten Ergebnisse erzielen. Die Entscheidung darüber, ob und in welcher Form Sie die angebotenen Artikel und Informationen nutzen, liegt daher allein bei Ihnen. Mit Ausnahme der gesetzlichen Haftung für Vorsatz ist jede Haftung von Microsoft im Zusammenhang mit Ihrer Nutzung dieser Artikel oder Informationen ausgeschlossen.

Ihr Feedback an uns

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com