PInvoke with LPSTR parameters problem

A

Asfar

I have a MFC dll in which one of the parameters is of LPSTR.

The declaration of my MFC dll looks like
extern "C" __declspec(dllexport) int WINAPI EXPORT TestPInvokeFunc(LPSTR
szFileName, LPSTR szResult[])
{
char resultchar[100];
//some processing which put the result values in resultchar
strcpy(szResult[0], &resultchar[0]);
return 0;
}


In VS2005 this function is declared as
[DllImport("PInvokeTest.dll")]
static int
TestPInvokeFunc([MarshalAsAttribute(UnmanagedType::LPStr)]String^,
[In][Out][MarshalAsAttribute(UnmanagedType::LPArray,
ArraySubType=UnmanagedType::LPStr, SizeConst=1)] array<String^>^);

This is how I call this function
int nFuncResult;
array<String^>^ strResult = gcnew array<String^>(1);
strResult[0] = ""
nFuncResult = TestPInvokeFunc(imgFileName, strResult)


I had to use string array because i want to return the modified value of
szResult to the calling function.
When doing the strcpy in TestPInvokeFunc I get an error that Heap block has
been modified past the requested size of 1.

Any ideas on what I might be doing wrong?

Thanks,
-Asfar
 
M

Marcus Heege

1) Why do you define a P/Invoke function manually. The compiler and the
linker do it for you if you include the header and call the function.
2) If you define a P/Invoke function manually, why don't you do it like
this:
[DllImport("PInvokeTest.dll")] int WINAPI EXPORT TestPInvokeFunc(LPSTR
szFileName, LPSTR szResult[])
 
J

Jochen Kalmbach [MVP]

Hi Marcus!
1) Why do you define a P/Invoke function manually. The compiler and the
linker do it for you if you include the header and call the function.

Really???
The main reason for using DllImport in C++/CLI is the posibility to
generate /clr:safe code; which is not possible for IJW (old name but
still true).

Also if you use IJW, then you can not use the "GetLastError" funtion to
retrive the last error-value of unmanaged functions, because the
transition from unmanaged to managed might already overwritten the
gle-result...

So there are plenty of reasons *not* to use IJW...

(of course, in such cases there are also plenty of reasons to do the
whle stuff in C# ;-) )

Here is an example of /clr:safe:

using namespace System;
using namespace System::Runtime::InteropServices;

namespace MyTest
{
ref class RefClass
{
public:
[DllImport(
"kernel32.dll",
EntryPoint="GetCommandLineW",
SetLastError=true,
CharSet=CharSet::Unicode, ExactSpelling=true,
CallingConvention=CallingConvention::StdCall)]
static String^ MyGetCommandLine();
};
}
int main(array<System::String ^> ^args)
{
String ^s = gcnew String(MyTest::RefClass::MyGetCommandLine());
return 0;
}



2) If you define a P/Invoke function manually, why don't you do it like
this:
[DllImport("PInvokeTest.dll")] int WINAPI EXPORT TestPInvokeFunc(LPSTR
szFileName, LPSTR szResult[])

You can´t. Either you use IJW or you use P/Invoke...

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
 
M

Marcus Heege

Hi Jochen!

This starts to be an interesting discussion.
Really???
The main reason for using DllImport in C++/CLI is the posibility to
generate /clr:safe code; which is not possible for IJW (old name but still
true).

I did not read anything in the Asfar's question that mentioned /clr:safe
compilation. If /clr:safe is required, then a P/Invoke function is
necessary. We both agree, that if you have to write code with /clr:safe,
then it is necessary to define a P/Invoke function manually. (Unless you
link with a /clr compiled object file, but that's another story.)
Also if you use IJW, then you can not use the "GetLastError" funtion to
retrive the last error-value of unmanaged functions, because the
transition from unmanaged to managed might already overwritten the
gle-result...

This is very similar to what I have thought, but if we talk about C++/CLI it
is wrong for 99% of the cases.

This code is guaanteed to work correctly in C++/CLI:

HANDLE h = ::CreateFile(...); // Win32 API CreateFile
if (h = NULL)
printf("error code: %d", GetLastError());

This is true, because in C++/CLI, the automatically generated P/Invoke
metadata for functions in other DLLs DOES have the lasterr flag.

GetLastError() only fails if you try to use report errors via SetLastError
in your own native functions. Automatically generated P/Invoke metadata for
native function living in the same PE File as the caller do NOT have the
lasterr flag.

In C++ Managed Extensions 2002 and 2003, the situation was different though.
2) If you define a P/Invoke function manually, why don't you do it like
this:
[DllImport("PInvokeTest.dll")] int WINAPI EXPORT TestPInvokeFunc(LPSTR
szFileName, LPSTR szResult[])

You can´t. Either you use IJW or you use P/Invoke...

There are some very few scenarios, where you will want to write a DllImport
function even though you compile with /clr. This can be the case when you
want to use some special marshalling features: e.g. Marshalling SafeHandles
or marshalling delegates as function pointers. In these cases you can still
use native types in the function declaration. The easiest way to get such a
P/Invoke function is to just copy the native declaration and remove native
modifiers like __declspec(dllimport) or extern "C". The code below can be
compiled with cl /clr /LD xxx.cpp

// xxx.cpp
#include "windows.h"

using System::Runtime::InteropServices::DllImportAttribute;

public ref class Test
{
public:
[DllImport("PInvokeTest.dll")] int TestPInvokeFunc(LPSTR szFileName, LPSTR
szResult[]);
};
 
J

Jochen Kalmbach [MVP]

Hi Marcus!
I did not read anything in the Asfar's question that mentioned /clr:safe
compilation.

On the other hand, I did not read anything *only* about "/clr"...

This is very similar to what I have thought, but if we talk about C++/CLI it
is wrong for 99% of the cases.

This code is guaanteed to work correctly in C++/CLI:

HANDLE h = ::CreateFile(...); // Win32 API CreateFile
if (h = NULL)
printf("error code: %d", GetLastError());

This is true, because in C++/CLI, the automatically generated P/Invoke
metadata for functions in other DLLs DOES have the lasterr flag.

GetLastError() only fails if you try to use report errors via SetLastError
in your own native functions. Automatically generated P/Invoke metadata for
native function living in the same PE File as the caller do NOT have the
lasterr flag.

If the C++/CLI generated DllImport with SetLastError, then you *must not
use* "GetLastError()" instead you must use
Marshal.GetLastWin32Error

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
 
M

Marcus Heege

Hi Jochen
If the C++/CLI generated DllImport with SetLastError, then you *must not
use* "GetLastError()" instead you must use
Marshal.GetLastWin32Error

No. Your statement is wrong. You can still use GetLastError. I have not
expected GetLastError to work either, but it does work, too. I tried it very
precisely and single stepped through the managed -> unmanaged thunks to make
sure that and it surely works, too.

Marcus Heege
 
J

Jochen Kalmbach [MVP]

Hi Marcus!
No. Your statement is wrong. You can still use GetLastError. I have not
expected GetLastError to work either, but it does work, too. I tried it very
precisely and single stepped through the managed -> unmanaged thunks to make
sure that and it surely works, too.

Have you a point to the documentation? I couldn´t find any...
It would really be helpfull if this feature is well documented...

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
 
M

Marcus Heege

I haven't found a documentation either. The only thing I can tell you is
that it works the way I told you and that C++/CLI Interop heavily relies on
that fact.

Marcus Heege
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top