0

I have this function which uses the winscard.h function SCardGetReaderDeviceInstanceId.

    WCHAR SCardInstanceId(SCARDCONTEXT phContext, LPTSTR szReaderName) {
    WCHAR    szDeviceInstanceId[256];
    DWORD    cchDeviceInstanceId = 256;

    long lReturn = SCardGetReaderDeviceInstanceId(phContext, szReaderName, szDeviceInstanceId, &cchDeviceInstanceId);

    if (lReturn != SCARD_S_SUCCESS) {
        cout << "Failed SCardGetReaderDeviceInstanceId, errorcode: " << std::hex << std::setfill('0') << std::setw(8) << lReturn << endl;
        exit(1);
    }
    
    return szDeviceInstanceId;
}

But it gives me a wierd error message on the return line.

E0120   return value type does not match the function type

and

Error   C2440   'return': cannot convert from 'WCHAR [256]' to 'WCHAR'

What can be the issue here? and how do I solve it?

I can't change the function type to WCHAR [256], it that even a type?

  • 1
    `WCHAR` is just `wchar_t`, which is a character type. But you also try to return 256 chars instead if just one. – BoP Sep 22 '22 at 13:48
  • 2
    The bigger issue seems to be that you are unable to recognize an array, know what it is, how it's different, etc. – sweenish Sep 22 '22 at 13:51
  • 1
    `WCHAR[256]` is an array of 256 WCHAR, and `WCHAR` is a single WCHAR. Both are types. Once you have the difference figured out the second problem will be that you cannot return an array from a function in C++. I'm thinking you need to spend a bit more time of the basics of C++, – john Sep 22 '22 at 13:56
  • Let the caller pass the array: void SCardInstanceId(SCARDCONTEXT phContext, LPCTSTR szReaderName, LPTSTR szReturnValue, DWORD dwReturnValueSize). Further improve by considering to return an error code so the caller can decide how to report a mishap. – Hans Passant Sep 22 '22 at 13:57
  • Assuming you want to return the entire content of `deviceInstanceId` then best option usually is to let the user allocate the buffer and provide it to the function via parameter (including size information) like `SCardInstanceId(* context, * readerName, size_t bufferLength, WCHAR buffer[])` – if you insist on allocating within the function there are several options, e.g. a `static` array or using `malloc()` – in both cases you should document how to use the array (freeing not legal in first case, required in second case). – Aconcagua Sep 22 '22 at 14:07
  • Side note: I wouldn't adopt Hungarian notation any more, seems to have become dated, only Microsoft still adheres to (well, usually not a good reference anyway) – not even in MS context (unless forced so by coding guide lines you're required to follow, of course). – Aconcagua Sep 22 '22 at 14:12
  • 2
    You tagged C++ and use C++ features internally anyway – why don't you then use a `std::basic_string` as return value instead? That would relieve you from all that trouble. – Aconcagua Sep 22 '22 at 14:14
  • 1
    @Aconcagua `std::basic_string` -> aka `std::wstring`, as `WCHAR` is just an alias for `wchar_t`, and `std::wstring` is an alias for `std::basic_string` – Remy Lebeau Sep 22 '22 at 16:19
  • @RemyLebeau Ah, indeed – it's **W**CHAR, not **T**CHAR – somehow didn't notice. Again and again those dumb MS aliases without any value (well, TCHAR would yet be acceptable, but the others...). – Aconcagua Sep 22 '22 at 16:37

1 Answers1

0

The function return type is WCHAR. You are trying to return a C-style array of WCHARs. This isn't possible. You can instead return an std::wstring object, so your code would look like this:

#include <string>
std::wstring SCardInstanceId(SCARDCONTEXT phContext, LPTSTR szReaderName) {
    std::wstring szDeviceInstanceId;
    DWORD cchDeviceInstanceId = 255;
    szDeviceInstanceId.resize(cchDeviceInstanceId);
    // I think it is safer to resize to 255 chars as it is implementation defined if the internal array has the null-terminator.
    // If it does and you resize to 256, you will end up with a 257-element array...

    long lReturn = SCardGetReaderDeviceInstanceId(phContext, szReaderName, szDeviceInstanceId.data(), &cchDeviceInstanceId); // from c++17
    // long lReturn = SCardGetReaderDeviceInstanceId(phContext, szReaderName, &szDeviceInstanceId[0], &cchDeviceInstanceId); 
    // before c++17 'data' returns const reference, so SCardGetReaderDeviceInstanceId couldn't modify the buffer (compilation error)

    szDeviceInstanceId.resize(cchDeviceInstanceId-1); // shrink the string length to the length actually occupied by characters
    // -1 because cchDeviceInstanceId is length including null-terminator (according to docs), and resize expects length excluding null
    

    if (lReturn != SCARD_S_SUCCESS) {
        cout << "Failed SCardGetReaderDeviceInstanceId, errorcode: " << std::hex << std::setfill('0') << std::setw(8) << lReturn << endl;
        exit(1);
    }
    
    return szDeviceInstanceId;
}

If the 4th parameter of SCardGetReaderDeviceInstanceId didn't output any data, you could just pass szDeviceInstanceId.size()+1 (+1 for null). Also note, that the buffer will be allocated on the heap, unless SSO occurs (which is implementation defined, yet I don't think it would occur in any implementation in case of this long string).
As you can see the code is somewhat complex because of the differences in the management of the null-terminator. To make it simpler (but less beautiful in the C++ context) you can use std::wstring only to return the string:

std::wstring SCardInstanceId(SCARDCONTEXT phContext, LPTSTR szReaderName) {
    WCHAR    szDeviceInstanceId[255];
    DWORD    cchDeviceInstanceId = 255;

    long lReturn = SCardGetReaderDeviceInstanceId(phContext, szReaderName, szDeviceInstanceId, &cchDeviceInstanceId);

    if (lReturn != SCARD_S_SUCCESS) {
        cout << "Failed SCardGetReaderDeviceInstanceId, errorcode: " << std::hex << std::setfill('0') << std::setw(8) << lReturn << endl;
        exit(1);
    }
    
    return std::wstring(szDeviceInstanceId);
}

Please note, that doing it like this might be a bit less performant as you have to allocate both WCHAR[255] array and std::wstring array. It isn't going to matter most of the time, but it might be worth using the first method in a very performance-sensitive context.

You can return the array in the C manner as well, however it isn't recommended to do that in C++.

michuu
  • 315
  • 4
  • 10