C++ DLL - WMI auslesen - Dll in C#
-
Guten morgen,
ich habe ein Problem.
Ich habe als Ansatz zum Auslesen der WMI diese Seite genommen:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa390423(v=vs.85).aspxIn einer eigenständigen Applikation läuft das soweit auch ganz gut. Nun habe ich aber eine C++ DLL daraus gebaut die dann in C# verwendet werden soll.
#define _WIN32_DCOM #include <string> #include <sstream> #include <iostream> #include <Windows.h> #include <comdef.h> #include <Wbemidl.h> using namespace std; # pragma comment(lib, "wbemuuid.lib") extern "C" __declspec(dllexport) char* CreateUID() { HRESULT hres; #pragma region COM initialisieren //hres = CoInitializeEx(NULL, COINIT_MULTITHREADED); //if (hres != S_OK) //{ // return "1"; //} #pragma endregion #pragma region COM Security initialisieren //hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); //if (hres != S_OK) //{ // CoUninitialize(); // return "2"; //} #pragma endregion #pragma region COM Instanz initialisieren IWbemLocator *pLoc = NULL; hres = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc); if (hres != S_OK) { CoUninitialize(); return "3"; } #pragma endregion #pragma region COM Connect Server initialisieren IWbemServices *pSvc = NULL; hres = pLoc->ConnectServer(_bstr_t("ROOT\\CIMV2"), NULL, NULL, NULL, NULL, NULL, NULL, &pSvc); if (hres != WBEM_S_NO_ERROR) { pLoc->Release(); CoUninitialize(); return "4"; } #pragma endregion #pragma region CoSetProxyBlanket hres = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); if (hres != S_OK) { pSvc->Release(); pLoc->Release(); CoUninitialize(); return "5"; } #pragma endregion #pragma region ExecQuery IEnumWbemClassObject* pEnumerator = NULL; hres = pSvc->ExecQuery(_bstr_t("WQL"), _bstr_t("SELECT * FROM Win32_OperatingSystem"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); CoUninitialize(); return "6"; } #pragma endregion #pragma region Werte lesen IWbemClassObject *pclsObj; ULONG uReturn = 0; stringstream ss; while (pEnumerator) { hres = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if(uReturn == 0) { break; } VARIANT vtProp; CIMTYPE vtType; hres = pclsObj->Get(L"Name", 0, &vtProp, &vtType, NULL); std::wstring ws(vtProp.bstrVal, SysStringLen(vtProp.bstrVal)); ss << ws.c_str(); ss << vtType; VariantClear(&vtProp); pclsObj->Release(); } #pragma endregion #pragma region Aufräumen pSvc->Release(); pLoc->Release(); pEnumerator->Release(); //pclsObj->Release(); //CoUninitialize(); #pragma endregion return (char*)ss.str().c_str(); }
Das Initialisieren von COM ist natürlich auskommentiert da ich schon im Kontext meiner .NET Anwendung Zugriff darauf habe.
Nun aber zu meinem Problem:
Ich kann mich drehen und wenden wie ich will. Ich schaffe es einfach nicht alle Werte in eine Zeichenkette zu pressen und dann zurück zu geben. Ich bekomme nur seltsame Zeichen:VARIANT vtProp; CIMTYPE vtType; hres = pclsObj->Get(L"Name", 0, &vtProp, &vtType, NULL); std::wstring ws(vtProp.bstrVal, SysStringLen(vtProp.bstrVal)); ss << ws.c_str(); ss << vtType;
Diese Stelle treibt mich in den Wahnsinn. Ich habe auch mal einen wstringstream genutzt. Ebenso einen stringstream usw.
Wenn ich jedoch die ursprüngliche Anwendung laufen lasse, dann sieht alles natürlich sauber aus. Ich hab irgendwie das Gefühl, dass das was mit dem Kontext zu tun hat?
-
Warum greifst Du in C# nicht direkt auf WMI zu? Es ist möglich und auch sehr einfach.
-
Weil ich dann mein Problem in ein Forum hätte posten müssen;-)
Ne im Ernst, ich weiß dass es in .NET möglich ist und mir wäre das auch lieber gewesen eben weil es so einfach dort ist. Wir haben jedoch eine Sammelsurium DLL in die dieser Kram rein soll. Und nicht zuletzt damit wir das nachher obfuscaten können(ja .NET kann das auch, kostet aber ne Menge Kohle).
Bleiben wir einfach dabei. Das ganze wird in C++ umgesetzt.
-
Stichwort DllImportAttribut. http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.dllimportattribute(v=vs.80).aspx
Du musst im DllImportAttribut den dll namen angeben und anschließend kanns schon losgehen.
Deklaration sieht so aus: [zugriff] static extern [Typ] FunktionsName(Parameter...).
Funktionsname kann auch abweichen, dann muss er aber im DllImportAttribut via EntryPoint gesetzt werden. Die Parameter sind das schwierigste. Je nach Parameter musst du schauen wie du sie marshalen musst. Für Standartdinge gibt es jedoch schon genug Marshaler und das solltest ihr hinbekommen. Falls es wirklich komische eigene Datentypen sind müsst ihr halt einen CustomMarshaler verwenden das habe ich jedoch erst einmal gebraucht.