[PATCH v2 0/2] MR10636: msvcp140: Implement basic_istream<char> move assignment.
This MR implements a missing operator that is invoked by Native Instruments Kontakt Player (and likely the full Kontakt too). --- _Disclaimers for reviewers:_ * _AI was used while tracking down the issue and coming up with the fix._ * _I am not affiliated with Native Instruments, I'm just a user._ -- v2: msvcp140: Implement basic_istream<char> move assignment. msvcp140/tests: Add basic_istream<char> move assignment tests. https://gitlab.winehq.org/wine/wine/-/merge_requests/10636
From: Theodoros Chatzigiannakis <tchatzigiannakis@gmail.com> --- dlls/msvcp140/tests/msvcp140.c | 132 +++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c index bad48e4934e..21b783bc6a0 100644 --- a/dlls/msvcp140/tests/msvcp140.c +++ b/dlls/msvcp140/tests/msvcp140.c @@ -93,6 +93,7 @@ struct thiscall_thunk static void * (WINAPI *call_thiscall_func1)( void *func, void *this ); static void * (WINAPI *call_thiscall_func2)( void *func, void *this, const void *a ); +static void * (WINAPI *call_thiscall_func4)( void *func, void *this, const void *a, const void *b, const void *c ); static void * (WINAPI *call_thiscall_func5)( void *func, void *this, const void *a, const void *b, const void *c, const void *d ); static void * (WINAPI *call_thiscall_func8)( void *func, void *this, const void *a, const void *b, @@ -109,12 +110,14 @@ static void init_thiscall_thunk(void) thunk->jmp_edx = 0xe2ff; /* jmp *%edx */ call_thiscall_func1 = (void *)thunk; call_thiscall_func2 = (void *)thunk; + call_thiscall_func4 = (void *)thunk; call_thiscall_func5 = (void *)thunk; call_thiscall_func8 = (void *)thunk; } #define call_func1(func,_this) call_thiscall_func1(func,_this) #define call_func2(func,_this,a) call_thiscall_func2(func,_this,(const void*)(a)) +#define call_func4(func,_this,a,b,c) call_thiscall_func4(func,_this,(const void*)(a),(const void*)(b),(const void*)(c)) #define call_func5(func,_this,a,b,c,d) call_thiscall_func5(func,_this,(const void*)(a),(const void*)(b), \ (const void*)(c), (const void*)(d)) #define call_func8(func,_this,a,b,c,d,e,f,g) call_thiscall_func8(func,_this,(const void*)(a),(const void*)(b), \ @@ -125,6 +128,7 @@ static void init_thiscall_thunk(void) #define init_thiscall_thunk() #define call_func1(func,_this) func(_this) #define call_func2(func,_this,a) func(_this,a) +#define call_func4(func,_this,a,b,c) func(_this,a,b,c) #define call_func5(func,_this,a,b,c,d) func(_this,a,b,c,d) #define call_func8(func,_this,a,b,c,d,e,f,g) func(_this,a,b,c,d,e,f,g) @@ -361,6 +365,75 @@ typedef struct static void (__cdecl *p___ExceptionPtrSwap)(exception_ptr *a, exception_ptr *b); +typedef SSIZE_T streamsize; + +typedef struct { void *mutex; } mutex; +typedef struct { struct _locale__Locimp *ptr; } locale; + +typedef struct { + const void *vtable; + MSVCP_size_t stdstr; + int state, except, fmtfl; + streamsize prec, wide; + void *arr, *calls; + locale *loc; +} ios_base; + +typedef struct { + ios_base base; + void *strbuf, *stream; + char fillch; +} basic_ios_char; + +typedef struct { const int *vbtable; } basic_ostream_char; + +typedef struct { + const int *vbtable; + streamsize count; +} basic_istream_char; + +typedef struct { + basic_istream_char base1; + basic_ostream_char base2; +} basic_iostream_char; + +typedef struct { + const void *vtable; + mutex lock; + char *rbuf, *wbuf, **prbuf, **pwbuf, *rpos, *wpos, **prpos, **pwpos; + int rsize, wsize, *prsize, *pwsize; + locale *loc; +} basic_streambuf_char; + +typedef struct { + basic_streambuf_char base; + char *seekhigh; + int state; + char allocator; +} basic_stringbuf_char; + +typedef struct { + basic_iostream_char base; + basic_stringbuf_char strbuf; + /* virtual inheritance */ + basic_ios_char basic_ios; +} basic_stringstream_char; + +#define BUF_SIZE_CHAR 16 +typedef struct { + void *allocator; + union { char buf[BUF_SIZE_CHAR]; char *ptr; } data; + size_t size, res; +} basic_string_char; + +static basic_string_char* (__thiscall *p_basic_string_char_ctor_cstr)(basic_string_char*, const char*); +static void (__thiscall *p_basic_string_char_dtor)(basic_string_char*); +static basic_stringstream_char* (__thiscall *p_basic_stringstream_char_ctor_str)(basic_stringstream_char*, + const basic_string_char*, int, MSVCP_bool); +static void (__thiscall *p_basic_stringstream_char_vbase_dtor)(basic_stringstream_char*); +static int (__thiscall *p_basic_istream_char_get)(basic_istream_char*); +static basic_istream_char* (__thiscall *p_basic_istream_char_assign)(basic_istream_char*, basic_istream_char*); + static HMODULE msvcp; #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -401,6 +474,18 @@ static BOOL init(void) SET(p__Fiopen_wchar, "?_Fiopen@std@@YAPEAU_iobuf@@PEB_WHH@Z"); SET(p__Fiopen, "?_Fiopen@std@@YAPEAU_iobuf@@PEBDHH@Z"); + SET(p_basic_string_char_ctor_cstr, + "??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@PEBD@Z"); + SET(p_basic_string_char_dtor, + "??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@XZ"); + SET(p_basic_stringstream_char_ctor_str, + "??0?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@1@H@Z"); + SET(p_basic_stringstream_char_vbase_dtor, + "??_D?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAAXXZ"); + SET(p_basic_istream_char_get, + "?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QEAAHXZ"); + SET(p_basic_istream_char_assign, + "??4?$basic_istream@DU?$char_traits@D@std@@@std@@IEAAAEAV01@$$QEAV01@@Z"); SET(p_codecvt_char16_ctor, "??_F?$codecvt@_SDU_Mbstatet@@@std@@QEAAXXZ"); SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@_K@Z"); SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@KW4_Codecvt_mode@1@_K@Z"); @@ -420,6 +505,18 @@ static BOOL init(void) SET(p__TaskEventLogger__LogTaskExecutionCompleted, "?_LogTaskExecutionCompleted@_TaskEventLogger@details@Concurrency@@QAAXXZ"); SET(p__TaskEventLogger__LogWorkItemCompleted, "?_LogWorkItemCompleted@_TaskEventLogger@details@Concurrency@@QAAXXZ"); SET(p__TaskEventLogger__LogWorkItemStarted, "?_LogWorkItemStarted@_TaskEventLogger@details@Concurrency@@QAAXXZ"); + SET(p_basic_string_char_ctor_cstr, + "??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAA@PBD@Z"); + SET(p_basic_string_char_dtor, + "??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAA@XZ"); + SET(p_basic_stringstream_char_ctor_str, + "??0?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAA@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@1@H@Z"); + SET(p_basic_stringstream_char_vbase_dtor, + "??_D?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAAXXZ"); + SET(p_basic_istream_char_get, + "?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAAHXZ"); + SET(p_basic_istream_char_assign, + "??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAAAAV01@$$QAV01@@Z"); SET(p_codecvt_char16_ctor, "??_F?$codecvt@_SDU_Mbstatet@@@std@@QAAXXZ"); SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@I@Z"); SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z"); @@ -438,6 +535,18 @@ static BOOL init(void) SET(p__TaskEventLogger__LogTaskExecutionCompleted, "?_LogTaskExecutionCompleted@_TaskEventLogger@details@Concurrency@@QAEXXZ"); SET(p__TaskEventLogger__LogWorkItemCompleted, "?_LogWorkItemCompleted@_TaskEventLogger@details@Concurrency@@QAEXXZ"); SET(p__TaskEventLogger__LogWorkItemStarted, "?_LogWorkItemStarted@_TaskEventLogger@details@Concurrency@@QAEXXZ"); + SET(p_basic_string_char_ctor_cstr, + "??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z"); + SET(p_basic_string_char_dtor, + "??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ"); + SET(p_basic_stringstream_char_ctor_str, + "??0?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@1@H@Z"); + SET(p_basic_stringstream_char_vbase_dtor, + "??_D?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEXXZ"); + SET(p_basic_istream_char_get, + "?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ"); + SET(p_basic_istream_char_assign, + "??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAEAAV01@$$QAV01@@Z"); SET(p_codecvt_char16_ctor, "??_F?$codecvt@_SDU_Mbstatet@@@std@@QAEXXZ"); SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@I@Z"); SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z"); @@ -2482,6 +2591,28 @@ static void test_exception_pointer(void) ok(ptr2.ref == (void *)2, "ptr2.ref = %p\n", ptr2.ref); } +static void test_istream_assign(void) +{ + basic_stringstream_char ss1, ss2; + basic_string_char str; + + call_func2(p_basic_string_char_ctor_cstr, &str, "hello"); + call_func4(p_basic_stringstream_char_ctor_str, &ss1, &str, OPENMODE_out|OPENMODE_in, TRUE); + call_func4(p_basic_stringstream_char_ctor_str, &ss2, &str, OPENMODE_out|OPENMODE_in, TRUE); + call_func1(p_basic_string_char_dtor, &str); + + call_func1(p_basic_istream_char_get, &ss1.base.base1); + ok(ss1.base.base1.count == 1, "expected count 1, got %d\n", (int)ss1.base.base1.count); + ok(ss2.base.base1.count == 0, "expected count 0, got %d\n", (int)ss2.base.base1.count); + + call_func2(p_basic_istream_char_assign, &ss1.base.base1, &ss2.base.base1); + ok(ss1.base.base1.count == 0, "expected count 0, got %d\n", (int)ss1.base.base1.count); + ok(ss2.base.base1.count == 1, "expected count 1, got %d\n", (int)ss2.base.base1.count); + + call_func1(p_basic_stringstream_char_vbase_dtor, &ss1); + call_func1(p_basic_stringstream_char_vbase_dtor, &ss2); +} + START_TEST(msvcp140) { if(!init()) return; @@ -2514,5 +2645,6 @@ START_TEST(msvcp140) test_codecvt_char16(); test_thread_library_reference(); test_exception_pointer(); + test_istream_assign(); FreeLibrary(msvcp); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10636
From: Theodoros Chatzigiannakis <tchatzigiannakis@gmail.com> --- dlls/msvcp110/msvcp110.spec | 6 +++--- dlls/msvcp120/msvcp120.spec | 6 +++--- dlls/msvcp140/msvcp140.spec | 6 +++--- dlls/msvcp90/ios.c | 10 ++++++++++ 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/dlls/msvcp110/msvcp110.spec b/dlls/msvcp110/msvcp110.spec index 2bc7e0bbeec..914ef4923bd 100644 --- a/dlls/msvcp110/msvcp110.spec +++ b/dlls/msvcp110/msvcp110.spec @@ -547,9 +547,9 @@ @ stub -arch=arm ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IAAAAV01@$$QAV01@@Z @ stub -arch=i386 ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IAEAAV01@$$QAV01@@Z @ stub -arch=win64 ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IEAAAEAV01@$$QEAV01@@Z -@ stub -arch=arm ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAAAAV01@$$QAV01@@Z -@ stub -arch=i386 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAEAAV01@$$QAV01@@Z -@ stub -arch=win64 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IEAAAEAV01@$$QEAV01@@Z +@ cdecl -arch=arm ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAAAAV01@$$QAV01@@Z(ptr ptr) basic_istream_char_assign +@ thiscall -arch=i386 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAEAAV01@$$QAV01@@Z(ptr ptr) basic_istream_char_assign +@ cdecl -arch=win64 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IEAAAEAV01@$$QEAV01@@Z(ptr ptr) basic_istream_char_assign @ stub -arch=arm ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IAAAAV01@$$QAV01@@Z @ stub -arch=i386 ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IAEAAV01@$$QAV01@@Z @ stub -arch=win64 ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IEAAAEAV01@$$QEAV01@@Z diff --git a/dlls/msvcp120/msvcp120.spec b/dlls/msvcp120/msvcp120.spec index 0a9f3ca47ee..0c878e241a9 100644 --- a/dlls/msvcp120/msvcp120.spec +++ b/dlls/msvcp120/msvcp120.spec @@ -547,9 +547,9 @@ @ stub -arch=arm ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IAAAAV01@$$QAV01@@Z @ stub -arch=i386 ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IAEAAV01@$$QAV01@@Z @ stub -arch=win64 ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IEAAAEAV01@$$QEAV01@@Z -@ stub -arch=arm ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAAAAV01@$$QAV01@@Z -@ stub -arch=i386 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAEAAV01@$$QAV01@@Z -@ stub -arch=win64 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IEAAAEAV01@$$QEAV01@@Z +@ cdecl -arch=arm ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAAAAV01@$$QAV01@@Z(ptr ptr) basic_istream_char_assign +@ thiscall -arch=i386 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAEAAV01@$$QAV01@@Z(ptr ptr) basic_istream_char_assign +@ cdecl -arch=win64 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IEAAAEAV01@$$QEAV01@@Z(ptr ptr) basic_istream_char_assign @ stub -arch=arm ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IAAAAV01@$$QAV01@@Z @ stub -arch=i386 ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IAEAAV01@$$QAV01@@Z @ stub -arch=win64 ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IEAAAEAV01@$$QEAV01@@Z diff --git a/dlls/msvcp140/msvcp140.spec b/dlls/msvcp140/msvcp140.spec index 4faf71ae3f7..70d0851f6fc 100644 --- a/dlls/msvcp140/msvcp140.spec +++ b/dlls/msvcp140/msvcp140.spec @@ -554,9 +554,9 @@ @ stub -arch=arm ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IAAAAV01@$$QAV01@@Z @ stub -arch=i386 ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IAEAAV01@$$QAV01@@Z @ stub -arch=win64 ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@IEAAAEAV01@$$QEAV01@@Z -@ stub -arch=arm ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAAAAV01@$$QAV01@@Z -@ stub -arch=i386 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAEAAV01@$$QAV01@@Z -@ stub -arch=win64 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IEAAAEAV01@$$QEAV01@@Z +@ cdecl -arch=arm ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAAAAV01@$$QAV01@@Z(ptr ptr) basic_istream_char_assign +@ thiscall -arch=i386 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAEAAV01@$$QAV01@@Z(ptr ptr) basic_istream_char_assign +@ cdecl -arch=win64 ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IEAAAEAV01@$$QEAV01@@Z(ptr ptr) basic_istream_char_assign @ stub -arch=arm ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IAAAAV01@$$QAV01@@Z @ stub -arch=i386 ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IAEAAV01@$$QAV01@@Z @ stub -arch=win64 ??4?$basic_istream@GU?$char_traits@G@std@@@std@@IEAAAEAV01@$$QEAV01@@Z diff --git a/dlls/msvcp90/ios.c b/dlls/msvcp90/ios.c index 71f8dc53159..dab99d639b5 100644 --- a/dlls/msvcp90/ios.c +++ b/dlls/msvcp90/ios.c @@ -9669,6 +9669,16 @@ void __thiscall basic_istream_char_swap(basic_istream_char *this, basic_istream_ this->count ^= r->count; } +/* ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IAEAAV01@$$QAV01@@Z */ +/* ??4?$basic_istream@DU?$char_traits@D@std@@@std@@IEAAAEAV01@$$QEAV01@@Z */ +DEFINE_THISCALL_WRAPPER(basic_istream_char_assign, 8) +basic_istream_char* __thiscall basic_istream_char_assign(basic_istream_char *this, basic_istream_char *r) +{ + TRACE("(%p %p)\n", this, r); + basic_istream_char_swap(this, r); + return this; +} + /* Caution: basic_istream uses virtual inheritance. */ static inline basic_ios_wchar* basic_istream_wchar_get_basic_ios(basic_istream_wchar *this) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10636
AI was used while tracking down the issue and coming up with the fix.
We're not accepting AI written code due to unclear copyright/licensing status. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10636#note_135880
On Sat Apr 11 19:16:06 2026 +0000, Piotr Caban wrote:
AI was used while tracking down the issue and coming up with the fix. We're not accepting AI written code due to unclear copyright/licensing status. Thanks for the clarification! I couldn't find the project's policy on this. Can you point me to it?
For reference, https://gitlab.winehq.org/wine/wine/-/merge_requests/10114 was labeled as such and was approved by other maintainers. It might need to be retracted if this is the case. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10636#note_135881
On Sat Apr 11 19:16:42 2026 +0000, Theodoros Chatzigiannakis wrote:
Thanks for the clarification! I couldn't find the project's policy on this. Can you point me to it? For reference, https://gitlab.winehq.org/wine/wine/-/merge_requests/10114 was labeled as such and was approved by other maintainers. It might need to be retracted if this is the case. My comment was based on @julliard reply in following email thread: https://list.winehq.org/hyperkitty/list/wine-devel@list.winehq.org/thread/RS...
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10636#note_135883
On Sat Apr 11 19:56:10 2026 +0000, Piotr Caban wrote:
My comment was based on @julliard reply in following email thread: https://list.winehq.org/hyperkitty/list/wine-devel@list.winehq.org/thread/RS... That makes sense, thank you! I will close this MR.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10636#note_135884
This merge request was closed by Theodoros Chatzigiannakis. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10636
participants (3)
-
Piotr Caban (@piotr) -
Theodoros Chatzigiannakis -
Theodoros Chatzigiannakis (@TChatzigiannakis)