0xC0000005: Access violation reading location 0xffffffffffffffff - c#

I have:
An unmanaged C++ app
A C++/CLI Wrapper
A C# GUI
I am seeing this crash occur only in Release and not debug. The crash also does not occur on neither debug or release when the unmanaged C++ app is run by itself.
All I can say is the crash occurs in this line of code:
if ((std::find(vstrEEDRRMachines.begin(), vstrEEDRRMachines.end(), m_sFrame.strSourceAddress) != vstrEEDRRMachines.end()
&& std::find(vstrEEDRRMachines.begin(), vstrEEDRRMachines.end(), m_sFrame.strDestAddress) != vstrEEDRRMachines.end())
|| (std::find(vstrRRRHMachines.begin(), vstrRRRHMachines.end(), m_sFrame.strSourceAddress) != vstrRRRHMachines.end()
&& std::find(vstrRRRHMachines.begin(), vstrRRRHMachines.end(), m_sFrame.strDestAddress) != vstrRRRHMachines.end()))
{
// Create appropriate buffer size for raw message (i.e. size of payload along with the extra padding
// for decoding)
m_sFrame.iMessageSize = m_sFrame.iPayloadLength;
m_sFrame.iOriginalMessageSize = m_sFrame.iPayloadLength;
m_sFrame.pszMessage = new unsigned char[m_sFrame.iPayloadLength + Constants::RSSL_DECODE_PADDING];
m_sFrame.pszOriginalMessage = new unsigned char[m_sFrame.iPayloadLength + Constants::RSSL_DECODE_PADDING];
}
Although I can't see what's inside vstrEEDRRMachines and vstrRRRHMachines (because we are in release), I can see when in the original C++/CLI wrapper that there are valid string entries in the vector.
The stack trace is as follows:
msvcr100.dll!0000000069abbdc0() [Frames below may be incorrect
and/or missing, no symbols loaded for msvcr100.dll]
DataVerifier.exe!std::_Find,std::allocator
__ptr64,std::basic_string,std::allocator
(std::basic_string,std::allocator > * _First, std::basic_string,std::allocator > *
_Last, const std::basic_string,std::allocator > &
_Val) Line 41 + 0x4a bytes C++
DataVerifier.exe!DataVerifier::CPCAPParser::Parse(const char * szFileName,
std::vector
& vSFramesRWF1Messages, std::vector,std::allocator
,std::allocator,std::allocator
vstrEEDRRMachines, std::vector,std::allocator
,std::allocator,std::allocator
vstrRRRHMachines, RsslDataDictionary & rsslDataDictionary) Line 178 + 0x19 bytes C++ [External Code]
DataVerifierLib.dll!DataVerifierLib::PCAPParserWrapper::ParseWrapper(System::String^
strInputFileNames) Line 136 + 0xf6 bytes C++
DataVerifierGUI.exe!DataVerifierGUI.Form1.button1_Click(object
sender, System.EventArgs e) Line 42 + 0x30 bytes C#
user32.dll!00007fff7a8c250d() user32.dll!00007fff7a8c2367()
System.Windows.Forms.ni.dll!00007fff535368c0()
The crash specifically occurs in c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\algorithm in this part of the code:
// TEMPLATE FUNCTION find
template<class _InIt,
class _Ty> inline
_InIt _Find(_InIt _First, _InIt _Last, const _Ty& _Val)
{ // find first matching _Val
for (; _First != _Last; ++_First)
if (*_First == _Val) <-- CRASH OCCURS HERE
break;
return (_First);
}
I have no idea what's going on, since release mode with C# + C++/CLI + C++ apps is where the crash is occurring. Is there any way I can easily fix this?
EDIT
It seems like this occurs 9 times out of 10, but i've noticed the times it does work, when I check the parameters of the Parse function just as it goes into the function (no lines are executed yet), all 3 vectors have normal values (the 1st one is 0, the second and third contain IP addresses as expected). However, most of the time, as soon as we get into the Parse function, all 3 vectors have negative size and capacities... and when they are used, everything goes boom. I'm going to past the entrance of the Parse function and also the C++/CLI wrapper before it.
bool CPCAPParser::Parse(const char* szFileName, std::vector<CRWFCapsule> &vSFramesRWF1Messages, std::vector<std::string> vstrEEDRRMachines, std::vector<std::string> vstrRRRHMachines, RsslDataDictionary &rsslDataDictionary) // change to a vector of rwf1capsules
{
This is the C++/CLI Wrapper:
// This is the main DLL file.
#include "stdafx.h"
#include "DataVerifierLib.h"
// Constructor Implementation
DataVerifierLib::PCAPParserWrapper::PCAPParserWrapper()
{
}
std::vector<std::string> DataVerifierLib::PCAPParserWrapper::CreateNewMachineCollection(std::vector<std::string> vstrNewMachines, std::vector<std::string> vstrMachine1, std::vector<std::string> vstrMachine2)
{
vstrNewMachines.reserve(vstrMachine1.size() + vstrMachine2.size()); // preallocate memory
vstrNewMachines.insert(vstrNewMachines.end(), vstrMachine1.begin(), vstrMachine1.end());
vstrNewMachines.insert(vstrNewMachines.end(), vstrMachine2.begin(), vstrMachine2.end());
return vstrNewMachines;
}
bool DataVerifierLib::PCAPParserWrapper::ParseWrapper(String^ managedString)
{
// String conversion from c# to c++
String^ managedStringTmp = managedString;
std::string strInputFileNames = msclr::interop::marshal_as<std::string>(managedStringTmp);
std::vector<std::string> vRRMachines;
std::vector<std::string> vRHMachines;
std::vector<std::string> vEEDMachines;
std::vector<std::string> vPorts; // decide on what checks are to be made. Should frame have matching src/dest ports? or just one of them.
std::vector<std::string> vEEDRRMachines;
std::vector<std::string> vRRRHMachines;
std::vector<std::string> vstrLines;
std::string strTxtFile = "ServerIPList.txt"; //argv[2]; // ServerIPList.txt
std::string strLine;
std::ifstream in(strTxtFile);
if (!in)
{
std::cout << "There was a problem opening the file." << std::endl;
std::cerr << "Error: " << strerror(errno) << std::endl;
return -1;
}
while (std::getline(in, strLine))
{
vstrLines.push_back(strLine);
}
for (int i = 0; i < vstrLines.size(); ++i)
{
if (vstrLines[i].substr(0, 2) == "rr")
{
boost::split(vRRMachines, vstrLines[i], boost::is_any_of(","));
if (vRRMachines.size() < 2)
return -1;
else
vRRMachines.erase(vRRMachines.begin());
}
else if (vstrLines[i].substr(0, 2) == "rh")
{
boost::split(vRHMachines, vstrLines[i], boost::is_any_of(","));
if (vRHMachines.size() < 2)
return -1;
else
vRHMachines.erase(vRHMachines.begin());
}
else if (vstrLines[i].substr(0, 3) == "eed")
{
boost::split(vEEDMachines, vstrLines[i], boost::is_any_of(","));
if (vEEDMachines.size() < 2)
return -1;
else
vEEDMachines.erase(vEEDMachines.begin());
}
else if (vstrLines[i].substr(0, 5) == "ports")
{
boost::split(vPorts, vstrLines[i], boost::is_any_of(","));
if (vPorts.size() < 2)
return -1;
else
vPorts.erase(vPorts.begin());
}
}
// Create a vector with EED/RR and RR/RH combined addresses
vEEDRRMachines = CreateNewMachineCollection(vEEDRRMachines, vEEDMachines, vRRMachines);
vRRRHMachines = CreateNewMachineCollection(vRRRHMachines, vRRMachines, vRHMachines);
// Initialise Rssl
RsslRet rsslRet;
RsslError rsslError;
rsslRet = rsslInitialize(RSSL_LOCK_NONE, &rsslError);
if (rsslRet != RSSL_RET_SUCCESS)
return -1;
// Initialise Field Dictionary
// To prevent memory issues, we need to use "malloc" for the data dictionary in order to load RWF.dat and enumtype.def
RsslDataDictionary *rsslDataDictionary = (RsslDataDictionary*)malloc(sizeof(RsslDataDictionary));
RsslBuffer rsslBufferError;
rsslClearDataDictionary(rsslDataDictionary);
if (rsslRet = rsslLoadFieldDictionary("RWF.DAT", rsslDataDictionary, &rsslBufferError) != RSSL_RET_SUCCESS)
{
// Ensure we free the data dictionary memory when finished
free(rsslDataDictionary);
//if (iDisplayType != DISPLAY)
// std::cout << "Could not load RDM Field Dictionary file." << std::endl;
return -1;
}
// Load enum dictionary
if (rsslLoadEnumTypeDictionary("enumtype.def", rsslDataDictionary, &rsslBufferError) != RSSL_RET_SUCCESS)
{
// Ensure we free the data dictionary memory when finished
free(rsslDataDictionary);
//if (iDisplayType != DISPLAY)
// std::cout << "Could not load Enum Type Dictionary file." << std::endl;
return -1;
}
std::string strCombinedFileName;
std::vector<CRWFCapsule> vRWFCapsules;
std::vector<std::string> vstrInputFileNames;
pPcapParser = new CPCAPParser(1,1); // Initiate C++ class instance
boost::algorithm::split_regex(vstrInputFileNames, strInputFileNames, boost::regex(","));
// Let's iterate through each PCAP file and parse it.
for (int i = 0; i < vstrInputFileNames.size(); ++i)
{
if (false == strCombinedFileName.empty())
{
strCombinedFileName.append("-");
}
if (false == pPcapParser->Parse(vstrInputFileNames[i].c_str(), vRWFCapsules, vEEDRRMachines, vRRRHMachines, *rsslDataDictionary))
{
delete pPcapParser;
// Ensure we free the data dictionary memory when finished
free(rsslDataDictionary);
vRWFCapsules.clear();
return -1;
}
strCombinedFileName = strCombinedFileName.append(pPcapParser->GetFileName());
}
//if (iDisplayType != NO_DISPLAY)
// std::cout << "Clearing up..." << std::endl;
delete pPcapParser;
if (rsslRet = rsslDeleteDataDictionary(rsslDataDictionary) != RSSL_RET_SUCCESS)
{
// Ensure we free the data dictionary memory when finished
free(rsslDataDictionary);
//if (iDisplayType != NO_DISPLAY)
// std::cout << "Problem deleting dictionary." << std::endl;
return -1;
}
// Ensure we free the data dictionary memory when finished
free(rsslDataDictionary);
if (vRWFCapsules.empty())
return -1;
CMessageSorter msgSorter(vEEDMachines, vRRMachines);
std::map<std::string, SMessageInfo> m_msMessageInfo;
std::map<std::string, SSystemInfo> m_msSystemInfo = msgSorter.SortMessages(vRWFCapsules, 1, m_msMessageInfo);
if (m_msSystemInfo.empty())
return -1;
CDataVerifier dataVerifier(strCombinedFileName, 1, 1);
dataVerifier.CreateOutputForAllMessages(m_msMessageInfo);
dataVerifier.Process(m_msSystemInfo);
// When clearing the vector, we have to make sure that the destructor of the RWF message is being called... so everything destroys itself correctly.
// Will need to check if destruction is down properly. However, how can we delete RWF Capsules encapsulated in eachother?...
vRWFCapsules.clear();
}
And its appropriate header file:
// DataVerifierLib.h
#pragma once
#include "../DataVerifier/CRWFCapsule.h"
#include "../DataVerifier/ISystem.h"
#include "../DataVerifier/CFrameAnalyser.h"
#include "../DataVerifier/CPCAPParser.h"
#include "../DataVerifier/CMessageSorter.h"
#include "../DataVerifier/CDataVerifier.h"
#include "../DataVerifier/CSourceDirectoryResponse.h"
#include "../DataVerifier/CDataVerifierConstants.h"
#include <vector>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include <msclr/marshal_cppstd.h>
using namespace System;
using namespace DataVerifier;
namespace DataVerifierLib {
public ref class PCAPParserWrapper
{
// TODO: Add your methods for this class here.
public:
// Constructor
PCAPParserWrapper();
// Wrapper Methods
bool ParseWrapper(String^ strInputFileNames);
// Fill vectors with appropriate RR/EED/RH addresses
std::vector<std::string> CreateNewMachineCollection(std::vector<std::string> vstrNewMachines, std::vector<std::string> vstrMachine1, std::vector<std::string> vstrMachine2);
// Public Variable
double initVal;
private:
CPCAPParser *pPcapParser; // an instance of class in C++
};
}

Don't know why I have received down votes for this, as the solution could be quite useful to people mixing C# and a C++/CLI wrapper.
I realized that the suspect line was actually this line:
//String^ managedStringTmp = managedString;
//std::string strInputFileNames = msclr::interop::marshal_as<std::string>(managedStringTmp);
std::string strInputFileNames = marshalString<E_UTF8>(managedString);
I've obviously changed it so that it is using this new "marshalString" function. I used a marshal string template class from the link here, as it seems to address performance issues.
http://blog.nuclex-games.com/mono-dotnet/cxx-cli-string-marshaling/
Now all I need to do is figure out why returning true from the wrapper and assigning it to a boolean in C# returns false... but hopefully this will help some people out.
UPDATE
This explains why returning false or true in the wrapper back to C# won't work, and that we either need to use marshalling of the function (didn't want to do) or return 0 for false and 1 for true.
C# DllImport with C++ boolean function not returning correctly

Related

How do I enumerate NVMe (M2) drives an get their temperature in c#?

How do I enumerate NVMe (M2) drives an get their temperature in c# ?
It's not accessible through WMI usual queries.
There this MSFT reference to do this in c, but it's rather obscure, code is not complete:
https://learn.microsoft.com/en-us/windows/desktop/fileio/working-with-nvme-devices#temperature-queries
This answer is as much a tutorial as answer. I am a newbie to C++ and had to learn by mistakes and a lot of research to understand the range of environment related steps necessary to get code compiled and working. Along the way Visual Studio 2022 implementation of C++ has lots of quirks that I haven’t yet figured out including Functions referenced in Header Files not being found even when the Header file was properly referenced, loaded and accessible.
I have written a desktop utility program in VB which shows graphically the amount of space used on Drives as well as R/W data rates, graphs for CPU/Thread usage, Memory info, clocks, temperatures etc plus a desktop calculator and units converter. Most of the data is extracted through inbuilt PerformanceCounters except CPU/Thread Temperature which thanks to CoreTemp can be read directly from their free App when running.
Why in VB? Because it is so easy to write GUI type apps on forms with controls and manage strings and this makes up 95% of the code, with data access being the other 5%. But VB has its limitations hence C++. (Don’t wish to fire up C experts who have a thousand reasons why not to use VB)
In VB I am able to extract Drive Temperatures for SATA drives using WMI functions which adhere to S.M.A.R.T protocol, but NVMe drives don’t follow this protocol. So have implemented code as suggested by Microsoft in C++ to access NVMe drive temperatures and built it as a DLL which can be accessed by either C or VB client programs. VB does not have a Malloc memory allocation function (memset in VS) so working with unmanaged memory is more difficult, even though research has suggested it can be done. But this C++ Function is a very small amount of code, simple, fast and scanning 5 drives for temperature takes around 3ms. By comparison using WMI to read 3 SATA drives takes typically 100ms.
This is specifically written in VB Studio 2022 environment using DLL template, with the project called ReadNVMeTemperatures.
Output is a file ReadNVMeTemperatures.dll and I am happy to make this available to anyone if you don’t want to build it yourself.
Firstly the C++ files for ReadNVMeTemperatures Project
//pch.h
#pragma once
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <Windows.h>
#include "pch.h"
#include <nvme.h>
#include <winioctl.h>
#include <string>
#include <ntddstor.h>
#include <corecrt_malloc.h>
// Main.h
#pragma once
namespace GetTemp
{
class MyGetTemps
{
public:
// Receives integer a as disk number from client and returns Temperature as GetNVMeTemp
static __declspec(dllexport) int GetNVMeTemp(int a);
};
}
//main.cpp
#include "pch.h"
#include <stdexcept>
#include "main.h"
using namespace winrt;
using namespace Windows::Foundation;
using namespace std;
namespace GetTemp
{
int MyGetTemps::GetNVMeTemp(int a)
{
// a is the physical disk number and must be added to string literal below to form a string constant TargetDrive
//if only 1 known drive eg 2 to be scanned can use constexpr auto TargetDrive = L"\\\\.\\PhysicalDrive2";
std::string stddrivestring = "\\\\.\\PhysicalDrive" + to_string(a);
std::wstring widedrivestring = std::wstring(stddrivestring.begin(),
stddrivestring.end());
const wchar_t* TargetDrive = widedrivestring.c_str();
HANDLE hDevice = INVALID_HANDLE_VALUE; // handle to the drive to be examined
hDevice = CreateFileW((LPWSTR)TargetDrive, // drive to open
0, // no access to the drive
FILE_SHARE_READ | // share mode
FILE_SHARE_WRITE,
NULL, // default security attributes
OPEN_EXISTING, // disposition
FILE_FLAG_OVERLAPPED, // file attributes
NULL); // do not copy file attributes
if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
return (FALSE);
}
BOOL result = 0;
PVOID buffer = NULL;
ULONG bufferLength = 0;
ULONG returnedLength = 0;
SHORT mytemp0 = 0;
SHORT mytemp1 = 0;
INT mytemp = 0;
PSTORAGE_PROPERTY_QUERY query = NULL;
PSTORAGE_PROTOCOL_DATA_DESCRIPTOR protocolDataDescr = NULL;
// Allocate buffer for use.
bufferLength = FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE;
buffer = malloc(bufferLength);
if (buffer == NULL) {
// printf("DeviceNVMeQueryProtocolDataTest: allocate buffer failed");
return 0;
}
// Initialize query data structure to get Identify Controller Data.
memset(buffer, 0, bufferLength);
query = (PSTORAGE_PROPERTY_QUERY)buffer;
protocolDataDescr = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
query->PropertyId = StorageDeviceProtocolSpecificProperty;
query->QueryType = PropertyStandardQuery;
protocolData->ProtocolType = ProtocolTypeNvme;
protocolData->DataType = NVMeDataTypeLogPage;
protocolData->ProtocolDataRequestValue = NVME_LOG_PAGE_HEALTH_INFO;
protocolData->ProtocolDataRequestSubValue = 0; // This will be passed as the lower 32 bit of log page offset if controller supports extended data for the Get Log Page.
protocolData->ProtocolDataRequestSubValue2 = 0; // This will be passed as the higher 32 bit of log page offset if controller supports extended data for the Get Log Page.
protocolData->ProtocolDataRequestSubValue3 = 0; // This will be passed as Log Specific Identifier in CDW11.
protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
protocolData->ProtocolDataLength = sizeof(NVME_HEALTH_INFO_LOG);
/ Send request down.
result = DeviceIoControl(hDevice,
IOCTL_STORAGE_QUERY_PROPERTY,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL
);
if (!result || (returnedLength == 0)) {
//printf(("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log failed. Error Code %d.\n"), GetLastError());
return 0;
}
// Validate the returned data.
if ((protocolDataDescr->Version != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR)) ||
(protocolDataDescr->Size != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR))) {
//printf(("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log - data descriptor header not valid.\n"));
return 0;
}
protocolData = &protocolDataDescr->ProtocolSpecificData;
if ((protocolData->ProtocolDataOffset < sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA)) ||
(protocolData->ProtocolDataLength < sizeof(NVME_HEALTH_INFO_LOG))) {
//printf(("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log - ProtocolData Offset/Length not valid.\n"));
return 0;
}
// SMART/Health Information Log Data
{
PNVME_HEALTH_INFO_LOG smartInfo = (PNVME_HEALTH_INFO_LOG)((PCHAR)protocolData + protocolData->ProtocolDataOffset);
// print or return as mytemp the Temperature S.M.A.R.T. data for drive a
mytemp0 = (ULONG)smartInfo->Temperature[1] << 8;// | smartInfo->Temperature[0] - 273;
mytemp1 = smartInfo->Temperature[0];
mytemp = mytemp0 + mytemp1 - 273;
}
//printf(("Temperature Drive %d = %d deg\n"), (a), (mytemp));
return mytemp;
}
}
Before Building the final DLL solution add a DEF file to the project so that Visual Basic can find the entry point name GetNVMeTemp as defined in the main.h Header File. This is necessary as C++ adds additional characters to the entrypoint name that C++ understands but VB doesn’t.
The file is named ReadNVMeTemperatures.def i.e. the same name as the Project .def.
It is a text file and should look like this.
; ReadNVMeTemperatures.def - defines the exports for ReadNVMeTemperatures.dll
LIBRARY Test
DESCRIPTION 'A C++ dll that can be called from VB'
EXPORTS GetNVMeTemp
Here is an explanation on how to load into VS. Written by someone else. Thank You!!!
INSTRUCTIONS if you need to create a DEF file for your DLL by hand:
In the folder where your project file (.vcproj) is, create a new text file.
Rename it to "outputname.def", where "outputname" is the name of your project's output. By default, this is the name of the project, see Step 4 for clarifications.
In Visual Studio, go to Project->Properties. Go to Configuration Properties->Linker->Input and enter the name of the DEF you just created into the field named Module Definition File.
Repeat steps 1 - 3 for each configuration that outputs a different named DLL. I.e. my Debug configuration makes output_d.dll, so I need an output_d.def for Debug along with an output.def for Release.
Open all of the DEF files in your current Visual Studio editor. The format for the DEF is easy:
LIBRARY outputnameEXPORTS Function1 #1 Function2
Replace "outputname" with the name of the configuration output. As mentioned in step 4, this could be output in output.def and output_d in output_d.def.
List all of the functions you are exporting. Those functions need to be correctly exported using the correct syntax in your source files. You can manually assign an ordinal by using the #number syntax. Alternatively, you can leave it assigned automatically by not having it.
That's all there is to it. Make sure to Build->Rebuild Solution afterwards. Use Dependency Walker to verify your DLL is correctly exporting your functions with the expected names.
There is more information on this topic here: Exporting from a DLL Using DEF Files . Good luck!
Now the project can be built/compiled to create the DLL.
Next an example of a C++ Client app that will retrieve a drive temperature from this DLL. I have called it ReadNVMeTemperaturesClient and set it up in a separate project directory of the that name.
It is necessary within properties of the ReadNVMeTemperaturesClient VS project to tell VS where to find (Include Directories in C++ section and Linker section) the ReadNVMeTemperatures.dll and ReadNVMeTemperatures.lib files as well as the “main.h” header file of the DLL project.
Here is a link to the details
https://www.technical-recipes.com/2012/how-to-link-dll-files-to-c-projects/
Code for ReadNVMeTemperaturesClient Client App in C++
//pch.h
#pragma once
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
//Main.cpp
#include "pch.h"
#include <iostream>
#include "main.h" //This is the main.h of the dll project
using namespace winrt;
using namespace Windows::Foundation;
using namespace std;
int main()
{
// The physical Disk number in int a eg 2
int a = 2;
int drivetemp = 0;
drivetemp = GetTemp::MyGetTemps::GetNVMeTemp(a);
printf(("result %d \n"), (drivetemp));
return 0;
}
Next a more comprehensive VB set of routines to retrieve NVMe drive temperatures for the PC.
First search for and build an array of physical drive numbers. I use the
"Win32_LogicalDiskToPartition” class which has a member called “instancename” as this is the only WMI class I have found which returns a string containing physical disk number, partition number and logical drive character all in one string. These can be easily extracted using various string functions. There are other WMI classes (eg "Win32_Diskdrive", "MSFT_PhysicalDisk, "Win32_Volume") but they require cross referencing of results to enable logical drive to be associated with physical disk number, or in the case of a RAID array, multiple physical disk numbers per logical drive.
Example code including setting up array. Some array members I have filled using other WMI or VB Function calls. Additional members can be added to the array as needed.
Public Class PHDD
Public Property PCaption() As String 'eg ST6000DM003-2CY186
Public Property PPhysical() As Integer 'physical drive number eg 1
Public Property PNVMeType() As Boolean 'true is NVMe drive, false is SATA
Public Property PDiscSize() As Integer 'Physical Disk Size in GB
Public Property PDriveChar() As String 'eg "D"
Public Property PTemperature() As Integer 'eg 32
Public Property PInstanceName() As String 'eg SCSI\Disk&Ven_SAMSUNG&Prod_SSD_830_Series\5&36Da4fed&0&020000_0 as extracted from MSStorageDriver_ATAPISmartData used to retrieve SATA Temp from SMART
End Class
Public Class Form1
Public PDiscDrives = New Dictionary(Of Integer, PHDD)()
Public PDrivesnum as integer
Public Declare Function GetNVMeTemp Lib "ReadNVMeTemperatures.dll" (a As Integer) As Integer ‘the string constant in quotes may have to reference the exact location of this file during local VS execution. Eg “C:\myVBProject\ ReadNVMeTemperatures.dll”
Sub CreatePHDDArray()
Dim n As Integer
For n = 0 To 35
Dim MyPhdd = New PHDD()
PDiscDrives.Add(n, MyPhdd) 'create 36 array size for each physical drive...ie 10 more than max 26 Logical letters in case there are RAID arrays of Logical Drives with Multiple Disks
Next
End Sub
Sub ExtractDiskNumber
Dim devst As New ManagementClass("Win32_LogicalDiskToPartition")
Dim moct As ManagementObjectCollection = devst.GetInstances()
Dim driveinfostring as string, PDisk as integer, LChar as string, n as integer, tstring as string
Pdrivesnum = 0
For Each mot As ManagementObject In moct
If True Then
driveinfostring = mot.ToString
‘add code to extract and save Physical Disk number PDisk and Logical Drive Char LChar in the PHDD class array. Use VB “Instr”, “Substring” or “Mid” Functions to isolate and extract the values
n = InStr(t, "Disk #")
If n > 0 Then
tstring = Mid(t, n, 9) ' typically Disk #4, Partition// don't know why inbuilt Substring Function returns string starting at 1 character after n so using Mid() hear instead!!!
m = InStr(tstring, ",")
If m > 0 Then
PDisc = Val(tstring.Substring(6, m - 7)) ‘Got the Physical Disk
End If
End If
'Get the Logical Drive Char
n = InStr(t, "LogicalDisk.DeviceID=\")
If n > 0 Then
LChar = Mid(t, n + 22, 1) ' typically DeviceID=\E:\
End If
PDiskDrives.PPysical = PDisk
PDiskDrives.PDriveChar = LChar
Pdrivesnum +=1
end if
Next
End Sub
End Class
Here is an “instance” string example…
"\HOMEDN\root\cimv2:Win32_LogicalDiskToPartition.Antecedent=""\\HOMEDN\root\cimv2:Win32_DiskPartition.DeviceID=""Disk #4, Partition #1"""",Dependent=""\\HOMEDN\root\cimv2:Win32_LogicalDisk.DeviceID=""C:"""""
This is Logical Drive C, and PHYSICAL4 and the number 4 is all that is needed to get the NVMe Temperature for drive C.
Below Sub can now loop through all the PC drives from 0 to Pdrivesnum-1 and get Temperature.
Shared Sub ReadNVMeTemp()
Dim n As Integer
For n = 0 To Pdrivesnum - 1
PDiskDrives(n).PTemperature = GetNVMeTemp(PDiskDrives(n).Pphysical) 'Reads the Temperature of actualphysical disk from ReadNVMeTemperatures.dll into array
If PDiskDrives(n).PTemperature > 0 Then
PDiskDrives(n).PNVMeType = True 'set this value as true meaning the drive was NVMe, because a valid temperature was returned
Else
PDiskDrives(n).PNVMeType = False 'set as false indicating an SATA or USB drive or an error retrieving Temperature – all return a 0
End If
Next
End Sub
Hope I haven't missed any of the steps which will enable anyone who like me is still learning the C++ programming environment to get the code working. Parts of the code can be written more concisely and efficiently but I have tried to make it easy to follow. I only program for fun.
A fast implementation (but not the simplest) is to use the nvme.h winioctl.h ntddstor.h API and interop with that in c#. Here is a complete implementation.
Declaration side:
#include <nvme.h>
#include <winioctl.h>
#include <ntddstor.h>
#ifndef Pinvoke
#define Pinvoke extern "C" __declspec(dllexport)
#endif
typedef void(__stdcall *MessageChangedCallback)(const wchar_t* string);
class __declspec(dllexport) NVmeQuery final
{
public:
NVmeQuery(MessageChangedCallback managedDelegate);
~NVmeQuery();
template <class ... T>
auto LogMessage(T&& ... args) -> void;
auto GetTemp(const wchar_t* nvmePath) -> unsigned long;
MessageChangedCallback LogMessageChangedCallback{};
PNVME_HEALTH_INFO_LOG SmartHealthInfo{};
PSTORAGE_PROPERTY_QUERY query{};
PSTORAGE_PROTOCOL_SPECIFIC_DATA protocolSpecificData{};
PSTORAGE_PROTOCOL_DATA_DESCRIPTOR protocolDataDescriptor{};
};
Definition side:
A function I use to retro-interop error messages to the managed side; it is critical for the understanding of what goes wrong, how/where:
template<class ...T>
auto NVmeQuery::LogMessage(T&&... args) -> void
{
wchar_t updatedMessage[256];
swprintf_s(updatedMessage, forward<T>(args)...);
if (LogMessageChangedCallback != nullptr)
LogMessageChangedCallback(updatedMessage);
}
The core function, which was not straightforward to design. It has 4 parts:
1. getting a handle to the NVMe drive
2. preparing the query
3. do the query
4. check and transmit results
auto NVmeQuery::GetTemp(const wchar_t* nvmePath) -> unsigned long
{
auto nvmeHandle = CreateFile(nvmePath, 0, 0,
0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
{
auto lastErrorID = GetLastError();
if (lastErrorID != 0)
{
LPVOID errorBuffer{};
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, lastErrorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorBuffer, 0, nullptr);
LogMessage(L"Query: ERROR creating handle to NVMe [%s]: %d, %s", nvmePath, lastErrorID, errorBuffer);
}
}
unsigned long bufferLength = FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters)
+ sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE;
void *buffer = malloc(bufferLength);
ZeroMemory(buffer, bufferLength);
query = (PSTORAGE_PROPERTY_QUERY)buffer;
protocolDataDescriptor = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
protocolSpecificData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
query->PropertyId = StorageDeviceProtocolSpecificProperty;
query->QueryType = PropertyStandardQuery;
protocolSpecificData->ProtocolType = ProtocolTypeNvme;
protocolSpecificData->DataType = NVMeDataTypeLogPage;
protocolSpecificData->ProtocolDataRequestValue = NVME_LOG_PAGE_HEALTH_INFO;
protocolSpecificData->ProtocolDataRequestSubValue = 0;
protocolSpecificData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
protocolSpecificData->ProtocolDataLength = sizeof(NVME_HEALTH_INFO_LOG);
unsigned long returnedLength{};
continuing, the actual query and then the miscellaneous checkings:
auto result = DeviceIoControl(nvmeHandle, IOCTL_STORAGE_QUERY_PROPERTY,
buffer, bufferLength,
buffer, bufferLength,
&returnedLength, nullptr);
if (!result || returnedLength == 0)
{
auto lastErrorID = GetLastError();
LPVOID errorBuffer{};
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, lastErrorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorBuffer, 0, nullptr);
LogMessage(L"Query: drive path: %s, nvmeHandle %lu", nvmePath, nvmeHandle);
LogMessage(L"Query: ERROR DeviceIoControl 0x%x %s", lastErrorID, errorBuffer);
}
if (protocolDataDescriptor->Version != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR) ||
protocolDataDescriptor->Size != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR))
{
LogMessage(L"Query: Data descriptor header not valid (size of descriptor: %llu)", sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR));
LogMessage(L"Query: DataDesc: version %lu, size %lu", protocolDataDescriptor->Version, protocolDataDescriptor->Size);
}
protocolSpecificData = &protocolDataDescriptor->ProtocolSpecificData;
if (protocolSpecificData->ProtocolDataOffset < sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) ||
protocolSpecificData->ProtocolDataLength < sizeof(NVME_HEALTH_INFO_LOG))
LogMessage(L"Query: ProtocolData Offset/Length not valid");
SmartHealthInfo = (PNVME_HEALTH_INFO_LOG)((PCHAR)protocolSpecificData + protocolSpecificData->ProtocolDataOffset);
CloseHandle(nvmeHandle);
auto temp = ((ULONG)SmartHealthInfo->Temperature[1] << 8 | SmartHealthInfo->Temperature[0]) - 273;
return temp;
} // end of GetTemp
And for the interop:
Pinvoke auto __stdcall New(MessageChangedCallback managedDelegate) -> NVmeQuery*
{
return new NVmeQuery(managedDelegate);
}
Pinvoke auto GetTemp(NVmeQuery* p, const wchar_t* nvmePath) -> unsigned long
{
return p->GetTemp(nvmePath);
}
And c# side:
public static class NVMeQuery
{
[DllImport("NVMeQuery.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr New(InteropBase.AssetCallback callback);
[DllImport("NVMeQuery.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern ulong GetTemp(IntPtr p, IntPtr drivePath);
}
public class NVMeQueries : InteropBase
{
public NVMeQueries()
{
_ptr = NVMeQuery.New(_onAssetErrorMessageChanged);
}
public ulong GetTemp() => GetTemp(#"\\.\PhysicalDrive4");
public ulong GetTemp(string drivePath)
{
var strPtr = Marshal.StringToHGlobalAuto(drivePath);
var result = NVMeQuery.GetTemp(_ptr, strPtr);
Marshal.FreeHGlobal(strPtr);
return result;
}
}
And the generic base class I use for interop:
public class InteropBase : INotifyPropertyChanged
{
protected IntPtr _ptr;
protected readonly AssetCallback _onAssetErrorMessageChanged;
public delegate void AssetCallback(IntPtr strPtr);
public List<string> LogMessages { get; private set; } = new List<string>();
public InteropBase()
{
_onAssetErrorMessageChanged = LogUpdater;
}
private unsafe void LogUpdater(IntPtr strPtr)
{
var LastLogMessage = new string((char*)strPtr);
LogMessages.Add(LastLogMessage);
OnPropertyChanged(nameof(LogMessages));
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
In my case, the NVMe drive I wanted to query was the 4th physical drive. We get all of them with:
Get-WmiObject Win32_DiskDrive
which will give in my case:
Partitions : 4
DeviceID : \\.\PHYSICALDRIVE4
Model : Samsung SSD 970 EVO Plus 1TB
Size : 1000202273280
Caption : Samsung SSD 970 EVO Plus 1TB
Remarks
This implementation is extremely fast (less that 1ms when there is no LogMessage call); you can define and fill your own structure to get other pieces of information that would be relevant; in that case, the structure must be hold in a field in the native class (eg., SmartHealthInfo in this example) and the query would transmit just the pointer to this structure.
I have implemented the Microsoft Code for this which I think is shorter and simpler - although the code shown here is very elegant. I have built it into a C++ DLL file which can be called by passing an integer value for the Physical Drive number. For my 5 drives, of which 2 are NVMe and 3 SATA, it returns the valid temperatures for the NVMe Drives and zero for the SATA. All take 4 ms. I use a WMI MSStorageDriver_ATAPISmartData call for the SATA drives...quite slow in comparison.
I can call this function from either C++ or VB. The VB implementation still only takes 4ms.
If anyone is after the code or a copy of the DLL file and client implementation then respond and I will list the code here. Otherwise I will assume no further interest.

Apple idfa in Unity via Plugin

I borrowed this code from another stack overflow answer:
example.mm
const char* getIdfa()
{
if([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled])
{
NSString * idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
//return (const char*)[idfa UTF8String]; // ##### malloc error I uncomment this line #####
}
return nil;
}
iOSplugin.cs
#if UNITY_IOS
[DllImport("__Internal")]
public static extern string getIdfa();
#endif
Xcode build settings that can affect the issue:
Project format: xcode 3.1 compatible
Obj-C Automatic Reference Counting: Yes
If I uncomment this line:
return (const char*)[fdsa UTF8String];
then I get the following error:
(531,0x19e9ef000) malloc: *** error for object 0x1357d44e1: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
There are some references to a function called cocoaToMonoString which is supposed to fix this problem, but I can't find any examples or documentation about it.
How do I return a string from Xcode back into Unity?
Just a two cents contribution. Now you can obtain IDFA (or GAID for android) using Application.RequestAdvertisingIdentifierAsync:
Application.RequestAdvertisingIdentifierAsync(
(string advertisingId, bool trackingEnabled, string error) =>
Debug.Log("advertisingId " + advertisingId + " " + trackingEnabled + " " + error)
);
Also that method returns a boolean value indicating true in case of success or false if the current platform doesn't support advertising identifiers.
[idfa UTF8String]
This is a reference to the internal byte array inside the NSString.
It can't be passed back to Unity/C#.
Add this function:
static const char* MakeStringCopy(const char* string)
{
if (string == NULL)
return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
and return:
return MakeStringCopy([idfa UTF8String]);

Convert from C to C#, or make DLL? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I am new to C# programming. I have a program that I modified in C, I now need to take the string array that I get from my C program and pass it to a C# program so that it can query an Oracle database using those values.
This program is used to get the serial numbers from all of the iButton devices connected to the computer.
Here is the C code
// function prototypes
void UnLoadTMEX(void);
short LoadTMEX(void);
// globals
static FARPROC Get_Version, TMGetTypeVersion, TMEndSession;
static FARPROC TMSetup, TMNext, TMRom, ExtendedStartSession;
static FARPROC TMReadDefaultPort;
long (__fastcall *TMExtendedStartSession)(short,short,void *);
static HINSTANCE hInst;
unsigned char state_buf[5125];
//--------------------------------------------------------------------------
// Main of iSerial64
//
void main(int argc, char **argv)
{
char refresh,buf[200];
short flag,i,didsetup=0;
short ROM[9];
short PortNum,PortType;
long SHandle;
char serialtmp[2], serial[10][17];
int j = -1;
// load the TMEX driver and get pointers to functions
if (!LoadTMEX())
{
printf("ERROR, could not load IBFS64.DLL\n");
exit(0);
}
// load the TMEX driver and get pointers to functions
TMReadDefaultPort(&PortNum, &PortType);
// get the TMEX driver version
printf("Port number: %d Port type: %d\n",PortNum,PortType);
Get_Version(buf);
printf("Main Driver: %s\n",buf);
printf("TYPE%d:",PortType);
if ((short)TMGetTypeVersion(PortType,buf) < 0)
{
printf("\nNo Hardware Driver for this type found!\n");
// Unload the TMEX driver
UnLoadTMEX();
exit(0);
}
printf(" %s\n\n\n",buf);
// check the command line
if (argc > 1)
PortNum = atoi(argv[1]);
// check for valid range of PortNum
if ((PortNum < 1) || (PortNum > 15))
{
printf("ERROR, invalid port requested: %d\n",PortNum);
exit(0);
}
// loop to display the rom numbers until key hit
do
{
// get a session handle to the requested port
SHandle = TMExtendedStartSession(PortNum,PortType,NULL);
if (SHandle > 0)
{
// check to see if TMSetup has been done once
if (!didsetup)
{
flag = (short)TMSetup(SHandle);
if (flag == 1 || flag == 2)
{
printf("TMSetup complete %d\n",flag);
didsetup = 1;
}
else
{
printf("ERROR doing setup %d\n",flag);
break;
}
}
// only get the next rom after setup complete
else
{
//j was added to keep track of the serial number strings
j++;
memset(serial[j], 0, strlen(serial[j]));
flag = (short)TMNext(SHandle,(void far *)&state_buf[0]);
if (flag > 0)
{
ROM[0] = 0;
flag = (short)TMRom(SHandle, (void far *)&state_buf[0], (short far *)&ROM[0]);
for (i = 7; i >= 0; i--)
{
//This section was changed from the original
//copies raw number into string
sprintf(serialtmp, "%02X", ROM[i]);
strcat(serial[j], serialtmp);
}
printf("%s ", serial[j]);
printf("\n");
}
else
printf("end of search\n");
}
// close the opened session
TMEndSession(SHandle);
}
}
while (flag > 0);
// Unload the TMEX driver
UnLoadTMEX();
printf("iSERIAL64 end\n");
}
//--------------------------------------------------------------------------
// Load the TMEX driver and get a pointers to the functions
//
short LoadTMEX(void)
{
// attempt to get a SHandle to the TMEX driver
hInst = LoadLibrary(L"IBFS64.DLL");
// get a pointer to the function needed by loopit64
if (hInst != NULL)
{
ExtendedStartSession = GetProcAddress(hInst,"TMExtendedStartSession");
TMEndSession = GetProcAddress(hInst,"TMEndSession");
TMSetup = GetProcAddress(hInst,"TMSetup");
TMNext = GetProcAddress(hInst,"TMNext");
TMRom = GetProcAddress(hInst,"TMRom");
Get_Version = GetProcAddress(hInst,"Get_Version");
TMGetTypeVersion = GetProcAddress(hInst,"TMGetTypeVersion");
TMReadDefaultPort = GetProcAddress(hInst, "TMReadDefaultPort");
// check to make sure got ALL of the functions needed
if ((ExtendedStartSession == NULL) || (TMEndSession == NULL) ||
(TMSetup == NULL) || (TMNext == NULL) ||
(TMRom == NULL) || (Get_Version == NULL) ||
(TMGetTypeVersion == NULL) || (TMReadDefaultPort == NULL))
{
printf("ERROR, could not get a pointer to all"
" of the TMEX functions needed\n");
return 0;
}
// get a function pointer that returns a long
TMExtendedStartSession = (long (__fastcall *)(short,short,void *))ExtendedStartSession;
return 1;
}
else
return 0;
}
//--------------------------------------------------------------------------
// UnLoad the TMEX driver
//
void UnLoadTMEX(void)
{
// release the TMEX driver
FreeLibrary(hInst);
}
Should I attempt to convert this, or just create a C DLL and import that into my C# program? Like I said, I haven't really worked with C#, I'm an intern and my boss is giving me this project so that I can learn C#. Any help is greatly appreciated
If you want use c dll in c# read this article.
But is better that convert the code to c# code because you can manage this code and update the code easily.
If your boss is giving you this project to learn C#, I'd say go ahead and convert it to C#. It will make future use of the code a lot easier.
I'd recommend looking at the book C# in Depth for a good intro to C#.

C# equivalent of C++ ostream::tellp for size limit on diskette

Is there a C# equivalent for StreamWriter of the C++ ostream::tellp? I am porting over some old C++ code to C#, but the client still wants to keep using diskettes (read: old equipment), so I need to find a way to find the file pointer position or to find how much I've written to disk already.
Below is the method that I've created so far:
private bool isDisketteBoundary(ref StreamWriter swOutput, int nCurrentDisketteNo) {
// Get current file pointer position
// long filePosition = nOStream.tellp(); <-- C++ code
long filePosition = 0; // <-- needs to change to find file pointer position
// Valid?
if(filePosition != -1) {
// Is the new size over a boundary?
float numDiskettes = (float)((float)filePosition / (float)Constants.DisketteSize);
int disketteCount = Convert.ToInt32(Math.Ceiling(numDiskettes));
// Is the diskette count larger than the current index?
return (nCurrentDisketteNo < disketteCount) ? true : false;
}
else {
throw new Exception("Unable to get file pointer from StreamWriter");
}
}
I think you are looking for
swOutput.BaseStream.Position
see MSDN: http://msdn.microsoft.com/en-us/library/system.io.stream.position.aspx .

ExecuteInDefaultAppDomain returns 8013101B

I am trying to host CLR in my native Win32 C++ application.
CLR loading works okay, but when i try to execute a method in the assembly, then ExecuteInDefaultAppDomain returns 0x8013101B, and bails out.
Here is the code snippet:
// Managed Code
namespace ManagedLibrary
{
public class LibraryBootstrapper
{
static LibraryBootstrapper()
{
MessageBox.Show("Static LibraryBootsrapper");
}
public LibraryBootstrapper()
{
}
public static int Initialize(String str)
{
MessageBox.Show("Hi " + str + ", Library Bootsrapped");
return 0;
}
}
// Native Code
int tmain()
{
// Bind to the runtime.
ICLRRuntimeHost *pClrHost = NULL;
HRESULT hrCorBind = CorBindToRuntimeEx(
NULL, // Load the latest CLR version available
L"wks", // Workstation GC ("wks" or "svr" overrides)
0, // No flags needed
CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost,
(PVOID*)&pClrHost);
// Now, start the CLR.
HRESULT hrStart = pClrHost->Start();
DWORD result = 0;
// Load an assembly and execute a method in it.
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(L"C:\\KIRAN\\Workspaces\\VS 2010\\HostCLR\\ManagedLibrary\\bin\\Debug\\ManagedLibrary.dll", L"ManagedLibrary.LibraryBootstrapper", L"Initialize", L"Kiran", &result);
//HRESULT hrStop = pClrHost->Stop();
return;
}
I figured it out!
The problem was that the versions of .NET frame that was being referenced by native and managed projects were different. Syncing that up worked.
And, btw, the error code 0x8013101B, corresponds to COR_E_NEWER_RUNTIME (see corerror.h), which helped me figure out the problem.
The error codes are explained here: http://blogs.msdn.com/b/yizhang/archive/2010/12/17/interpreting-hresults-returned-from-net-clr-0x8013xxxx.aspx

Categories

Resources