none
Using a C dll function which passes back a handle to an "Object" RRS feed

  • Question

  • I am trying to use a dll written in C.

    I load the dll with LoadLibrary (DllPath), then call function with GetProcAddress; but when I want to start a "conversation", the function called passes back a handle to an "object" (as described in the limited documentation).I have tried receiving it into a long or Int32. But failed.

    This qusetion has been asked by Stephen R Hill with the same topic ten years ago, and solved by Peter Ritchie.  It is a pity that I can not get access to Stephen R Hill for no email or anyother imformation left by him.

    Does anyone have any idea how I progress with this? If I need to clarify further please let me know!

    Many thanks,


    • Edited by Yingjia Zhu Wednesday, June 12, 2019 8:54 AM
    Wednesday, June 12, 2019 8:50 AM

Answers


  •      BOOL TcAccessLoggedOn();,
         which returns 1 if you are and 0 if you are not.

    I do just like the doc tells, use BOOL and it works. as for the conversation, it does not.


    As Darran already pointed out, you are trying to use C++ types when you are
    using a DLL apparently written in C.

    bool is a C++ type.

    BOOL is a Windows API/SDK data type.

    A BOOL will be 0 or 1.

    A bool will be false or true.

    You can get away with assigning a BOOL variable to a bool.

    If you assign a BOOL with a value of 0 to a bool that bool will contain false.
    If you assign a BOOL with a value of 1 to a bool that bool will contain true.

    However you can't get away with doing that same kind of interchanging of types
    with string types. "string" is a C++ class type:

    "A type that describes a specialization of the template class basic_string 
    with elements of type char as a string."

    A C library (DLL) will *not* be using any types which are specific to C++.

    If you try to force the compiler to ignore the defined types by casting, 
    typedefs, etc. you will almost always have issues - serious issues.

    If the DLL has functions that return a string then it almost certainly will
    be a C-style string: a nul-terminated character array.

    Furthermore if you are compiling C++ code (as you obviously are), then any
    exported functions from the C DLL must be declared as extern "C" to avoid the 
    name mangling (or decorating) that C++ does.

    Care must also be taken to ensure that the DLL supports both wide and narrow
    strings and characters. If it doesn't then you have to ensure that you are
    only using the correctly defined types, especially when doing a Unicode-enabled
    build if the DLL doesn't support Unicode.

    - Wayne

    • Marked as answer by Yingjia Zhu Monday, June 17, 2019 8:17 AM
    Thursday, June 13, 2019 4:22 AM
  • >If the DLL has functions that return a string then it almost certainly will
    >be a C-style string: a nul-terminated character array.

    Just to avoid any misunderstanding, you can of course assign a C string to a
    C++ std::string. However trying to use a std::string as an argument to a function
    that expects/requires a C string will not work simply by casting or defining the
    prototype/declaration to accept a std::string. So a declaration such as this:

    >TCConversation TcAccessOpenConversation(string Topic);

    will not work if this is a function in the C DLL, as an argument of type string
    cannot exist since "string" is a C++ class type.

    If the documentation for the DLL refers to a string then it will mean a
    C style string not a C++ std::string.

    >Furthermore if you are compiling C++ code (as you obviously are), then any
    >exported functions from the C DLL must be declared as extern "C" to avoid the
    >name mangling (or decorating) that C++ does.

    For more on this see:

    extern "C" and extern "C++" function declarations
    https://docs.microsoft.com/en-us/cpp/cpp/extern-cpp?view=vs-2019#extern-c-and-extern-c-function-declarations

    Name Decoration
    https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/name-decoration?view=vs-2019

    >Care must also be taken to ensure that the DLL supports both wide and narrow
    >strings and characters. If it doesn't then you have to ensure that you are
    >only using the correctly defined types, especially when doing a Unicode-enabled
    >build if the DLL doesn't support Unicode.

    This means that you have to ensure that the exported DLL functions exist in
    versions that will accept narrow (char} arguments, as well as versions
    that will accept wide (wchar_t) arguments. If the DLL only supports one or
    the other then you have to ensure that you are passing the correct type.
    That may also mean that you cannot/should not use the macros which map to
    either wide or narrow characters or strings depending on the build properties.

    You can use tools to see what the DLL exports, as well as what decorated and
    undecorated names are used. These tools include dumpbin, depends.exe (Dependency
    Walker), undname.exe, Dependencies.exe

    - Wayne

    • Marked as answer by Yingjia Zhu Monday, June 17, 2019 8:08 AM
    Thursday, June 13, 2019 7:17 AM

