I am using MsiGetProperty to get string parameter value from the installer.
And after that I am calling a managed dll and I pass the that value:
nvBufferSize = MAX_STRING;
MsiGetProperty (hMSI, "DBHMS", sDbHost, nvBufferSize);
when I pass the value of sDbHost is like this when I receive it from managed code:
srvdata-02NULNULNULNULNULNUL......
however in the interface I wrote just "srvdata-02".
With that same code it was fine with Installshield 2010, now I am upgrading it to installshield 2012.
Do you have any solution with that please?
There were some behavior changes to MsiGetProperty awhile back. Try setting nvBufferSize to MAX_SIZE instead of MAX_STRING. Also check the return code of MsiGetProperty to see if it equals ERROR_MORE_DATA or if it's returning some other code. Finally check the value of nvBufferSize to see how many bytes are needed.
BTW, if you are just trying to marshal a property over to managed code, you might want to conisder looking into Deployment Tools Framework (DTF) in Windows Installer XML (WiX). This is a very nice SDK that allows you to write managed code custom actions and package them up as if they are native Win32 libraries. InstallShield can then easily use this as an MSI DLL custom action.
DTF provides an interop library and a session object that can be used like:
Deployment Tools Foundation (DTF) Managed Custom Actions
Reasons DTF is Better
As ridiculous as it may seem, here's a working InstallScript solution for you:
nvBufferSize = MAX_STRING;
nResult = MsiGetProperty( ISMSI_HANDLE, szPropertyName, svValue, nvBufferSize );
if( nResult = ERROR_MORE_DATA ) then
MsiGetProperty( ISMSI_HANDLE, szPropertyName, svValue, nvBufferSize );
endif;
The first attempt returns the actual buffer size needed. If it is more then max string (1024?), the second call gets the whole thing.
Alternatively, I found I could assign nvBufferSize to larger value right off the bat e.g. 4096 and use that with a single call (assuming the data was no longer that limit). The double call, however, is more fool proof.
According to: https://msdn.microsoft.com/en-us/library/aa370134(v=vs.85).aspx the api function is actually designed to return the buffersize by passing an empty literal ("") instead of a string variable. InstallScript 2013 throws a compilation error at you if you try that though...
Related
I'm trying to write a DLL to access the C++-only method Windows.Devices.Bluetooth.BluetoothLEDevice.Close() from a C# app. I seem to be able to access the class, but Visual Studio will not build if I try to use that one method. It is shown in the member list that comes up as you type, and there is no Intellisense error, just the build error.
using namespace Windows::Devices::Bluetooth;
__declspec(dllexport) void CloseBleDeviceUnmanaged(BluetoothLEDevice^device)
{
if (device->ConnectionStatus == BluetoothConnectionStatus::Connected) //no complaints for a property
{
device->GetDeviceSelector(); //no complaints for a method either
device->Close(); //Error C2039 | 'Close': is not a member of 'Windows::Devices::Bluetooth::BluetoothLEDevice'
}
return;
}
How do I get this to at least build?
(edit: removed extraneous syntax problem as per Nico Zhu - MSFT's answer)
You don't need to do any of this to call Close from C# -- just put it in a using(...) block and it will dispose (close) the object for you automatically. If the lifetime of the objection doesn't lend itself well to a using block, you can simply call IDisposabe.Dispose() on it directly.
For the compiler error, this is documented in MSDN since you should never call Close from C++/CX:
Close methods aren't callable through Visual C++ component extensions (C++/CX) on Windows Runtime class instances where the class implemented IClosable. Instead, Visual C++ component extensions (C++/CX) code for runtime classes should call the destructor or set the last reference to null.
The parameter of CloseBleDeviceUnmanaged method is Value Type in your case. When you pass a parameter to the method. The parameter will generate a copy. And the original parameter has not been changed. Please pass the
reference type parameter like the follow.
void DevicesTool::CloseBleDeviceUnmanaged(Windows::Devices::Bluetooth::BluetoothLEDevice^device)
{
if (device->ConnectionStatus == BluetoothConnectionStatus::Connected)
{
device->GetDeviceSelector();
device->Close();
}
}
As for the Close method, I have reproduced the issue in my side(target platform version 16299), and I have report this to related team. Please pay attention to the thread update.
I want to change property value to selected text in a dialog.
this is my sample source.
#include "ifx.h"
STRING outPath;
export prototype MyFunction(HWND);
function OnFirstUIBefore()
NUMBER nResult, nSetupType, nvSize, nUser;
STRING szTitle, szMsg, szQuestion, svName, svCompany, szFile, szDir;
STRING szLicenseFile;
BOOL bCustom, bIgnore1, bIgnore2;
begin
Dlg_SdAskDestPath:
nResult = SdAskDestPath(szTitle, szMsg, INSTALLDIR, 0);
if (nResult = BACK) goto Dlg_SdAskDestPath;
Dlg_AskOutPath:
nResult = AskDestPath(szTitle, szmsg, szDir, 0);
if (nResult = BACK) goto Dlg_SdAskDestPath;
outPath = szDir;
MyFunction(ISMSI_HANDLE);
return 0;
end;
function MyFunction(hMSI)
STRING value;
begin
MsiSetProperty(hMSI, "OutPutPath", outPath);
end;
OutPutPath used in custom action after finish install.
But OutPutPath was not changed when read in custom action.
I think I must not use ISMSI_HANDLE. But i don't know what i have to use instead.
I tried to make custom action which load Install scripts's method MyFunction after finish install.
It worked well, But the global variable outPath was nul..
Please teach me how can i do this if you know.
Thank you.
At a minimum, you must use a public property, that is one with a name that does not contain any lowercase letters. You may also have to list it in SecureCustomProperties in order to allow users to modify it, if you support installation in restricted environments.
However I'm not certain the exact scenario described by your comment:
I tried to make custom action which load Install scripts's method MyFunction after finish install.
If this scenario is truly after the end of the Windows Installer portion of the installation (an InstallScript MSI runs code both before and after), properties as a whole may not survive to do what you need. To support reading the value at that time you will have to consider other approaches, such as writing the value in the registry, or into a file (e.g. in SUPPORTDIR).
I was just trying out the Windows app sample for the Recipe Property Handler which is available here and I modified it to be used on .doc files instead of .recipe files:
const WCHAR c_szRecipeFileExtension[] = L".doc";
But, this seemed to overwrite the previous Office handler's properties with itself, which begs the question, does the Windows shell support multiple shell property handlers, or can you only use one at a time for a given file type? If its possible, what am I missing from the code or logic in the sample?
I couldn't find a concrete answer on MSDN for this question.
No.
But there is a variant you can use (I dont like it but I dont see any additional variant). Save previous Property handler CLSID when you register your own. And when shell request the property that you cannot process - just create instance of previous handler and pass request to them.
CoCreateInstance(SavedCLSID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IPropertyStore, PS)
PS.QueryInterface(IInitializeWithStream, IWS)
IWS.Initialize(Stream, Mode)
PS.GetValue(AUnknownKey)
I've just created a custom dialog with a checkbox asking if the user wants to create a desktop shortcut. I used to always include a shortcut I'm not using the AskText() function as I plan on adding more pieces to this page later and want to simplify these few options to this one page.
I get an item on my desktop when I run, but it's not what I expect. The target seems to be pointing to a location on the desktop itself and not the actual executable. Also, this shortcut does not delete on uninstall (I'm assuming this needs to be handled separately anyway) and the shortcut needs admin rights to be manually deleted (which I don't want, for obvious reasons).
Below is my InstallScript code. It is in a custom action that was inserted after InstallFiles.
function MyFunction(hMSI)
STRING szProgramFolder, szItemName, szCommandLine, szWorkingDir;
STRING szShortCutKey, szProgram, szParam, szIconPath;
NUMBER nIcon, nResult;
begin
szProgramFolder = FOLDER_DESKTOP;
szItemName = "myProgram";
szProgram = INSTALLDIR + "myProgram.exe" ;
LongPathToQuote (szProgram, TRUE);
szCommandLine = szProgram;
szWorkingDir = INSTALLDIR;
szIconPath = "";
nIcon = 0;
szShortCutKey = "";
nResult = AddFolderIcon (szProgramFolder, szItemName, szCommandLine,szWorkingDir,
szIconPath, nIcon, szShortCutKey, REPLACE);
end;
I'm not quite sure where I'm going wrong here, although my knowledge of InstallShield (let alone InstallScript) is very limited.
As it turned out, this is a deferred custom action, hence the INSTALLDIR variable is not initialized (nor any other Windows Installer built-in variables). Change it to an Immediate-type custom action (and relocate it to an appropriate location in the execution sequence) and it should work.
To fix the shortcut's parameters, start by ensuring they are correct. Debug your function to verify you are actually passing what you want to. As commented, INSTALLDIR may not be available directly to an InstallScript custom action. A simple way to "debug" would be to add calls like MessageBox(szCommandLine, 0); to key points in your code. If you find you are passing something like C:\Program Files\Company\ProductmyProgram.exe, consider using the ^ operator to concatenate your paths: szProgram = INSTALLDIR ^ "myProgram.exe";.
To uninstall the shortcut, you have to understand that custom actions in MSI projects are not automatically reversed. So use a different approach. Either explicitly code up its removal during uninstall in another action, switch to pure InstallScript where logging will reverse your actions, or go with a proper MSI-based approach. For the last of those, define the shortcut in its own component, and give the component a condition that correlates to a property you set in your UI (or via AskText for now), or skip the condition and just use feature selection by putting the component in a child feature. Then Windows Installer will track and remove the shortcut for you.
I am using VS2008 to port code from VC6. When I ran the new build app, I get this error "R6031 Attemp to initialize the CRT more than once. This indicates a bug in your application".
There are a total of 21 dlls that are involve in the build this one app. Some DLL has .c files in them and explicitly calls _CRT_INIT() in DllMain. code below:
BOOL APIENTRY DllMain (HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch( dwReason)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
if(!_CRT_INIT( hModule, dwReason, lpReserved))
return FALSE;
break;
}
return TRUE;
}
I am not sure how to fix this problem. Do I need to comment out the call to _CRT_INIT()?
Thanks in advance.
Yes, you should not need to call _CRT_INIT() explicitly. It's probably being called by one or another DLLMain.
See MSDN for details.
Edit
I think you have misread MSDN:
When building a DLL which uses any of
the C Run-time libraries, in order to
ensure that the CRT is properly
initialized, either
the initialization function must be named DllMain() and the entry point
must be specified with the linker
option -entry:_DllMainCRTStartup#12 -
or -
You have named the init function DllMain(), so _CRT_INIT() is being called automatically. I think.
Why not simply comment out that line and see what happens?
This error code is specific to mixed-mode assemblies. Have you enabled the CLR during the port by mistake? You should not see this during a simple port from VC6 to a later Visual C++ revision.
This diagnostic indicates that MSIL
instructions were executing during
loader lock. For more information, see
Initialization of Mixed Assemblies.
You can check the project setting by right-clicking the project in Solution Explorer, then under Properties look at Configuration Properties -> General -> Common Language Runtime Support