Wywoływanie funkcji macierzystych z kodu zarządzanego
Aparat plików wykonywalnych języka wspólnego zapewnia platformę wywołania usług lub funkcji PInvoke, że umożliwia zarządzany kod, aby wywoływać funkcje stylu C w macierzystym biblioteki łączone dynamicznie (dll).Te same dane organizacyjne służy co do współdziałania ze składnikami COM środowiska wykonawczego i dla mechanizmu "To po prostu działa" lub IJW.
Aby uzyskać więcej informacji, zobacz:
Przykłady w tej sekcji tylko ilustrują jak PInvoke mogą być używane.PInvokemożna uprościć, dostosowaną dane organizacyjne, ponieważ użytkownik poda informacje dotyczące organizowania deklaratywny w atrybutach zamiast pisać kod procedury dotyczące organizowania.
[!UWAGA]
Biblioteka dotyczące organizowania zapewnia alternatywnym sposobem organizowania utworzony danych między środowiskami macierzystych i zarządzanych w sposób zoptymalizowany.Zobacz Omówienie przekazywania międzyprocesowego w języku C++ Aby uzyskać więcej informacji o bibliotece dotyczące organizowania.Dotyczące organizowania stacja jest gotowa, aby tylko dane, a nie dla funkcji.
Funkcji PInvoke i atrybut DllImport
W poniższym przykładzie pokazano użycie PInvoke w programie Visual C++.Stawia macierzystego funkcja jest zdefiniowana w msvcrt.dll.Atrybut DllImport jest używany dla deklaracji stawia.
// platform_invocation_services.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
[DllImport("msvcrt", CharSet=CharSet::Ansi)]
extern "C" int puts(String ^);
int main() {
String ^ pStr = "Hello World!";
puts(pStr);
}
Poniższy przykład jest równoważne w poprzednim przykładzie, ale używa IJW.
// platform_invocation_services_2.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
#include <stdio.h>
int main() {
String ^ pStr = "Hello World!";
char* pChars = (char*)Marshal::StringToHGlobalAnsi(pStr).ToPointer();
puts(pChars);
Marshal::FreeHGlobal((IntPtr)pChars);
}
Zalety IJW
Nie ma potrzeby pisać DLLImport atrybut deklaracji w zakresie niezarządzanego interfejsów API jest używana.Po prostu dołączyć plik nagłówkowy i łącza z biblioteki import.
Mechanizm IJW jest trochę szybszy (na przykład, kategoria: IJW nie trzeba sprawdzić o nich numeru pin lub kopii elementów danych, ponieważ jawnie Sporządzono przez autora).
Wyraźnie pokazuje problemy z wydajnością.W takim przypadku fakt, że są tłumaczenia z ciągiem Unicode na ciąg ANSI i mieć obsługujący pamięci alokacji i dezalokacji.W takim przypadku autora, pisanie kodu przy użyciu IJW sobie sprawę tego połączenia _putws i za pomocą PtrToStringChars lepiej byłoby dla wydajności.
Jeśli wywołamy wielu niezarządzanego API przy użyciu tych samych danych, to kierowanie tylko raz i mijania zorganizowanym kopia jest znacznie bardziej efektywne niż re-marshaling za każdym razem.
Wady IJW
Kierowanie musi być jawnie określony w kodzie, a nie według atrybutów (które często mają odpowiednie wartości domyślne).
Dotyczące organizowania kodu jest w tekście, gdzie jest bardziej inwazyjne w przepływie logiki aplikacji.
Ponieważ jawne dotyczące organizowania interfejsów API zwraca IntPtr typów przenośności 32-bitowych do 64-bitowych, należy użyć dodatkowych ToPointer wywołań.
Określona metoda udostępniane przez C++ jest metodą bardziej efektywnego i jawnych, kosztem złożoność procesu.
Jeśli aplikacja używa głównie typy niezarządzanych danych lub jeśli wywołuje bardziej niezarządzanego API niż interfejsów API programu.NET Framework, zalecana jest funkcja IJW.Aby wywołanie okolicznościowe API niezarządzanego głównie administrowaną aplikację, do wyboru jest bardziej subtelny.
PInvoke z interfejsów API systemu Windows
PInvoke jest wygodne w przypadku wywoływanie funkcji w systemie Windows.
W tym przykładzie program Visual C++ współdziała również z funkcji MessageBox, która jest częścią interfejsu Win32 API.
// platform_invocation_services_4.cpp
// compile with: /clr /c
using namespace System;
using namespace System::Runtime::InteropServices;
typedef void* HWND;
[DllImport("user32", CharSet=CharSet::Ansi)]
extern "C" int MessageBox(HWND hWnd, String ^ pText, String ^ pCaption, unsigned int uType);
int main() {
String ^ pText = "Hello World! ";
String ^ pCaption = "PInvoke Test";
MessageBox(0, pText, pCaption, 0);
}
Dane wyjściowe są okno komunikatu, który ma tytuł PInvoke Test i zawiera tekst Hello World!.
Informacje dotyczące organizowania umożliwia również przez PInvoke wyszukiwać funkcje w bibliotece DLL.W bibliotece User32 nie ma w rzeczywistości nie ma funkcji MessageBox, ale CharSet = CharSet::Ansi, włączenie funkcji PInvoke używać MessageBoxA, w wersji ANSI, zamiast MessageBoxW, która jest wersja Unicode.Ogólnie rzecz biorąc zaleca się, aby użyć wersje Unicode niezarządzanego interfejsów API, który eliminuje translacji napowietrznych z natywnym formacie Unicode.NET Framework obiektów typu string na ANSI.
Kiedy nie należy używać funkcji PInvoke
Za pomocą funkcji PInvoke nie jest właściwe dla wszystkich funkcji stylu języka C, w bibliotekach DLL.Załóżmy na przykład, jest funkcją MakeSpecial w mylib.dll zadeklarowany następująco:
char * MakeSpecial(char * pszString);
Jeśli używamy funkcji PInvoke w aplikacji Visual C++, może być piszemy się coś podobnego do następującego:
[DllImport("mylib")]
extern "C" String * MakeSpecial([MarshalAs(UnmanagedType::LPStr)] String ^);
Trudności w tym miejscu jest, że firma Microsoft nie można usunąć pamięć dla niezarządzanego ciąg zwracany przez MakeSpecial.Inne funkcje wywoływane za pośrednictwem funkcji PInvoke zwraca wskaźnik do wewnętrznego buforu, który ma być przydziałów przez użytkownika.W takim przypadku przy użyciu funkcji IJW jest oczywistym wyborze.
Ograniczenia funkcji PInvoke
Nie może zwrócić ten sam wskaźnik dokładnie przez funkcję macierzystego, który pobierałby jako parametr.Jeśli funkcja macierzystego zwraca wskaźnik organizowanego do niego przez PInvoke, związanej z uszkodzeniem pamięci i wyjątki mogą wystąpić.
__declspec(dllexport)
char* fstringA(char* param) {
return param;
}
Poniższy przykładowy ujawnia się tego problemu, a mimo, że program może wydawać się, aby podać poprawne dane wyjściowe, dane wyjściowe pochodzi z pamięci, które zostały uwolnione.
// platform_invocation_services_5.cpp
// compile with: /clr /c
using namespace System;
using namespace System::Runtime::InteropServices;
#include <limits.h>
ref struct MyPInvokeWrap {
public:
[ DllImport("user32.dll", EntryPoint = "CharLower", CharSet = CharSet::Ansi) ]
static String^ CharLower([In, Out] String ^);
};
int main() {
String ^ strout = "AabCc";
Console::WriteLine(strout);
strout = MyPInvokeWrap::CharLower(strout);
Console::WriteLine(strout);
}
Argumenty dotyczące organizowania
Z PInvoke, kierowanie nie jest potrzebna między zarządzane i prymitywne macierzystym C++ typy z tym samym formularzu.Na przykład kierowanie nie jest wymagana, między Int32 i int lub między Podwójna precyzja i podwójne.
Jednakże musi zorganizować typów, które nie mają tego samego formularza.Dotyczy to typu char, string i struct.W poniższej tabeli przedstawiono mapowania używane przez organizatora dla różnych typów:
wtypes.h |
Visual C++ |
Visual C++ z/CLR |
Aparat plików wykonywalnych języka wspólnego |
---|---|---|---|
UCHWYT |
void * |
void * |
IntPtr, UIntPtr |
BAJT |
niepodpisany char |
niepodpisany char |
Bajt |
KRÓTKI |
short |
short |
Int16 |
PROGRAM WORD |
niepodpisany short |
niepodpisany short |
UInt16 |
INT |
int |
int |
Int32 |
UINT |
niepodpisany int |
niepodpisany int |
UInt32 |
DŁUGA |
long |
long |
Int32 |
BOOL |
long |
bool |
Wartość logiczna |
DWORD |
niepodpisany long |
niepodpisany long |
UInt32 |
ULONG |
niepodpisany long |
niepodpisany long |
UInt32 |
(ZNAK) |
char |
char |
Char |
LPCSTR |
char * |
Ciąg ^ [in], StringBuilder ^ [Miasto, out] |
Ciąg ^ [in], StringBuilder ^ [Miasto, out] |
LPCSTR |
const char * |
Ciąg ^ |
Ciąg |
LPWSTR |
wchar_t * |
Ciąg ^ [in], StringBuilder ^ [Miasto, out] |
Ciąg ^ [in], StringBuilder ^ [Miasto, out] |
LPCWSTR |
Const wchar_t * |
Ciąg ^ |
Ciąg |
PŁYWAK |
typ float |
typ float |
Pojedyncze |
PODWÓJNE |
double |
double |
Wartość dwubajtowa |
Organizator szpilki automatycznie pamięci zaalokowanego na stercie runtime, jeśli jego adres zostanie przekazany do niezarządzanego funkcji.Przypinanie uniemożliwia przenoszenie zaalokowanego bloku pamięci podczas kompaktowania garbage collector.
W przykładzie pokazano wcześniej w tym temacie parametr CharSet DllImport Określa jak zarządzanych ciągi powinny być przekazywane; w takim przypadku należy można zorganizować na ciągi znaków ANSI dla macierzystych strony.
Zawiera informacji organizacyjnych dla poszczególnych argumentów funkcji macierzystego można określić przy użyciu atrybutu MarshalAs.Istnieje kilka możliwości dla przekazywania międzyprocesowego ciąg * argument: BStr, ANSIBStr, TBStr, LPStr, LPWStr i LPTStr.Wartość domyślna to LPStr.
W tym przykładzie ciąg jest zorganizowany jako dwubajtowy ciąg znaków Unicode, LPWStr.Dane wyjściowe są litery na początku Hello World! ponieważ drugi bajt zorganizowanym ciąg ma wartość null, a stawia interpretuje to jako znacznik końca ciągu.
// platform_invocation_services_3.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
[DllImport("msvcrt", EntryPoint="puts")]
extern "C" int puts([MarshalAs(UnmanagedType::LPWStr)] String ^);
int main() {
String ^ pStr = "Hello World!";
puts(pStr);
}
Atrybut MarshalAs jest w obszarze nazw System::Runtime::InteropServices.Atrybut może służyć z innymi typami danych, takich jak tablice.
Jak wspomniano wcześniej w temacie, dotyczące organizowania Biblioteka zapewnia nową, zoptymalizowaną metodę Ci trybu przekazywania międzyprocesowego danych między macierzystych i zarządzanych środowiskach.Aby uzyskać więcej informacji, zobacz Omówienie przekazywania międzyprocesowego w języku C++.
Zagadnienia dotyczące wydajności
PInvoke ma obciążenie związane z między 10 a 30 x 86 instrukcje na wywołanie.Oprócz tego kosztu stałego przekazywania międzyprocesowego tworzy dodatkowe obciążenie.Nie ma żadnych kosztów dotyczące organizowania między typów istnieć, które mają taką samą reprezentację w kod zarządzany i niezarządzany.Na przykład istnieje żadnych kosztów tłumaczenie między int i Int32.
Aby zapewnić lepszą wydajność mają mniej wywołania funkcji PInvoke, które MARSZAŁEK tyle danych, jak to możliwe, zamiast więcej połączeń, które MARSZAŁEK mniej danych na wywołanie.