All replies

  • Hello,

    I'm sorry, but your questions lacks Information in every sentence. What is a "conversation"? Why is it wrong what a function Returns since it is already described in a documentation? Show us the source code why you failed to receive something into a Long or Int32. Who is "Stephen R Hill" and where is the link to the same question topic that he asked 10 years ago? What was solved by Peter Ritchie and why is it not the solution to your question?

    Regards, Guido

    Wednesday, June 12, 2019 9:03 AM
  • Hi,

    According to the link, I wonder if you want to use the dll in C#. If so, I will help you move thread to Visual C# Language forum for better support.

    Best Regards,

    Jeanine Zhang

    Wednesday, June 12, 2019 9:45 AM
    Moderator
  • Hello,

    I'm sorry, but your questions lacks Information in every sentence. What is a "conversation"? Why is it wrong what a function Returns since it is already described in a documentation? Show us the source code why you failed to receive something into a Long or Int32. Who is "Stephen R Hill" and where is the link to the same question topic that he asked 10 years ago? What was solved by Peter Ritchie and why is it not the solution to your question?

    Regards, Guido

    The link to the same question topic is pasted:

    https://social.msdn.microsoft.com/Forums/en-US/1b02b0ae-890d-45b1-a038-5127fcd2aa5a/using-a-c-dll-function-which-passes-back-a-handle-to-an-quotobjectquot?forum=csharplanguage

    Thanks  for your patience! I think you can get more imformation from the link above.

    I'm sorry that I'm not very good at API programing, even describe the problem I confront.

    TCConversation TcAccessOpenConversation(string Topic);

    The doc just says that TcConversation is an object that identifies this conversation, and acts as a handle that represents an object, this handle is used in future calls concerned with this object.

    And the error message I got is "Unable to read memory!".

    I don't know how to put my source code here, Just paste?


    Wednesday, June 12, 2019 9:48 AM
  • Hi,

    According to the link, I wonder if you want to use the dll in C#. If so, I will help you move thread to Visual C# Language forum for better support.

    Best Regards,

    Jeanine Zhang

    Please don't do this! Are there any difference for C++ and C# to use a DLL?

    The best solution I think is help me get access to Stephen R Hill, who have great experience on solving this problem!

    Thanks all the same!

    Best Regards!

    Yingjia Zhu

    Wednesday, June 12, 2019 10:11 AM
  • The best solution I think is help me get access to Stephen R Hill, who have great experience on solving this problem!

    I think no-one can give you some "access" like email or so to this person. When you look his profile, there is nothing mentioned and it seems that he is not active.

    But you can try to post a response to his 10 year old question and ask him for the solution you need. Maybe he gets an alert email when someone responds to his question. Maybe you are lucky.

    Regards, Guido

    Wednesday, June 12, 2019 11:03 AM
  • The best solution I think is help me get access to Stephen R Hill, who have great experience on solving this problem!

    I think no-one can give you some "access" like email or so to this person. When you look his profile, there is nothing mentioned and it seems that he is not active.

    But you can try to post a response to his 10 year old question and ask him for the solution you need. Maybe he gets an alert email when someone responds to his question. Maybe you are lucky.

    Regards, Guido

    That sounds perfect! I'll try though it's ten years ago!

    Thanks for your suggestions!

    Regards, Yingjia Zhu

    Wednesday, June 12, 2019 12:31 PM
  • Source code is as below:

    HINSTANCE   h = LoadLibrary(_T("C:\\Users\\manager\\Documents\\Visual Studio 2013\\Projects\\GetRawPoint\\Debug\\TcAccess.dll"));
    typedef void(_stdcall *FunPtr_init)();
    typedef bool(_stdcall *FunPtr_LoggedOn)();

    typedef LPCTSTR(_stdcall *FunPtr_GetErrorMessage)();
    typedef INT32( *FunPtr_Tc_Open)(LPCTSTR Conversation_name);
    typedef long( *FunPtr_TC_Set)(INT32 Conversation_name, LPCTSTR item_name, LPCTSTR Value);
    typedef long(*FunPtr)(INT32 Conversation_name, long first_point, long numpoints, long* points);
    typedef string(*FunPtr2)(INT32 Conversation_name, LPCTSTR item_name);

    string RawFile;
    string RawPath;
    string FileAuthor;
    string New_File;
    if (h == NULL)
    {
    FreeLibrary(h);
    cout << "Load lib error!\n";

    }
    else
    {
    FunPtr_init funPtr_init = (FunPtr_init)GetProcAddress(h, "TcAccessInit");
    FunPtr_LoggedOn funPtr_LoggedOn = (FunPtr_LoggedOn)GetProcAddress(h,"TcAccessLoggedOn");
    FunPtr_GetErrorMessage funPtr_GetErrorMessage = (FunPtr_GetErrorMessage)GetProcAddress(h, "TcAccessErrorMessage");
    FunPtr_Tc_Open funPtr_Tc_Open = (FunPtr_Tc_Open)GetProcAddress(h, "TcAccessOpenConversation");
    FunPtr_TC_Set funPtr_TC_Set = (FunPtr_TC_Set)GetProcAddress(h,"TcAccessSet");
    FunPtr2 funPtr2 = (FunPtr2)GetProcAddress(h, "TcAccessGet");
    FunPtr funPtr = (FunPtr)GetProcAddress(h, "TcAccessGetRawPoints");   //   youFuntionNameAlias   
    //   youFuntionName    
    if (funPtr_init != NULL&&funPtr_Tc_Open != NULL&&funPtr2 != NULL&& funPtr != NULL)
    {
    funPtr_init();
    bool is_login = funPtr_LoggedOn();
    cout << is_login<<endl;
    INT32 c1;
    c1 = funPtr_Tc_Open(_T("RAW"));
    if (c1 != 0){
    string PointNum = funPtr2(c1, _T("AD_NUM_POINTS"));
    int Point_Num = atoi(PointNum.c_str());
    cout << Point_Num << endl;
    long* point_arry = new long[Point_Num];
    long Point_Copied = funPtr(c1, 0, Point_Num, point_arry);
    FreeLibrary(h);

    }
    else{
    LPCTSTR _error = funPtr_GetErrorMessage();
    cout << "Failed to open conversation!" << _error;
    FreeLibrary(h);
    }
    }
    else{
    cout << "Get process error!\n";
    cout << "Get last error" << GetLastError();
    FreeLibrary(h);
    }
    return 0;
    }

    • Edited by Yingjia Zhu Wednesday, June 12, 2019 12:40 PM
    Wednesday, June 12, 2019 12:38 PM
  • When you are doing this sort of thing, you must make sure that your typedefs for the function pointers match EXACTLY. There is not much room for changing types.

    First, you have a mix of function pointers marked as __stdcall, but then others not marked as __stdcall. It is very rare that you get this kind of mix without a good reason. Windows only does this when on the occasional variable argument function as an example.

    What's more, you really need to get this correct otherwise this will lead to crashes since there will either be too much cleanup or not enough cleanup.

    What's more, you seem to have some problematic types in the return types for these pointers. There is a bool and string there. Both of these types are C++ types, so if this is a C DLL like you say, then there is no way these could be returned from these functions. If you are doing this kind of thing as an attempt to make it easier for you thinking that the compiler will automatically convert things, it won't. What you are doing there is the equivalent of a reinterpret_cast, the compiler will try to take what is really returned and will try to interpret it as the type you specify.

    Before you go on, you need to go through the documentation and fix your function pointers. Make sure everything matches, return types, calling conventions and parameters. Once this has been done, try again.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Wednesday, June 12, 2019 2:17 PM
  • According to the link, I wonder if you want to use the dll in C#. If so, I will help you move thread to Visual C# Language forum for better support.

    Please don't do this!

    Please do allow that. That other thread is in the C# forum. That is where the questions belong. You do not state explicitly that you are using C# and you really should make that clear. Having the question in the C# forum helps a lot. The code you are writing is C#, correct?



    Sam Hobbs
    SimpleSamples.Info

    Wednesday, June 12, 2019 8:10 PM
  • when I want to start a "conversation", the function called passes back a handle to an "object" (as described in the limited documentation).

    Apparently a conversation is something that the DLL does so we would not know what that is without knowing the DLL and what it does. Correspondingly, I do not know what documentation you are referring to. If you mean the documentation of the DLL then you need to get help from the developer.

    Is this for TeamCity? If so then there are many samples.



    Sam Hobbs
    SimpleSamples.Info

    Wednesday, June 12, 2019 8:21 PM
  • Where did you get this DLL?  One almost never calls a DLL this way, by fudging a bunch of GetProcAddress calls.  Instead, you should have a .h file and a .lib file that describe the DLL, so that you call the functions just like you would call a Windows API function.  Then, the operating system loader will automatically load the DLL at run-time.  Assuming you have the proper .h file, that would eliminate all your type concerns.

    It looks like the TcOpen call returns an object that gets passed to the other calls.  That's commonly done to make a C-compatible interface to a C++ library.  However, in that case, an INT32 is not going to be enough on a 64-bit system.  The object returned will be a pointer, which requires a 64-bit type.

    By the way, "if (h == NULL)", then it is silly to call FreeLibrary(h).


    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    • Marked as answer by Yingjia Zhu Monday, June 17, 2019 8:08 AM
    • Unmarked as answer by Yingjia Zhu Monday, June 24, 2019 1:10 AM
    Wednesday, June 12, 2019 8:28 PM
  • Where did you get this DLL?  One almost never calls a DLL this way, by fudging a bunch of GetProcAddress calls.  Instead, you should have a .h file and a .lib file that describe the DLL, so that you call the functions just like you would call a Windows API function.  Then, the operating system loader will automatically load the DLL at run-time.  Assuming you have the proper .h file, that would eliminate all your type concerns.

    It looks like the TcOpen call returns an object that gets passed to the other calls.  That's commonly done to make a C-compatible interface to a C++ library.  However, in that case, an INT32 is not going to be enough on a 64-bit system.  The object returned will be a pointer, which requires a 64-bit type.

    By the way, "if (h == NULL)", then it is silly to call FreeLibrary(h).


    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    I get the dll from PerkinElmer to get the raw data of chromatography(They do not know anything about the dll!). It seems that there is no .h file and no .lib file, only a doc that introduces the functions and the file structure briefly, and the demo is written by MS Visual Basic(The dll can be interfaced with C++ and MS Visual Basic).  I have no choice but call DLL this way, which confuses me a lot. 

    Thanks a lot for pointing out the mistakes I've made.

    Sincerely,

    Yingjia Zhu 


    • Edited by Yingjia Zhu Thursday, June 13, 2019 1:06 AM
    Thursday, June 13, 2019 1:02 AM

  • First, you have a mix of function pointers marked as __stdcall, but then others not marked as __stdcall.


    What's more, you seem to have some problematic types in the return types for these pointers. 

    I've marked all the function pointers as _stdcall, but the compiler tells"The value of ESP was not properly saved across a function call." That is a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention, I think. But according to the doc introdution:

         Before any other calls to TcAccess it must be properly initialized to load the string resources for error
         messages in the local language. This is done by calling:
         void TcAccessInit(); .
         If you are not logged onto TotalChrom, you have only read access to files. If there is an interactive
         TotalChrom application running on the workstation where your program is running, you are logged on. This can be proven at run time by the call:
         BOOL TcAccessLoggedOn();,
         which returns 1 if you are and 0 if you are not.

    I do just like the doc tells, use BOOL and it works. as for the conversation, it does not.

         A conversation begins with
         TcConversation TcAccessOpenConversation(LPCTSTR topic_name);.
         A TcConversation is a handle that represents an object. This handle is used in future calls concerned with
         this object. If the topic is not found, the handle returned is zero.

    Best Regards!

    Yingjia Zhu

    • Edited by Yingjia Zhu Thursday, June 13, 2019 2:07 AM
    Thursday, June 13, 2019 1:53 AM
  • Please do allow that. 

    The code you are writing is C#, correct?

    The code I'm writing is C++, and I'm quite sure!

    I have no .h file and .lib file, so I have to load DLL like that way. If this problem can be solved with C# 10 years ago, I think C++ can also make it today.

    Regards

    Yingjia Zhu


    • Edited by Yingjia Zhu Thursday, June 13, 2019 2:08 AM
    Thursday, June 13, 2019 2:02 AM

  •      BOOL TcAccessLoggedOn();,
         which returns 1 if you are and 0 if you are not.

    I do just like the doc tells, use BOOL and it works. as for the conversation, it does not.


    As Darran already pointed out, you are trying to use C++ types when you are
    using a DLL apparently written in C.

    bool is a C++ type.

    BOOL is a Windows API/SDK data type.

    A BOOL will be 0 or 1.

    A bool will be false or true.

    You can get away with assigning a BOOL variable to a bool.

    If you assign a BOOL with a value of 0 to a bool that bool will contain false.
    If you assign a BOOL with a value of 1 to a bool that bool will contain true.

    However you can't get away with doing that same kind of interchanging of types
    with string types. "string" is a C++ class type:

    "A type that describes a specialization of the template class basic_string 
    with elements of type char as a string."

    A C library (DLL) will *not* be using any types which are specific to C++.

    If you try to force the compiler to ignore the defined types by casting, 
    typedefs, etc. you will almost always have issues - serious issues.

    If the DLL has functions that return a string then it almost certainly will
    be a C-style string: a nul-terminated character array.

    Furthermore if you are compiling C++ code (as you obviously are), then any
    exported functions from the C DLL must be declared as extern "C" to avoid the 
    name mangling (or decorating) that C++ does.

    Care must also be taken to ensure that the DLL supports both wide and narrow
    strings and characters. If it doesn't then you have to ensure that you are
    only using the correctly defined types, especially when doing a Unicode-enabled
    build if the DLL doesn't support Unicode.

    - Wayne

    • Marked as answer by Yingjia Zhu Monday, June 17, 2019 8:17 AM
    Thursday, June 13, 2019 4:22 AM
  • >If the DLL has functions that return a string then it almost certainly will
    >be a C-style string: a nul-terminated character array.

    Just to avoid any misunderstanding, you can of course assign a C string to a
    C++ std::string. However trying to use a std::string as an argument to a function
    that expects/requires a C string will not work simply by casting or defining the
    prototype/declaration to accept a std::string. So a declaration such as this:

    >TCConversation TcAccessOpenConversation(string Topic);

    will not work if this is a function in the C DLL, as an argument of type string
    cannot exist since "string" is a C++ class type.

    If the documentation for the DLL refers to a string then it will mean a
    C style string not a C++ std::string.

    >Furthermore if you are compiling C++ code (as you obviously are), then any
    >exported functions from the C DLL must be declared as extern "C" to avoid the
    >name mangling (or decorating) that C++ does.

    For more on this see:

    extern "C" and extern "C++" function declarations
    https://docs.microsoft.com/en-us/cpp/cpp/extern-cpp?view=vs-2019#extern-c-and-extern-c-function-declarations

    Name Decoration
    https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/name-decoration?view=vs-2019

    >Care must also be taken to ensure that the DLL supports both wide and narrow
    >strings and characters. If it doesn't then you have to ensure that you are
    >only using the correctly defined types, especially when doing a Unicode-enabled
    >build if the DLL doesn't support Unicode.

    This means that you have to ensure that the exported DLL functions exist in
    versions that will accept narrow (char} arguments, as well as versions
    that will accept wide (wchar_t) arguments. If the DLL only supports one or
    the other then you have to ensure that you are passing the correct type.
    That may also mean that you cannot/should not use the macros which map to
    either wide or narrow characters or strings depending on the build properties.

    You can use tools to see what the DLL exports, as well as what decorated and
    undecorated names are used. These tools include dumpbin, depends.exe (Dependency
    Walker), undname.exe, Dependencies.exe

    - Wayne

    • Marked as answer by Yingjia Zhu Monday, June 17, 2019 8:08 AM
    Thursday, June 13, 2019 7:17 AM
  • I've marked all the function pointers as _stdcall, but the compiler tells"The value of ESP was not properly saved across a function call." That is a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention, I think.

    I apologise if what I wrote implied anything. But I was saying that you have a mix of __stdcall and non __stdcall and it is very rare for there to be a mix like this without a reason. I wasn't saying that you should put everything to __stdcall, I meant to say that you should figure out what the calling convention really is and use the correct one. This means that you could also make them all the C calling convention too. (I.e. remove __stdcall.)

    In this kind of situation I would be very surprised if there is no mention of the calling convention in the documentation that you are using. Since you have to generate these function prototypes off of this document then this is vital information.

    The ESP not being saved properly across a function call is a symptom of using the wrong calling convention. But unfortunately, you can get this kind of error through a bug by overwriting part of the stack too.

    I do just like the doc tells, use BOOL and it works.

    No, you used bool. The BOOL is a typedef of int where bool is a built in type. The big important difference here is that BOOL, being a typdef of int is 32 bits, but bool is only 8 bit.

    This adds a bit of complexity because technically any value which isn't 0 will be seen as true by the compiler. If TcAccessLoggedOn follows the Windows API convention of returning 0 being failure or false and any other value being success or true, then it could return a value > 255. If the right value is returned, the bottom 8 bits would all be 0 but the value returned is not 0. This would result in a success or true result, but reading it as the C++ bool type would return a failure or false result.

    To be honest, if you are having this type of trouble, you should put the compiler into C mode and get it to work in C before using it in C++.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Thursday, June 13, 2019 2:33 PM
    Thursday, June 13, 2019 2:19 PM
  • No, you used bool. The BOOL is a typedef of int where bool is a built in type. The big important difference here is that BOOL, being a typdef of int is 32 bits, but bool is only 8 bit.

    Oh, now I see! I used to believe that  the BOOL and bool are the same! I will spend more time on figuring out the differences of type define between C and C++.

    As for the name mangling that C++ does, I will also refer to some papers for details.

    Thanks a lot!

    Sincerely!

    Yingjia Zhu

    Monday, June 17, 2019 3:43 AM
  • As for the name mangling that C++ does, I will also refer to some papers for details.

    We usually don't need to know much about name mangling. The details of what the compiler does varies depending on the compiler; the C++ standard does not specify how it must be done. We just need to know what has already been said; that C++ function names are named in a manner that is not compatible with C. If we had to we might be able to find the mangled name but usually that is not practical.



    Sam Hobbs
    SimpleSamples.Info

    Monday, June 17, 2019 4:08 AM
  • >If the DLL has functions that return a string then it almost certainly will
    >be a C-style string: a nul-terminated character array.

    It really takes me long time to understand the doc of DLL given by the company. Just like you said, now I know that the C-style string is just a nul-teminated char array, and it seems that my souce code happens to be right here.

    it looks like the TcOpen call returns an object that gets passed to the other calls. That's commonly done to make a C-compatible interface to a C++ library. However, in that case, an INT32 is not going to be enough on a 64-bit system. The object returned will be a pointer, which requires a 64-bit type.
    —— Tim Roberts

    The mistake I made,which Tim Roberts told me earlier and I did not understand at that time, that INT32 is not enough  to get the object returned pointer on 64-byte system, it should be a 64-byte type. It's INT64, I know after your kindly interpritation with patience. It seems that the returned object pointer type varies as the compiler.

    By now, the problem is solved. To be honest, all the replies help me a lot, especially for your reply, thanks. But as Tim firstly told the answer and meets the topic, So I decide to make his reply as the answer.

    Sincerely

    Yingjia Zhu



    Monday, June 17, 2019 8:07 AM
  • Care must also be taken to ensure that the DLL supports both wide and narrow
    strings and characters. If it doesn't then you have to ensure that you are
    only using the correctly defined types, especially when doing a Unicode-enabled
    build if the DLL doesn't support Unicode.

    - Wayne

    I do apologise that I made my conclusion too early.

    When I come back to program the code, I found that the INT64 can always get a handle number (not zero) no matter the function return is correct or not. (I do not know why!)

    I use  type INT64 to start a conversation and get the error message immediately, the error message is still there in unreadable code, and I can't do anything with that handle number. Then I use the VS watch to make the error message readable, whick tells: "Topic R not found". It seems that the function only get the first letter of the C-type string topic, so I realise that the dll may not support unicode.

    Now I still use INT32 but with multi-byte character set, and everything goes well. Thanks for all of you!

    Sincerely,

    Yingjia Zhu

    Monday, June 24, 2019 2:27 AM

  •  It seems that the function only get the first letter of the C-type string topic, so I realise that the dll may not support unicode.

    Now I still use INT32 but with multi-byte character set, and everything goes well. 

    Yes, that is what happens if you try to use a wide character string with a
    function that expects a narrow string. Typically a Unicode character consists
    of two bytes, the actual character and a binary zero. If you try to read the 
    Unicode string as a narrow string the function reading it will interpret the 
    first binary zero it encounters - which is part of the first Unicode 
    character - as the terminating nul character marking the end of the string.
    So you only get what appears to be a single character string, even though the
    other characters are actually there - they just never get accessed.

    It isn't necessary to change the overall project settings to Multi-Byte
    instead of Unicode. You just have to make sure that when you are accessing
    the DLL you never try to use wide characters (e.g. - Unicode). That means
    that you have to avoid using type-changing macros such as _T(), TEXT(), 
    LPCTSTR, etc. with the arguments in a call to the dll.

    Those macros will change the character/string type based on the project settings
    for the character set. For example:

    LPCTSTR

    "An LPCWSTR if UNICODE is defined, an LPCSTR otherwise. For more information, 
    see Windows Data Types for Strings."

    When passing arguments to a DLL built as C you should *explicitly* pass arguments
    as char or char* and never allow the compiler (preprocessor) to change the 
    argument type via macros based on the character set specified for the project.

    For example, you should use LPCSTR instead of LPCTSTR.

    - Wayne

    Monday, June 24, 2019 6:14 AM

  • When passing arguments to a DLL built as C you should *explicitly* pass arguments
    as char or char* and never allow the compiler (preprocessor) to change the 
    argument type via macros based on the character set specified for the project.

    I should clarify that statement. The fact that the DLL is built using C isn't
    the key factor. It's whether or not the exported functions have been written
    so that there are both wide character and narrow character versions. For example
    there are many such dual version functions in the Windows API/SDK which is
    written primarily in C.

    As an illustration, if you use the following code:

    MessageBox(NULL, _T("Aloha!"), _T("Greetings!"), MB_ICONASTERISK | MB_OK);
    

    If Unicode is defined for the project, then MessageBox maps to MessageBoxW
    which is a function that expects/requires wide character strings as arguments.

    If Unicode is NOT defined for the project, then MessageBox maps to MessageBoxA
    which is a function that expects/requires narrow character strings as arguments.

    When _T() is used with the arguments then the associated string is defined as
    a wide or narrow string depending on whether or not Unicode is defined. So
    the type of strings being passed will match the actual function being called.

    So if the function you are calling has only one definition which accepts a
    specific format for the arguments, then you should NOT be using _T() or other
    methods which alter the argument type based on the project property setting for
    the character set. If you do then the function will be passed the wrong type
    of argument. A function that expects a char string must be passed a char string
    and not a wchar_t string.

    - Wayne

    Monday, June 24, 2019 10:58 AM