-
May 25th, 2020, 01:42 PM
#1
DLL C++ and C#
Hi,
I'm writing a dll in C++ to get the serial hd, this is the code of the function:
Code:
__declspec(dllexport) char* __stdcall char* GetSerialHD()
{
WmiQueryResult res = getWmiQueryResult(L"SELECT SerialNumber FROM Win32_PhysicalMedia", L"SerialNumber");
char* ret = NULL;
std::string strValue;
char* strReturn = NULL;
for (const auto& item : res.ResultList) {
strValue.assign(item.begin(), item.end());
ULONG ulSize = strlen(MyString) + sizeof(char);
strReturn = (char*)::CoTaskMemAlloc(ulSize);
strcpy(strReturn, strValue.c_str());
break;
}
return strReturn;
}
The dll function calls it from an application written in C#:
Code:
[DllImport("mydll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetSerialHD();
...
string GetHD = GetSerialHD();
function GetSerialHD() always returns null, but the same code in a console application C++ for testing works well.
thanks
-
May 26th, 2020, 11:19 AM
#2
Re: DLL C++ and C#
You need to declare the c-style function to take a string buffer and buffer size as parameters, not return a pointer to a string. For an example, see how GetComputerName is declared in the winapi, then look on pinvoke.net to see how it is called from .net.
Btw, this code may 'work' in c, but it isn't the proper way to do it - a function should never return a variable to a local string.
-
May 27th, 2020, 04:53 AM
#3
Re: DLL C++ and C#
Originally Posted by Arjay
You need to declare the c-style function to take a string buffer and buffer size as parameters, not return a pointer to a string. For an example, see how GetComputerName is declared in the winapi, then look on pinvoke.net to see how it is called from .net.
Btw, this code may 'work' in c, but it isn't the proper way to do it - a function should never return a variable to a local string.
but this test function works well
Code:
__declspec(dllexport) char* __stdcall Mytest()
{
char szSampleString[] = "Hello World";
ULONG ulSize = strlen(szSampleString) + sizeof(char);
char* pszReturn = NULL;
pszReturn = (char*)::CoTaskMemAlloc(ulSize);
strcpy(pszReturn, szSampleString);
return pszReturn;
}
from c#
string ret = Mytest(); //works well
-
May 27th, 2020, 05:23 AM
#4
Re: DLL C++ and C#
Works well? You mean by leaking memory?
The caller of the dll needs to be responsible to allocate and clean up memory. The dll can't do it, because if it allocated the memory, it wouldn't know when to free it, so the STANDARD Windows practice is to allocate a buffer in the caller, and pass the buffer and its size to the dll.
Look at any WinApi function that needs to return a string and they all do it by getting passed a buffer and a size.
As I mentioned, see GetComputerName on how to properly declare such a function. https://docs.microsoft.com/en-us/win...tcomputernamew
-
May 27th, 2020, 06:31 AM
#5
Re: DLL C++ and C#
Originally Posted by Arjay
Works well? You mean by leaking memory?
The caller of the dll needs to be responsible to allocate and clean up memory. The dll can't do it, because if it allocated the memory, it wouldn't know when to free it, so the STANDARD Windows practice is to allocate a buffer in the caller, and pass the buffer and its size to the dll.
Look at any WinApi function that needs to return a string and they all do it by getting passed a buffer and a size.
As I mentioned, see GetComputerName on how to properly declare such a function. https://docs.microsoft.com/en-us/win...tcomputernamew
I created a C # test project and called this function, and it returns me a string correctly
unfortunately I am not an expert in C ++, I would just like to solve this problem
-
May 27th, 2020, 07:22 AM
#6
Re: DLL C++ and C#
Originally Posted by HenryDev
I created a C # test project and called this function, and it returns me a string correctly
Yes. But it produces the memory leaks! Your DLL allocates some piece of memory by every call. And it never deallocates these pieces!
Originally Posted by HenryDev
unfortunately I am not an expert in C ++, I would just like to solve this problem
Then change the function in the DLL. It should have two parameters (the same as GetComputerName Arjay mentioned).
It should NOT allocate any memory that it won't be able to free back.
It will just copy something to the buffer passes as a (first) parameter and ensure that the buffer size is enough for the text it will copy.
Victor Nijegorodov
-
May 27th, 2020, 08:36 AM
#7
Re: DLL C++ and C#
The c/c++ dll should look something like (not tested):
Code:
_declspec(dllexport) void __stdcall Mytest(char* buf, int buflen)
{
char szSampleString[] = "Hello World";
strncpy_s(buf, buflen, szSampleString, _TRUNCATE);
}
with the caller allocating/deallocating the memory.
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
May 28th, 2020, 06:25 AM
#8
Re: DLL C++ and C#
There are a couple of things to consider when pinvoking between C# and C. The first is C# strings are all UNICODE character set internally. The 2nd is what target is C# code getting compiled for - 32 or 64 bit?
Since C# is UNICODE, it makes sense to expose strings in the C dll as UNICODE, i.e. not as char *. So the function prototype becomes:
Code:
declspec(dllexport) BOOL __stdcall GetSerialHD(LPWSTR lpBuffer, LPDWORD nSize );
While the Windows convention would have you create two separate function exports, an A suffix MCBS variant GetSerialHDA and a W UNICIDE variant GetSerialHDW, that would be probably be overkill since only the UNICODE variant is needed.
With regard to 32 or 64 bit.. you'll need to compile the C dll to match the target of what the C# assembly is compiled for, i.e. you can't call a 32 bit C dll from a 64 bit C# assembly and vice versa.
All that aside, is the C dll even needed? I ask because if you are using the C dll to only make WMI calls, you can do that in C# (as C# has WMI classes to use directly).
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|