LPCSTR data type equivalent in C# - c#

I have an API that takes three parameters:
HANDLE Connect(LPCSTR MachineName, LPCSTR ServerName, BOOL EnableDLLBuffering);
How can I use this method in C#?
What is the equivalence of LPCSTR? And what should be use in place of HANDLE?

The HANDLE equivalent is IntPtr (or you could use one of the subclasses of SafeHandle, many of which are defined in the namespace Microsoft.Win32.SafeHandles). The equivalent of LPCSTR is string or StringBuilder (but string is better, because you are passing the string to the method and the method won't modify it). You can even use a byte[] (as I have wrote you in the other response, but you must encode your string in the buffer, and add a \0 at the end... it's quite inconvenient). In the end an LPCSTR is a constant LPSTR that the method won't modify. It's better you set the CharSet.Ansi as in the other response.
[DllImport("YourDll.dll", CharSet = CharSet.Ansi)]
static extern IntPtr Connect(string machineName, string serverName, bool enableDLLBuffering);
and you call it as:
IntPtr ptr = Connect("MyMachine", "MyServer", true);
or, if you really want to do the encoding yourself:
[DllImport("YourDll.dll", CharSet = CharSet.Ansi)]
static extern IntPtr Connect(byte[] machineName, byte[] serverName, bool enableDLLBuffering);
and
public static byte[] GetBytesFromStringWithZero(Encoding encoding, string str)
{
int len = encoding.GetByteCount(str);
// Here we leave a "space" for the ending \0
// Note the trick to discover the length of the \0 in the encoding:
// It could be 1 (for Ansi, Utf8, ...), 2 (for Unicode, UnicodeBE), 4 (for UTF32)
// We simply ask the encoder how long it would be to encode it :-)
byte[] bytes = new byte[len + encoding.GetByteCount("\0")];
encoding.GetBytes(str, 0, str.Length, bytes, 0);
return bytes;
}
IntPtr ptr = Connect(
GetBytesFromStringWithZero(Encoding.Default, "MyMachine"),
GetBytesFromStringWithZero(Encoding.Default, "MyServer"),
true);
This variant is better if you have to call the method many many times always with the same strings, because you can cache the encoded versions of the string and gain something in speed (yes, normally it's an useless optimization)

According to How to map Win32 types to C# types when using P/Invoke?:
LPCSTR (C) - string (C#)
HANDLE (C) - IntPtr (C#)

I use StringBuilder:
[DllImport("user32.dll", CharSet = CharSet.Ansi)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
var sb = new StringBuilder();
var ret = GetClassName(hwnd, sb, 100);
var klass = sb.ToString();

Related

Issue with API with char* return being caught by IntPtr in P/invoke

I am trying to P/Invoke a C++ API returning a char* which is a string in C#. I know this should be caught by IntPtr then convert the pointer to string using the Marshaler like so,
C++ API
char* WINAPI MY_API(const char *basebuf, char *strbuf)
{
return targetfunction(basebuf, strbuf);
}
C# P/Invoke
[DllImport("myDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
internal static extern IntPtr MY_API([MarshalAs(UnmanagedType.LPStr)] string basebuf,
[MarshalAs(UnmanagedType.LPArray), Out] byte[] strbuf);
C# Wrapper
public string MY_API_WRAPPER(string basebuf, out string strbuf)
{
byte[] strbufTemp = new byte[256];
IntPtr ret = MY_API(basebuf, strbufTemp);
string retstr = Marshal.PtrToStringAnsi(ret);
strbuf = MyDataConverter.ByteToString(strbufTemp);
return retstr;
}
The out parameter string is OK. but the returned string (converted from IntPtr) is garbage.
I can modify the C++ API to allocate the char* to Task Memory then free it in the C# Wrapper, but changing the native code was not an option. also it has a risk of memory leak if the API was used without the wrapper.
i was wondering what happened to the pointer when the API call ended?
Thanks to David, i was able to fix my problem. Since the return value of the function is a pointer to the basebuf parameter, it acquires garbage value when the function returns.
The basebuf should be an IntPtr not a string for the IntPtr return to get the correct value after the call. So it should be P/Invoked like this.
C# P/Invoke
[DllImport("myDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
internal static extern IntPtr MY_API(IntPtr basebuf,
[MarshalAs(UnmanagedType.LPArray), Out] byte[] strbuf);
C# Wrapper
public string MY_API_WRAPPER(string basebuf, out string strbuf)
{
byte[] strbufTemp = new byte[256];
IntPtr basebufPtr = Marshal.StringtoHGlobalAnsi(basebuf)
IntPtr ret = MY_API(basebufPtr , strbufTemp);
string retstr = Marshal.PtrToStringAnsi(ret);
strbuf = MyDataConverter.ByteToString(strbufTemp);
Marshal.FreeHGlobal(basebufPtr);
return retstr;
}

Unknown error when using .NET 4.0 with dll

c++ code is
MSIPC_SDK LONG __stdcall Ms_IpcClient_CaptureImage(LONG nUserId, char *sFilePath,
int nPathLen, const char *sDiskPath = NULL);//sDiskPath example: "C: \\".
Affect: Take a snapshoot
Parameters remark:
LONG nUserId: Ms_Ipc_Login()//Return value after login successfully
char *sFilePath: //destination for saving the recording files
int nPathLen: //the length of the path
const char *sDiskPath = NULL: //which disk to be saved
my c# code is:
[DllImport("MsIpcSDK", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern int Ms_IpcClient_CaptureImage(
int lUserID,
[MarshalAs(UnmanagedType.LPStr)]
string sFilePath,
int nPathLen,
[MarshalAs(UnmanagedType.LPStr)]
string sDiskPath
);
and using is method:
var ret = Ms_IpcClient_CaptureImage(loginID, "C:\\a.bmp", 10000, "C:\\");
It is working in .Net Framework 2 but does not work in .Net Framework 4.
How can I fix it in .Net Framework 4?
sFilePath is used to pass a string from callee to caller. That's why the type is char* rather than const char*, and that's why there is a parameter for buffer length. That means you need to use StringBuilder rather than string. The p/invoke should be:
[DllImport("MsIpcSDK", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern int Ms_IpcClient_CaptureImage(
int lUserID,
StringBuilder sFilePath,
int nPathLen,
string sDiskPath
);
And the call should be:
var filePath = new StringBuilder(260);
var ret = Ms_IpcClient_CaptureImage(loginID, filePath, filePath.Capacity, "C:\\");
Your code has always been wrong and you've just been getting away with it up until now. The fact that you passed a made up buffer length value of 10000 should have set the alarm bells ringing!

A Problem in Pinvoke

I have the following function in C++ native dll, and I want to use it in a C# app.
DWORD __cdecl Foo(
LPCTSTR Input,
TCHAR** Output,
DWORD Options,
ErroneousWord** List = NULL,
LPDWORD Count = 0
);
Using Pinvoke
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Foo(string InputWord, out string Output, UInt32 Options, out object List,out UInt32 Count);
Calling code:
string output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y,out dummyError,out dummyCount);
I got the following exception
Attempted to read or write protected
memory. This is often an indication
that other memory is corrupt
P.S:
ErroneousWord is struct and I do not need its output, so I marshal it as object
That error more than likely means that you have a marshaling problem.
You don't show us what the ErroneousWord type is, but I assume it's some kind of class defined in your C++ code. My guess is that it's not being marshaled correctly to a .NET object.
Considering that it's a pointer (or a pointer to a pointer), try changing that parameter to an IntPtr type to represent a pointer, instead. It shouldn't matter, since you're simply passing NULL for the argument anyway, easily represented using the static IntPtr.Zero field.
You probably also want to marshal Output the exact same way. If you change the parameter to an IntPtr type, you'll receive a pointer to a TCHAR*, which you can then pass to the other unmanaged functions however you see fit (e.g., to free it).
Try the following code:
[
DllImport("dllName",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl)
]
public static extern UInt32 Foo(
string InputWord,
out IntPtr Output, // change to IntPtr
UInt32 Options,
out IntPtr List, // change to IntPtr
out UInt32 Count);
IntPtr output;
IntPtr dummyError = IntPtr.Zero;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y, out dummyError, out dummyCount);
You might also need to use the Marshal.AllocHGlobal method to allocate unmanaged memory from your process that is accessible to the C++ code. Make sure that if you do so, you also call the corresponding Marshal.FreeHGlobal method to release the memory.
Given Cody's answer and the comments, you will have to do it this way:
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static UInt32 Foo(string InputWord, out IntPtr Output, UInt32 Options, out IntPtr List, out UInt32 Count);
Now to get the string value in Output marshalled over to managed memory you will do:
string outputValue = Marshal.PtrToStringAnsi(Output);
You must know if TCHAR is Ansi or Unicode and use the appropriate marshal.
Remember to hang onto the Output IntPtr so you can pass that to the native Free method.
Thanks Cody for your answer but I want to make a seperate one, first Output is created by Foo from the native side, and I call FreeFoo to free the allocated memory by Foo.
The following is the code
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Correct(string InputWord, out IntPtr Output, UInt32 Options, out object List,out UInt32 Count);
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void FreeFoo(IntPtr Output);
}
To use it:
public string FooWrapper(string Text)
{
IntPtr output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x,out dummyError,out dummyCount);
string str = Marshal.PtrToStringUni(output);
FreeFoo(output);
return str;
}
Whatever the ErroneousWord type is, you can't marshal an array as a single out object. If it is at all possible to marshal as an object...

Call win32 CreateProfile() from C# managed code

Quick question (hopefully), how do I properly call the win32 function CreateProfile() from C# (managed code)? I have tried to devise a solution on my own with no avail.
The syntax for CreateProfile() is:
HRESULT WINAPI CreateProfile(
__in LPCWSTR pszUserSid,
__in LPCWSTR pszUserName,
__out LPWSTR pszProfilePath,
__in DWORD cchProfilePath
);
The supporting documents can be found in the MSDN library.
The code I have so far is posted below.
DLL Import:
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int CreateProfile(
[MarshalAs(UnmanagedType.LPWStr)] string pszUserSid,
[MarshalAs(UnmanagedType.LPWStr)] string pszUserName,
[Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath,
uint cchProfilePath);
Invoking the function:
/* Assume that a user has been created using: net user TestUser password /ADD */
// Get the SID for the user TestUser
NTAccount acct = new NTAccount("TestUser");
SecurityIdentifier si = (SecurityIdentifier)acct.Translate(typeof(SecurityIdentifier));
String sidString = si.ToString();
// Create string buffer
StringBuilder pathBuf = new StringBuilder(260);
uint pathLen = (uint)pathBuf.Capacity;
// Invoke function
int result = CreateProfile(sidString, "TestUser", pathBuf, pathLen);
The problem is that no user profile is ever created and CreateProfile() returns an error code of: 0x800706f7. Any helpful information on this matter is more than welcomed.
Thanks,
-Sean
Update:
Solved!
string buffer for pszProfilePath cannot have a length greater than 260.
For the out parameter you should set the marshalling. More importantly, by passing a StringBuilder you already implicitly have an output parameter. Thus, it should become:
[DllImport("userenv.dll", CharSet = CharSet.Auto)]
private static extern int CreateProfile(
[MarshalAs(UnmanagedType.LPWStr)] string pszUserSid,
[MarshalAs(UnmanagedType.LPWStr)] string pszUserName,
[Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath,
uint cchProfilePath);
Calling this method:
int MAX_PATH = 260;
StringBuilder pathBuf = new StringBuilder(MAX_PATH);
uint pathLen = (uint)pathBuf.Capacity;
int result = CreateProfile(sidString, "TestUser", pathBuf, pathLen);
It may not be the only problem but you need to add the [Out] attribute to the pszProfilePath parameter in your DLL import declaration.

GetShortPathNameA not working in C#

I'm trying to get get the short filename from a long filename but I'm having problem in c# code. VB.Net code is:
Declare Function GetShortPathName Lib "kernel32" _
Alias "GetShortPathNameA" (ByVal lpszLongPath As String, _
ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long
Public Function GetShortName(ByVal sLongFileName As String) As String
Dim lRetVal As Long, sShortPathName As String, iLen As Integer
'Set up buffer area for API function call return
sShortPathName = Space(255)
iLen = Len(sShortPathName)
'Call the function
lRetVal = GetShortPathName(sLongFileName, sShortPathName, iLen)
'Strip away unwanted characters.
GetShortName = Left(sShortPathName, lRetVal)
End Function
I've converted this function to c#:
[DllImport("kernel32", EntryPoint = "GetShortPathNameA")]
static extern long GetShortPathName(string lpszLongPath, string lpszShortPath, long cchBuffer);
public string GetShortName(string sLongFileName)
{
long lRetVal;
string sShortPathName;
int iLen;
// Set up buffer area for API function call return
sShortPathName = new String(' ', 1024);
iLen = sShortPathName.Length;
// Call the function
lRetVal = GetShortPathName(sLongFileName, sShortPathName, iLen);
// Strip away unwanted characters.
return sShortPathName.Trim();
}
But I cant get the c# version work. Am I missing something or what is wrong?
The VB declaration dates back to VB6, it is quite inappropriate for a .NET language. Although the P/Invoke marshaller will allow unmanaged code scribbling into a string, it causes random failure due to string interning. You also really want to use the Unicode version so you don't get unexpected character translation. And you want to do something meaningful if the function fails. Here's my version:
public static string GetShortName(string sLongFileName) {
var buffer = new StringBuilder(259);
int len = GetShortPathName(sLongFileName, buffer, buffer.Capacity);
if (len == 0) throw new System.ComponentModel.Win32Exception();
return buffer.ToString();
}
[DllImport("kernel32", EntryPoint = "GetShortPathName", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetShortPathName(string longPath, StringBuilder shortPath, int bufSize);
Maybe because of CharSet/Marshaling, remember also that string are imutable. Try the following :
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError=true)]
static extern uint GetShortPathName(
[MarshalAs(UnmanagedType.LPTStr)]
string lpszLongPath,
[MarshalAs(UnmanagedType.LPTStr)]
StringBuilder lpszShortPath,
uint cchBuffer);
(From pinvoke)
/// <summary>
/// The ToLongPathNameToShortPathName function retrieves the short path form of a specified long input path
/// </summary>
/// <param name="longName">The long name path</param>
/// <returns>A short name path string</returns>
public static string ToShortPathName(string longName)
{
uint bufferSize = 256;
// donĀ“t allocate stringbuilder here but outside of the function for fast access
StringBuilder shortNameBuffer = new StringBuilder((int)bufferSize);
uint result = GetShortPathName(longName, shortNameBuffer, bufferSize);
return shortNameBuffer.ToString();
}
pinvoke.net lists the import at such:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError=true)]
static extern uint GetShortPathName(
[MarshalAs(UnmanagedType.LPTStr)]
string lpszLongPath,
[MarshalAs(UnmanagedType.LPTStr)]
StringBuilder lpszShortPath,
uint cchBuffer);
pinvoke.net is a great reference for these types of issues where you're not exactly sure how the singature would translate.
That would change your code like so:
public static string GetShortName(string sLongFileName)
{
long lRetVal;
int iLen = 1024;
StringBuilder sShortPathName = new StringBuilder(iLen);
// Call the function
lRetVal = GetShortPathName(sLongFileName, sShortPathName, iLen);
// Strip away unwanted characters.
return sShortPathName.ToString();
}

Categories

Resources