Link to home
Start Free TrialLog in
Avatar of Mensana
Mensana

asked on

Function that has a DllImport Custom Attribute cannot return an instance of a class!?!

Hello Experts,

I am trying to call GetLargestConsoleWindowSize from a Managed C++ class. I have created the prototype in my managed code like this:

[DllImport("KERNEL32.DLL", EntryPoint="GetLargestConsoleWindowSize")]
static COORD GetLargestConsoleWindowSize(int hConsoleOutput);

COORD is a structure declared like this

[StructLayout(LayoutKind::Sequential)]
private __value struct COORD
{
   short x;
   short y;
};

which mimics the API structure used by the API's GetLargestConsoleWindowSize function.

The problem is that when I try to compile I get an error C3385 that tells me that "a function that has a DllImport Custom Attribute cannot return an instance of a class". I know for sure that the C# compiler doesn't have this limitation. I tried to find any references to this error on the net, but couldn't find any.
Did anyone else bumped into it?

Regards,
Eddie
Avatar of Svetlin_Panayotov
Svetlin_Panayotov

Yes - I had similar problems.I don't know if that's the best way around - but you can probably do something like:

static IntPtr* GetLargestConsoleWindowSize(int hConsoleOutput);

and then
Marshal::PtrToStructure(pResult,COORD);

Svetlin
Avatar of Mensana

ASKER

Hello Svetlin, AlexFM,

Both your postings point to the same way of doing interop: IJW. It's an untersting approach and I will split the points between you. However, before I do that I need you to help me make this work.

The "GetLargestConsoleWindowSize" API function happens to return a COORD object by value (COORD has two shorts, so it's really 4 bytes, just like its address).
Therefore I wrote my wrapper like this:

static COORD MyOtherGetLargestConsoleWindowSize( int nWindowHandle )
{
   COORD objWindowSize;

   IntPtr pCoord = GetLargestConsoleWindowSize( GetStdHandle(STD_OUTPUT_HANDLE) );

   Marshal::PtrToStructure( pCoord, __box(objWindowSize) );

   return objWindowSize;
};

This compiles fine except I cannot include the "windows.h" header. My project is a Visual C++.NET class library (generates a managed C++ .dll) and when I include the header, ahead of all my "using namespace..." directives (as AlexFM's suggested article tells you to do) I get a bunch of errors that are somewhere else. I comment out the “#include” directive and the call to GetLargestConsoleWindowSize and everything compiles.
I can't help but feel that I am doing something wrong. IJW it is suppose to be simple, right?
The example in Nish’s article is not very good because it deals with a passed in parameter (string) and not a returned value. With returned values things are different because they are allocated in the called function and not by the caller. Especially when a structure is returned by value and not by reference.

Just for the sake of this discussion, here is my workaround:
I wrote my own unmanaged .dll and I wrapped the call like this:

#include "windows.h"

extern "C" UNMANAGEDDLL_API BOOL MyGetLargestConsoleWindowSize( HANDLE hConsoleOutput, COORD *pobjCoordinates );
{
   // return value
   BOOL bReturn = TRUE;

   // check pointer to be initialized
   if( pobjCoordinates != NULL )
   {
      // pointer is initialized => get the largest console window size
      // by calling the real API function
      *pobjCoordinates = GetLargestConsoleWindowSize( GetStdHandle(STD_OUTPUT_HANDLE) );

      // check if structure was initialized
      if( (pobjCoordinates->X == 0) || (pobjCoordinates->Y == 0) )
         // structure not initialized => signal error
         bReturn = FALSE;
   }
   else
   {
      // the pointer is not initialized => signal error
      bReturn = FALSE;
   }

   // return
   return bReturn;
};

Now I can use P/Invoke because MyGetLargestConsoleWindowSize doesn’t return a class or structure anymore. The downside is that when calling it two .dlls must be loaded: my unmanaged dll followed by kernel32.dll that hosts the real GetLargestConsoleWindowSize API function.

What compilation errors do you have using IJW? I created new managed C++ Clicc Library project and added windows.h to it:

// test1.h

#pragma once

#include <windows.h>

using namespace System;

namespace test1
{
    public __gc class Class1
    {
        // TODO: Add your methods for this class here.
    };
}

It compiles successfully.
IJW is not so simple because of possible name conflicts. We need to see your code and error messages.
Avatar of Mensana

ASKER

I figured it out. I had lots of name collisions not reported by the compiler. Since the initial path was P/Invoke I had lots of declarations such as:

[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleScreenBufferSize")]
static int SetConsoleScreenBufferSize(int hConsoleOutput, COORD dwWindowSize );

[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleWindowInfo")]
static int SetConsoleWindowInfo(int hConsoleOutput, int bAbsolute, const SMALL_RECT *srWindowSize );

Linking the "windows.h" header, functions with similar names were imported, hence the collision. Note to self: NEVER MIX P/INVOKE WITH IJW!

I just had to rename all my imports and everything compiled just fine.

[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleScreenBufferSize")]
static int MySetConsoleScreenBufferSize(int hConsoleOutput, MyCOORD dwWindowSize );

[DllImport("KERNEL32.DLL", EntryPoint="SetConsoleWindowInfo")]
static int MySetConsoleWindowInfo(int hConsoleOutput, int bAbsolute, const MySMALL_RECT *srWindowSize );

In the end I had to modify the code and use API's GetLargestConsoleWindowSize directly:

COORD objScreenBufferSize = GetLargestConsoleWindowSize( GetStdHandle(STD_OUTPUT_HANDLE) );

I guess that the Marshal class really comes in handy when you deal with dynamically allocated objects. With this feature I can say that Managed C++ is better than C# for those who are trying to port legacy code under .NET.

Thanks,
Eddie
>> NEVER MIX P/INVOKE WITH IJW!
Right, managed C++ programmer doesn't need PInvoke.
Avatar of Mensana

ASKER

Please close this question and return points in my pool. Points were awarded.
You can split points here instead of opening new questions.
If you want to refund your points ask Community Support.
Avatar of Mensana

ASKER

Oh, I didn't know this - probabely a new feature I wasn't aware of. Well, next time I will try it. Since Svetlin "cashed" his points, you are welcome to get yours and I hope this question will be closed by the guys from Support.
Ask community support to close this question. You need to post request here:
https://www.experts-exchange.com/Community_Support/

I add note to "Points for" question.
ASKER CERTIFIED SOLUTION
Avatar of modulo
modulo

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial